From 6e1bd28aac052e6b8f77964d2c182802fc109d7e Mon Sep 17 00:00:00 2001 From: slurdge Date: Wed, 17 Jul 2019 14:50:59 +0200 Subject: [PATCH 01/40] Fix build for linux And move to xenial --- .travis.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index be68e761a..ee0ed1196 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,16 @@ # Travis-CI Build for RfidResearchGroup/Proxmark3 language: c -#default linux build env is: Ubuntu 14.04 trusty +#default linux build env is: xenial compiler: gcc # Test on Linux and MacOS matrix: include: -# - os: osx -# osx_image: xcode7.3 # OS X 10.11 -# - os: osx -# osx_image: xcode8.3 # OS X 10.12 -# - os: osx -# osx_image: xcode9 # OS X 10.13 - os: osx osx_image: xcode9.1 # OS X 10.13.1 - os: linux - dist: trusty + dist: xenial sudo: required before_install: @@ -25,7 +19,7 @@ before_install: ## Note: all dependencies on MacOS should be resolved by the brew install command if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq; - sudo apt-get install -y gcc-arm-none-eabi; + sudo apt-get install -y gcc-arm-none-eabi libnewlib-arm-none-eabi libnewlib-dev binutils-arm-none-eabi; elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew tap RfidResearchGroup/proxmark3; From 67ad6298cd4f7d4e34a40cb43c2302c710df5e1b Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 17 Jul 2019 15:54:22 +0300 Subject: [PATCH 02/40] added 14a send/receive arm timeout --- armsrc/iso14443a.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index b5e612ec9..6851ef85a 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1578,6 +1578,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing volatile uint8_t b; uint16_t c = 0; + uint32_t sendtimer = GetTickCount(); while (c < len) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = cmd[c++]; @@ -1587,6 +1588,8 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); (void)b; } + if (GetTickCount() - sendtimer > 100) + break; } NextTransferTime = MAX(NextTransferTime, LastTimeProxToAirStart + REQUEST_GUARD_TIME); @@ -1937,6 +1940,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive (void)b; uint32_t timeout = iso14a_get_timeout(); + uint32_t receive_timer = GetTickCount(); for (;;) { WDT_HIT(); @@ -1949,7 +1953,12 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive return false; } } + + // timeout already in ms + 100ms guard time + if (GetTickCount() - receive_timer > timeout + 100) + break; } + return false; } void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing) { From 81fa3f20df702815602e922e52380f0994965ac2 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 17 Jul 2019 16:22:14 +0300 Subject: [PATCH 03/40] speed optimization --- armsrc/iso14443a.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 6851ef85a..8543cdfa5 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1579,17 +1579,23 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing volatile uint8_t b; uint16_t c = 0; uint32_t sendtimer = GetTickCount(); + uint32_t cntr = 0; while (c < len) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = cmd[c++]; + } else { + if (cntr++ > 1000) { + cntr = 0; + if (GetTickCount() - sendtimer > 100) + break; + } } + //iceman test if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); (void)b; } - if (GetTickCount() - sendtimer > 100) - break; } NextTransferTime = MAX(NextTransferTime, LastTimeProxToAirStart + REQUEST_GUARD_TIME); From 87e183e664140662c3bfbba2de6270195a37de79 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 17 Jul 2019 16:26:10 +0300 Subject: [PATCH 04/40] small fix --- armsrc/iso14443a.c | 1 + 1 file changed, 1 insertion(+) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 8543cdfa5..032766cb6 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1583,6 +1583,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing while (c < len) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = cmd[c++]; + cntr = 0; } else { if (cntr++ > 1000) { cntr = 0; From 7efaeec0549a785f3c7037970b7a2fbd1d119c91 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 17 Jul 2019 19:01:51 +0200 Subject: [PATCH 05/40] Linux install: limit dependencies to the really needed ones --- .../Linux-Installation-Instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/md/Installation_Instructions/Linux-Installation-Instructions.md b/doc/md/Installation_Instructions/Linux-Installation-Instructions.md index bb5afb268..fe23db001 100644 --- a/doc/md/Installation_Instructions/Linux-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Linux-Installation-Instructions.md @@ -25,7 +25,7 @@ Install the requirements ```sh sudo apt-get install p7zip git ca-certificates build-essential libreadline5 libreadline-dev \ -libusb-0.1-4 libusb-dev perl pkg-config wget libncurses5-dev gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib libqt4-dev +libusb-0.1-4 libusb-dev perl pkg-config wget libncurses5-dev gcc-arm-none-eabi libnewlib-dev libqt4-dev ``` If you don't need the graphical components of the Proxmark3 client, you can skip the installation of `libqt4-dev`. From 7d836d9f1b76c6054b7fbc4b195bb613048b0d17 Mon Sep 17 00:00:00 2001 From: slurdge Date: Wed, 17 Jul 2019 19:17:20 +0200 Subject: [PATCH 06/40] Less apt libs for compilation --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ee0ed1196..b81af3d97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ before_install: ## Note: all dependencies on MacOS should be resolved by the brew install command if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq; - sudo apt-get install -y gcc-arm-none-eabi libnewlib-arm-none-eabi libnewlib-dev binutils-arm-none-eabi; + sudo apt-get install -y gcc-arm-none-eabi libnewlib-dev; elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew tap RfidResearchGroup/proxmark3; From 259c730bb06f0c45ae8050ea281507c5b390094e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 17 Jul 2019 20:15:47 +0200 Subject: [PATCH 07/40] exit(EXIT_FAILURE) if a port was specified to the client and no pm3 is usable --- client/proxmark3.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/proxmark3.c b/client/proxmark3.c index 480f39fa3..1d3a0e8b5 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -503,6 +503,9 @@ int main(int argc, char *argv[]) { CloseProxmark(); } + if ((port != NULL) && (!session.pm3_present)) + exit(EXIT_FAILURE); + if (!session.pm3_present) PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") "mode. Check \"%s -h\" if it's not what you want.\n", exec_name); @@ -531,5 +534,5 @@ int main(int argc, char *argv[]) { CloseProxmark(); } - exit(0); + exit(EXIT_SUCCESS); } From dfdef0bbcc6d5d3888d4851cbd14c2d20ac3cb32 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 17 Jul 2019 20:39:32 +0200 Subject: [PATCH 08/40] Clarify some PLATFORM_EXTRAS usages --- Makefile.platform.sample | 3 +++ doc/uart_notes.md | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile.platform.sample b/Makefile.platform.sample index b0d6cf5fe..9d0e2d965 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -1,4 +1,7 @@ # If you want to use it, copy this file as Makefile.platform and adjust it to your needs +# Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file. + PLATFORM=PM3RDV4 +# If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #STANDALONE=LF_SAMYRUN diff --git a/doc/uart_notes.md b/doc/uart_notes.md index d074bddc8..88b10a109 100644 --- a/doc/uart_notes.md +++ b/doc/uart_notes.md @@ -27,10 +27,10 @@ Proxmark3 RDV4 has a FPC connector outputting on 2 pins a USART from the ARM: USART support is in `common/usart.c`. There are mainly two ways to use this USART: -* connect the host client to the Proxmark3 via this USART instead of USB-CDC, this is the `FPC_USART_HOST`. The most used way is through the BT add-on (blue shark) that we will cover later. Instead of BT add-on, we can also use e.g. a FTDI cable (mostly for internal development, it's much slower than USB-CDC anyway) or in the future other ways to connect the host such as a USART-to-Wi-Fi bridge. +* connect the host client to the Proxmark3 via this USART instead of USB-CDC, this is the `FPC_USART_HOST` option you can add to `PLATFORM_EXTRAS` in `Makefile.platform`. The most used way is through the BT add-on (blue shark) that we will cover later. Instead of BT add-on, we can also use e.g. a FTDI cable (mostly for internal development, it's much slower than USB-CDC anyway) or in the future other ways to connect the host such as a USART-to-Wi-Fi bridge. * connect "slave" devices to the Proxmark3 to add functionnalities. In such case, the host client will use USB-CDC and the USART will be use to, e.g. connect the Proxmark3 to various daughterboards. These is no such example of daughterboard as of today, except when we're talking to the BT add-on in its AT configuration mode. -This USART can be reached from the host client (if connected via USB-CDC) through the following commands, available in `FPC_USART_DEV` build: +This USART can be reached from the host client (if connected via USB-CDC) through the following commands, available when you add `FPC_USART_DEV` to `PLATFORM_EXTRAS` in `Makefile.platform`: * `usart config`, to configure the baudrate and the parity of the Proxmark3 USART * `usart txrx/tx/rx/txhex/rxhex` to transmit and receive bytes @@ -45,7 +45,7 @@ Internally, the desired baudrate is converted to UART settings: a BRGR and a FP. When the BT add-on is turned on but no actively connected to a host, it's in a configuration mode where it accepts "AT" commands and its blue LED is blinking at about 1Hz. -Some specific commands are available in `BTADDON` build, only to configure specific features of the BT add-on: +Some specific commands are available when you add `BTADDON` to `PLATFORM_EXTRAS` in `Makefile.platform` (it will automatically enable `FPC_USART_HOST` as well), to configure specific features of the BT add-on: * `usart btpin`, to change the BT add-on PIN * `usart btfactory`, to guess the current BT add-on UART settings and to reset its configuration. From 9dc1b1664d6c5e8bb908295141e41e39038f01f9 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 17 Jul 2019 21:11:34 +0200 Subject: [PATCH 09/40] Add external flash layout notes --- README.md | 1 + doc/ext_flash_notes.md | 95 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 doc/ext_flash_notes.md diff --git a/README.md b/README.md index a1e7ad76c..e3be68472 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Note that it also supports other Proxmark3 platforms as well! |[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) |[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)| |[Notes on UART](/doc/uart_notes.md)||| |[Notes on Frame format](/doc/new_frame_format.md)||| +|[Notes on external flash](/doc/ext_flash_notes.md)||| |[Notes on Termux / Android](/doc/termux_notes.md)||| |[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode) || |[Donations](#Donations)||| diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md new file mode 100644 index 000000000..46059d9ca --- /dev/null +++ b/doc/ext_flash_notes.md @@ -0,0 +1,95 @@ +# External flash + +External 256kbytes flash is a unique feature of the RDV4 edition. + +## Addresses + +Flash memory is + +* 256kb (0x40000= 262144) +* divided into 4 pages of 64kb (0x10000 = 65536) +* 4 pages divided into 16 sectors of 4kb (0x1000 = 4096), so last sector is at 0x3F000 + +Therefore a flash address can be interpreted as such: +``` +0xPSxxx e.g. 0x3FF7F + ^ page ^ page 3 + ^ sector ^ sector 0xF + ^^^ offset ^^^ offset 0xF7F +``` + +## Layout + +Page 0: +* available for user data +* to dump it: `mem dump f page0_dump o 0 l 65536` +* to erase it: `mem wipe p 0` + +Page 1: +* available for user data +* to dump it: `mem dump f page1_dump o 65536 l 65536` +* to erase it: `mem wipe p 1` + +Page 2: +* available for user data +* to dump it: `mem dump f page2_dump o 131072 l 65536` +* to erase it: `mem wipe p 2` + +Page 3: +* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details +* to dump it: `mem dump f page3_dump o 196608 l 65536` +* to erase it: + * **Beware** it will erase your flash signature (see below) so better to back it up first as you won't be able to regenerate it by yourself! + * It's possible to erase completely page 3 by erase the entire flash memory with the voluntarily undocumented command `mem wipe i`. + * Updating keys dictionaries doesn't require to erase page 3. + +## Page3 Layout + +Page3 is used as follows by the Proxmark3 RDV4 firmware: + +* **MF_KEYS** + * offset: page 3 sector 9 (0x9) @ 3*0x10000+9*0x1000=0x39000 + * length: 2 sectors + +* **ICLASS_KEYS** + * offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000 + * length: 1 sector + +* **T55XX_KEYS** + * offset: page 3 sector 12 (0xC) @ 3*0x10000+12*0x1000=0x3C000 + * length: 1 sector + +* **T55XX_CONFIG** + * offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000 + * length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure) + +* **RSA SIGNATURE**, see below for details + * offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F + * length: 128 bytes + * offset should have been 0x3FF80 but historically it's one byte off and therefore the last byte of the flash is unused + +## RSA signature + +To ensure your Proxmark3 RDV4 is not a counterfeit product, its external flash contains a RSA signature of the flash unique ID. +You can verify it with: `mem info` + +``` +[usb] pm3 --> mem info + +[=] --- Flash memory Information --------- + +[=] ------------------------------------------------------------- +[=] ID | xx xx xx xx xx xx xx xx +[=] SHA1 | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +[=] RSA SIGNATURE | +[00] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +[01] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +[02] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +[03] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +[=] KEY length | 128 +[+] RSA key validation ok +[+] RSA Verification ok +``` + +For a backup of the signature: `mem dump p f flash_signature_dump o 262015 l 128` + From f748c217b6013e2af335d66a03aea2d9fa2981a3 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 18 Jul 2019 15:27:12 +0200 Subject: [PATCH 10/40] Update README.md appveyor RRG/Iceman link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e3be68472..46dc5f785 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This repo is based on iceman fork for Proxmark3. It is dedicated to bringing the most out of the new features for Proxmark3 RDV4.0 new hardware and design. Note that it also supports other Proxmark3 platforms as well! -[![Build status](https://ci.appveyor.com/api/projects/status/ct5blik2wa96bv0x/branch/master?svg=true)](https://ci.appveyor.com/project/iceman1001/proxmark3-ji4wj/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/uvk6cexs6xxwonn4/branch/master?svg=true)](https://ci.appveyor.com/project/iceman1001/proxmark3-isfoh/branch/master) [![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) --- From 29f8bfb80c08bc506ca85cf554912b2a1592f3a3 Mon Sep 17 00:00:00 2001 From: Julien Piat Date: Thu, 18 Jul 2019 15:54:21 +0200 Subject: [PATCH 11/40] Update default_keys.dic with new keys from Scan Badge APK --- client/default_keys.dic | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/default_keys.dic b/client/default_keys.dic index c8d96bd44..4df09cb1c 100644 --- a/client/default_keys.dic +++ b/client/default_keys.dic @@ -944,5 +944,9 @@ A23456789123 A00003000084 675A32413770 395244733978 +A0004A000036 +2C9F3D45BA13 +4243414F5250 +DFE73BE48AC6 # B069D0D03D17 From 5a5b3db92ee618b435c202b1946723b98f1b5b85 Mon Sep 17 00:00:00 2001 From: Julien Piat Date: Thu, 18 Jul 2019 16:11:03 +0200 Subject: [PATCH 12/40] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c78d813db..89a4d7ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Change: new keys for Vigik badges in default_keys.dict (@luminouw) - Add 'hw standalone' to jump to standalone mode from command line or script (@doegox) - Add to 'hf 14a apdu' print apdu and compose apdu (@merlokk) - Change: buggy 'mem read' removed, 'mem save' renamed 'mem dump', can now display too (@doegox) From 1e0433c1d79002cce7e67e54d247673ce3a29403 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 17 Jul 2019 19:02:01 +0300 Subject: [PATCH 13/40] now code can't use uninitialized fpga link and encoders/decoders --- armsrc/iso14443a.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index b5e612ec9..db20481c8 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -13,6 +13,7 @@ #define MAX_ISO14A_TIMEOUT 524288 static uint32_t iso14a_timeout; +static bool iso14443a_active = false; uint8_t colpos = 0; int rsamples = 0; @@ -1551,6 +1552,9 @@ void PrepareDelayedTransfer(uint16_t delay) { //------------------------------------------------------------------------------------- static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing) { + if (!iso14443a_active) + return; + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); if (timing) { @@ -1922,6 +1926,9 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start //----------------------------------------------------------------------------- static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receivedResponsePar, uint16_t offset) { uint32_t c = 0; + + if (!iso14443a_active) + return false; // Set FPGA mode to "reader listen mode", no modulation (listen // only, since we are receiving, not transmitting). @@ -2354,6 +2361,14 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { UartReset(); NextTransferTime = 2 * DELAY_ARM2AIR_AS_READER; iso14a_set_timeout(1060); // 106 * 10ms default + + iso14443a_active = true; +} + +void iso14443a_off() { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + iso14443a_active = false; } /* Peter Fillmore 2015 @@ -2558,7 +2573,7 @@ void ReaderIso14443a(PacketCommandNG *c) { return; OUT: - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + iso14443a_off(); set_tracing(false); LEDsoff(); } @@ -2854,7 +2869,7 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { reply_mix(CMD_ACK, isOK, 0, 0, buf, sizeof(buf)); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + iso14443a_off(); LEDsoff(); set_tracing(false); } @@ -3094,7 +3109,7 @@ void DetectNACKbug() { //reply_mix(CMD_ACK, isOK, num_nacks, i, 0, 0); BigBuf_free(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + iso14443a_off(); LEDsoff(); set_tracing(false); } From e87abc9681ecae0cac02adc52984bbd92c1511e5 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 18 Jul 2019 17:32:56 +0300 Subject: [PATCH 14/40] small fix apdu leds --- armsrc/iso14443a.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index db20481c8..11f2ca66b 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -13,6 +13,7 @@ #define MAX_ISO14A_TIMEOUT 524288 static uint32_t iso14a_timeout; +// if iso14443a not active - transmit/receive dont try to execute static bool iso14443a_active = false; uint8_t colpos = 0; @@ -2367,7 +2368,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { void iso14443a_off() { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); + LEDsoff(); iso14443a_active = false; } @@ -2575,7 +2576,6 @@ void ReaderIso14443a(PacketCommandNG *c) { OUT: iso14443a_off(); set_tracing(false); - LEDsoff(); } // Determine the distance between two nonces. @@ -2870,7 +2870,6 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { reply_mix(CMD_ACK, isOK, 0, 0, buf, sizeof(buf)); iso14443a_off(); - LEDsoff(); set_tracing(false); } @@ -3110,6 +3109,5 @@ void DetectNACKbug() { //reply_mix(CMD_ACK, isOK, num_nacks, i, 0, 0); BigBuf_free(); iso14443a_off(); - LEDsoff(); set_tracing(false); } From 9cd80406e210830d04e4fb3f7f24e7c32f2c01e9 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 18 Jul 2019 16:39:48 +0200 Subject: [PATCH 15/40] Add Travis badge --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 46dc5f785..331edb337 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,9 @@ This repo is based on iceman fork for Proxmark3. It is dedicated to bringing the most out of the new features for Proxmark3 RDV4.0 new hardware and design. Note that it also supports other Proxmark3 platforms as well! -[![Build status](https://ci.appveyor.com/api/projects/status/uvk6cexs6xxwonn4/branch/master?svg=true)](https://ci.appveyor.com/project/iceman1001/proxmark3-isfoh/branch/master) -[![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) +| Releases | Linux & OSX CI | Windows CI | +| ------------------- |:-------------------:| -------------------:| +| [![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://travis-ci.org/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.org/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/uvk6cexs6xxwonn4/branch/master?svg=true)](https://ci.appveyor.com/project/iceman1001/proxmark3-isfoh/branch/master) | --- From fdada47325d3c0622c179d7c095fa2a0d1ae0106 Mon Sep 17 00:00:00 2001 From: RFID Research Group Date: Thu, 18 Jul 2019 20:45:08 +0200 Subject: [PATCH 16/40] Update README.md swapped to RRG account for Appveyor --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 331edb337..50813868e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Note that it also supports other Proxmark3 platforms as well! | Releases | Linux & OSX CI | Windows CI | | ------------------- |:-------------------:| -------------------:| -| [![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://travis-ci.org/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.org/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/uvk6cexs6xxwonn4/branch/master?svg=true)](https://ci.appveyor.com/project/iceman1001/proxmark3-isfoh/branch/master) | +| [![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://travis-ci.org/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.org/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | --- From 01e6db5c2e05f4dfde50c5b8823f2b5fa8d447e1 Mon Sep 17 00:00:00 2001 From: slurdge Date: Thu, 18 Jul 2019 22:26:01 +0200 Subject: [PATCH 17/40] Add a simple tool to analyze elf files --- CHANGELOG.md | 1 + tools/analyzesize.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100755 tools/analyzesize.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a4d7ec8..bedc1dc04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Add a simple python tool to check the elf sizes (@slurdge) - Change: new keys for Vigik badges in default_keys.dict (@luminouw) - Add 'hw standalone' to jump to standalone mode from command line or script (@doegox) - Add to 'hf 14a apdu' print apdu and compose apdu (@merlokk) diff --git a/tools/analyzesize.py b/tools/analyzesize.py new file mode 100755 index 000000000..ef14e2879 --- /dev/null +++ b/tools/analyzesize.py @@ -0,0 +1,31 @@ +#! /usr/bin/python3 + +import json +import subprocess +import sys + +def print_increase(x, y, name): + if x > y: + print("{} increase by: {} (0x{:08X}) bytes ({}%)".format(name, x-y, x-y, (x-y)*100/y)) + else: + print("{} decrease by: {} (0x{:08X}) bytes ({}%)".format(name, y-x, y-x, (y-x)*100/x)) +dbname = "tools/data.json" +db = json.load(open(dbname,"r")) + +if len(sys.argv) < 3: + print("Usage: analazysize.py ") + exit(-1) +action, name = sys.argv[1:3] +currentdata = subprocess.run(["arm-none-eabi-size","armsrc/obj/fullimage.stage1.elf"], stdout=subprocess.PIPE).stdout +currentdata = currentdata.split(b"\n")[1].strip() +text,data,bss = [int(x) for x in currentdata.split(b"\t")[:3]] +if action.lower() == "add": + db[name] = [text, data, bss] + json.dump(db, open(dbname, "w")) +elif action.lower() == "diff": + text_ref, data_ref, bss_ref = db[name] + flash_ref = text_ref+data_ref + flash = text+data + print_increase(flash, flash_ref, "Flash") + print_increase(bss, bss_ref, "RAM") + \ No newline at end of file From 32382ace3be9efd3e99e6f8e5b0903360d9f29ca Mon Sep 17 00:00:00 2001 From: slurdge Date: Thu, 18 Jul 2019 22:28:07 +0200 Subject: [PATCH 18/40] Ignore local openocd configuration --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e37ce9534..f5ee135e6 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ client/traces/* armsrc/TEMP EMV/* tools/mf_nonce_brute/* tools/andrew/* +tools/jtag_openocd/openocd_configuration ppls patches/* *- Copy.* @@ -73,3 +74,4 @@ client/lualibs/mf_default_keys.lua client/lualibs/pm3_cmd.lua # recompiled fpga_version_info.c + From 41731ea0849c4d5bca0e46236292e06570b01f0d Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 18 Jul 2019 23:42:58 +0200 Subject: [PATCH 19/40] analyzesize: create db on first use --- tools/analyzesize.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/analyzesize.py b/tools/analyzesize.py index ef14e2879..70c35de26 100755 --- a/tools/analyzesize.py +++ b/tools/analyzesize.py @@ -10,7 +10,10 @@ def print_increase(x, y, name): else: print("{} decrease by: {} (0x{:08X}) bytes ({}%)".format(name, y-x, y-x, (y-x)*100/x)) dbname = "tools/data.json" -db = json.load(open(dbname,"r")) +try: + db = json.load(open(dbname,"r")) +except FileNotFoundError: + db = dict() if len(sys.argv) < 3: print("Usage: analazysize.py ") From 4e9e7d6da004140c7c4a2ed2c3f560ac9a4d6763 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 18 Jul 2019 23:50:45 +0200 Subject: [PATCH 20/40] temporary fix to get Appveyor happy. Will need more tuning later. --- client/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index a62924787..e75224cd2 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -3591,7 +3591,7 @@ static command_t CommandTable[] = { {"list", CmdHF14AMfList, AlwaysAvailable, "List Mifare history"}, {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack. read parity error messages."}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack. Test nested authentication"}, - {"hardnested", CmdHF14AMfNestedHard, IfPm3Iso14443a, "Nested attack for hardened Mifare cards"}, + {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened Mifare cards"}, {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, {"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for Mifare NACK bug"}, {"chk", CmdHF14AMfChk, IfPm3Iso14443a, "Check keys"}, From 7bf3255a6cd6f901a8cc01306c5dd4794ed974cc Mon Sep 17 00:00:00 2001 From: slurdge Date: Thu, 18 Jul 2019 21:18:52 +0200 Subject: [PATCH 21/40] Flasher support for 512K flash A better way would be to cut the connecting function and flashing function and to move the whole mem computation to flash.c Working flasher --- CHANGELOG.md | 1 + bootrom/bootrom.c | 29 ++++++++++++++++++----------- client/flash.c | 35 ++++++++++++++++++----------------- client/flash.h | 2 +- client/flasher.c | 20 ++++++++++++++------ 5 files changed, 52 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bedc1dc04..cfa26a2b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Add support for flashing 512K units (@slurdge) - Add a simple python tool to check the elf sizes (@slurdge) - Change: new keys for Vigik badges in default_keys.dict (@luminouw) - Add 'hw standalone' to jump to standalone mode from command line or script (@doegox) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 4e92bc95b..bda7de2c6 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -153,28 +153,35 @@ void UsbPacketReceived(uint8_t *packet, int len) { case CMD_FINISH_WRITE: { uint32_t *flash_mem = (uint32_t *)(&_flash_start); for (int j = 0; j < 2; j++) { - for (i = 0 + (64 * j); i < 64 + (64 * j); i++) { - flash_mem[i] = c->d.asDwords[i]; - } - uint32_t flash_address = arg0 + (0x100 * j); - + AT91PS_EFC efc_bank = AT91C_BASE_EFC0; + int offset = 0; + uint32_t page_n = (flash_address - ((uint32_t)flash_mem)) / AT91C_IFLASH_PAGE_SIZE; + if (page_n >= AT91C_IFLASH_NB_OF_PAGES / 2) { + page_n -= AT91C_IFLASH_NB_OF_PAGES / 2; + efc_bank = AT91C_BASE_EFC1; + // We need to offset the writes or it will not fill the correct bank write buffer. + offset = (AT91C_IFLASH_NB_OF_PAGES / 2) * AT91C_IFLASH_PAGE_SIZE / sizeof(uint32_t); + } + for (i = 0 + (64 * j); i < 64 + (64 * j); i++) { + flash_mem[offset+i] = c->d.asDwords[i]; + } + /* Check that the address that we are supposed to write to is within our allowed region */ if (((flash_address + AT91C_IFLASH_PAGE_SIZE - 1) >= end_addr) || (flash_address < start_addr)) { /* Disallow write */ dont_ack = 1; reply_old(CMD_NACK, 0, 0, 0, 0, 0); } else { - uint32_t page_n = (flash_address - ((uint32_t)flash_mem)) / AT91C_IFLASH_PAGE_SIZE; - /* Translate address to flash page and do flash, update here for the 512k part */ - AT91C_BASE_EFC0->EFC_FCR = MC_FLASH_COMMAND_KEY | - MC_FLASH_COMMAND_PAGEN(page_n) | - AT91C_MC_FCMD_START_PROG; + + efc_bank->EFC_FCR = MC_FLASH_COMMAND_KEY | + MC_FLASH_COMMAND_PAGEN(page_n) | + AT91C_MC_FCMD_START_PROG; } // Wait until flashing of page finishes uint32_t sr; - while (!((sr = AT91C_BASE_EFC0->EFC_FSR) & AT91C_MC_FRDY)); + while (!((sr = efc_bank->EFC_FSR) & AT91C_MC_FRDY)); if (sr & (AT91C_MC_LOCKE | AT91C_MC_PROGE)) { dont_ack = 1; reply_old(CMD_NACK, sr, 0, 0, 0, 0); diff --git a/client/flash.c b/client/flash.c index e41b47fb0..a29d8d7b0 100644 --- a/client/flash.c +++ b/client/flash.c @@ -12,13 +12,6 @@ #define FLASH_START 0x100000 -#ifdef HAS_512_FLASH -# define FLASH_SIZE (512*1024) -#else -# define FLASH_SIZE (256*1024) -#endif - -#define FLASH_END (FLASH_START + FLASH_SIZE) #define BOOTLOADER_SIZE 0x2000 #define BOOTLOADER_END (FLASH_START + BOOTLOADER_SIZE) @@ -33,7 +26,7 @@ static const uint8_t elf_ident[] = { // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed -static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, uint16_t num_phdrs) { +static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, uint16_t num_phdrs, uint32_t flash_end) { Elf32_Phdr *phdr = phdrs; flash_seg_t *seg; uint32_t last_end = 0; @@ -77,11 +70,11 @@ static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, PrintAndLogEx(ERR, "Error: PHDRs not sorted or overlap"); return -1; } - if (paddr < FLASH_START || (paddr + filesz) > FLASH_END) { + if (paddr < FLASH_START || (paddr + filesz) > flash_end) { PrintAndLogEx(ERR, "Error: PHDR is not contained in Flash"); return -1; } - if (vaddr >= FLASH_START && vaddr < FLASH_END && (flags & PF_W)) { + if (vaddr >= FLASH_START && vaddr < flash_end && (flags & PF_W)) { PrintAndLogEx(ERR, "Error: Flash VMA segment is writable"); return -1; } @@ -153,7 +146,7 @@ static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, } // Sanity check segments and check for bootloader writes -static int check_segs(flash_file_t *ctx, int can_write_bl) { +static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_end) { for (int i = 0; i < ctx->num_segs; i++) { flash_seg_t *seg = &ctx->segments[i]; @@ -165,7 +158,7 @@ static int check_segs(flash_file_t *ctx, int can_write_bl) { PrintAndLogEx(ERR, "Error: Segment is outside of flash bounds"); return -1; } - if (seg->start + seg->length > FLASH_END) { + if (seg->start + seg->length > flash_end) { PrintAndLogEx(ERR, "Error: Segment is outside of flash bounds"); return -1; } @@ -182,11 +175,12 @@ static int check_segs(flash_file_t *ctx, int can_write_bl) { } // Load an ELF file and prepare it for flashing -int flash_load(flash_file_t *ctx, const char *name, int can_write_bl) { +int flash_load(flash_file_t *ctx, const char *name, int can_write_bl, int flash_size) { FILE *fd; Elf32_Ehdr ehdr; Elf32_Phdr *phdrs = NULL; uint16_t num_phdrs; + uint32_t flash_end = FLASH_START + flash_size; int res; fd = fopen(name, "rb"); @@ -239,10 +233,10 @@ int flash_load(flash_file_t *ctx, const char *name, int can_write_bl) { goto fail; } - res = build_segs_from_phdrs(ctx, fd, phdrs, num_phdrs); + res = build_segs_from_phdrs(ctx, fd, phdrs, num_phdrs, flash_end); if (res < 0) goto fail; - res = check_segs(ctx, can_write_bl); + res = check_segs(ctx, can_write_bl, flash_end); if (res < 0) goto fail; @@ -362,15 +356,22 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *chipinfo = resp.oldarg[0]; } + uint32_t flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; + if (((*chipinfo & 0xF00) >> 8) > 9) { + flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; + } + + PrintAndLogEx(INFO, "End of flahs: 0x%08x", flash_end); + if (state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { // This command is stupid. Why the heck does it care which area we're // flashing, as long as it's not the bootloader area? The mind boggles. PacketResponseNG resp; if (enable_bl_writes) { - SendCommandBL(CMD_START_FLASH, FLASH_START, FLASH_END, START_FLASH_MAGIC, NULL, 0); + SendCommandBL(CMD_START_FLASH, FLASH_START, flash_end, START_FLASH_MAGIC, NULL, 0); } else { - SendCommandBL(CMD_START_FLASH, BOOTLOADER_END, FLASH_END, 0, NULL, 0); + SendCommandBL(CMD_START_FLASH, BOOTLOADER_END, flash_end, 0, NULL, 0); } return wait_for_ack(&resp); } else { diff --git a/client/flash.h b/client/flash.h index 7140de673..cc0387bf3 100644 --- a/client/flash.h +++ b/client/flash.h @@ -37,7 +37,7 @@ typedef struct { flash_seg_t *segments; } flash_file_t; -int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); +int flash_load(flash_file_t *ctx, const char *name, int can_write_bl, int flash_size); int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *chipid); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); diff --git a/client/flasher.c b/client/flasher.c index 94e2ef6ba..8b6a87bf8 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -21,6 +21,7 @@ #include "ui.h" #define MAX_FILES 4 +#define ONE_KB 1024 static void usage(char *argv0) { PrintAndLogEx(NORMAL, "Usage: %s [-b] image.elf [image.elf...]\n", argv0); @@ -76,6 +77,7 @@ int main(int argc, char **argv) { int num_files = 0; int res; flash_file_t files[MAX_FILES]; + char * filenames[MAX_FILES]; memset(files, 0, sizeof(files)); @@ -102,11 +104,7 @@ int main(int argc, char **argv) { return -1; } } else { - res = flash_load(&files[num_files], argv[i], can_write_bl); - if (res < 0) - return -1; - - PrintAndLogEx(NORMAL, ""); + filenames[num_files] = argv[i]; num_files++; } } @@ -132,9 +130,19 @@ int main(int argc, char **argv) { PrintAndLogEx(NORMAL, "Available memory on this board: "_RED_("UNKNOWN")"\n"); PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new CHIP_INFO command")); PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); + mem_avail = 256; //we default to a low value } + + for (int i = 0 ; i < num_files; ++i){ + res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail*ONE_KB); + if (res < 0) + return -1; + + PrintAndLogEx(NORMAL, ""); + } + PrintAndLogEx(SUCCESS, "\n" _BLUE_("Flashing...")); -// TODO check if enough space on Pm3 mem to write the given files + for (int i = 0; i < num_files; i++) { res = flash_write(&files[i]); if (res < 0) From 44278272a41e9140ba459e87845de1c9196920c2 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 19 Jul 2019 12:39:23 +0200 Subject: [PATCH 22/40] typo --- client/flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/flash.c b/client/flash.c index a29d8d7b0..abd81a9de 100644 --- a/client/flash.c +++ b/client/flash.c @@ -361,7 +361,7 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; } - PrintAndLogEx(INFO, "End of flahs: 0x%08x", flash_end); + PrintAndLogEx(INFO, "End of flash: 0x%08x", flash_end); if (state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { // This command is stupid. Why the heck does it care which area we're From f6f14f82d48d7306492e356f5dd8d36db6ab2c83 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 19 Jul 2019 13:02:12 +0200 Subject: [PATCH 23/40] Add option -i to flasher to query Pm3 for its memory size, and some doc tuning --- CHANGELOG.md | 1 + client/flasher.c | 58 +++++++++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfa26a2b3..45c2074b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Add option -i to flasher to query Pm3 for its memory size (@doegox) - Add support for flashing 512K units (@slurdge) - Add a simple python tool to check the elf sizes (@slurdge) - Change: new keys for Vigik badges in default_keys.dict (@luminouw) diff --git a/client/flasher.c b/client/flasher.c index 8b6a87bf8..4caeab63b 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -24,15 +24,20 @@ #define ONE_KB 1024 static void usage(char *argv0) { - PrintAndLogEx(NORMAL, "Usage: %s [-b] image.elf [image.elf...]\n", argv0); - PrintAndLogEx(NORMAL, "\t-b\tEnable flashing of bootloader area (DANGEROUS)\n"); - PrintAndLogEx(NORMAL, "\nExample:\n\n\t %s "SERIAL_PORT_EXAMPLE_H" armsrc/obj/fullimage.elf", argv0); + PrintAndLogEx(NORMAL, "Usage: %s [-b] image.elf [image.elf...]", argv0); + PrintAndLogEx(NORMAL, " %s -i\n", argv0); + PrintAndLogEx(NORMAL, "\t-b\tEnable flashing of bootloader area (DANGEROUS)"); + PrintAndLogEx(NORMAL, "\t-i\tProbe the connected Proxmark3 to retrieve its memory size"); + PrintAndLogEx(NORMAL, "\nExamples:\n\t %s "SERIAL_PORT_EXAMPLE_H" -i", argv0); + PrintAndLogEx(NORMAL, "\t %s "SERIAL_PORT_EXAMPLE_H" armsrc/obj/fullimage.elf", argv0); #ifdef __linux__ - PrintAndLogEx(NORMAL, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark3 to reappear on ',"); - PrintAndLogEx(NORMAL, " you need to blacklist Proxmark3 for modem-manager - see wiki for more details:\n"); - PrintAndLogEx(NORMAL, " https://github.com/Proxmark/proxmark3/wiki/Gentoo Linux\n"); - PrintAndLogEx(NORMAL, " https://github.com/Proxmark/proxmark3/wiki/Ubuntu Linux\n"); - PrintAndLogEx(NORMAL, " https://github.com/Proxmark/proxmark3/wiki/OSX\n"); + PrintAndLogEx(NORMAL, "\nNote (Linux):\nif the flasher gets stuck in 'Waiting for Proxmark3 to reappear on ',"); + PrintAndLogEx(NORMAL, "you need to blacklist Proxmark3 for modem-manager - see documentation for more details:"); + PrintAndLogEx(NORMAL, "* https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md"); + PrintAndLogEx(NORMAL, "\nMore info on flashing procedure from the official Proxmark3 wiki:"); + PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Gentoo%%20Linux"); + PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Ubuntu%%20Linux"); + PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/OSX\n"); #endif } @@ -78,7 +83,7 @@ int main(int argc, char **argv) { int res; flash_file_t files[MAX_FILES]; char * filenames[MAX_FILES]; - + bool info = false; memset(files, 0, sizeof(files)); session.supports_colors = false; @@ -99,6 +104,8 @@ int main(int argc, char **argv) { if (argv[i][0] == '-') { if (!strcmp(argv[i], "-b")) { can_write_bl = 1; + } else if (!strcmp(argv[i], "-i")) { + info = true; } else { usage(argv[0]); return -1; @@ -133,24 +140,25 @@ int main(int argc, char **argv) { mem_avail = 256; //we default to a low value } - for (int i = 0 ; i < num_files; ++i){ - res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail*ONE_KB); - if (res < 0) - return -1; + if (! info) { + for (int i = 0 ; i < num_files; ++i){ + res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail*ONE_KB); + if (res < 0) + return -1; - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, ""); + } + + PrintAndLogEx(SUCCESS, "\n" _BLUE_("Flashing...")); + + for (int i = 0; i < num_files; i++) { + res = flash_write(&files[i]); + if (res < 0) + return -1; + flash_free(&files[i]); + PrintAndLogEx(NORMAL, "\n"); + } } - - PrintAndLogEx(SUCCESS, "\n" _BLUE_("Flashing...")); - - for (int i = 0; i < num_files; i++) { - res = flash_write(&files[i]); - if (res < 0) - return -1; - flash_free(&files[i]); - PrintAndLogEx(NORMAL, "\n"); - } - res = flash_stop_flashing(); if (res < 0) return -1; From 33f2cb09817936a7bcf8ae8311bc3d46fdb83792 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 19 Jul 2019 13:15:32 +0200 Subject: [PATCH 24/40] Rework flasher exit on error --- client/flasher.c | 56 +++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/client/flasher.c b/client/flasher.c index 4caeab63b..0db87cb94 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -81,6 +81,7 @@ int main(int argc, char **argv) { int can_write_bl = 0; int num_files = 0; int res; + int ret = 0; flash_file_t files[MAX_FILES]; char * filenames[MAX_FILES]; bool info = false; @@ -127,8 +128,10 @@ int main(int argc, char **argv) { uint32_t chipid = 0; res = flash_start_flashing(can_write_bl, serial_port_name, &chipid); - if (res < 0) - return -1; + if (res < 0) { + ret = -1; + goto finish; + } int mem_avail = chipid_to_mem_avail(chipid); if (mem_avail != 0) { @@ -140,32 +143,41 @@ int main(int argc, char **argv) { mem_avail = 256; //we default to a low value } - if (! info) { - for (int i = 0 ; i < num_files; ++i){ - res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail*ONE_KB); - if (res < 0) - return -1; + if (info) + goto finish; - PrintAndLogEx(NORMAL, ""); - } - - PrintAndLogEx(SUCCESS, "\n" _BLUE_("Flashing...")); - - for (int i = 0; i < num_files; i++) { - res = flash_write(&files[i]); - if (res < 0) - return -1; - flash_free(&files[i]); - PrintAndLogEx(NORMAL, "\n"); + for (int i = 0 ; i < num_files; ++i){ + res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail*ONE_KB); + if (res < 0) { + ret = -1; + goto finish; } + PrintAndLogEx(NORMAL, ""); } + + PrintAndLogEx(SUCCESS, "\n" _BLUE_("Flashing...")); + + for (int i = 0; i < num_files; i++) { + res = flash_write(&files[i]); + if (res < 0) { + ret = -1; + goto finish; + } + flash_free(&files[i]); + PrintAndLogEx(NORMAL, "\n"); + } + +finish: res = flash_stop_flashing(); if (res < 0) - return -1; + ret = -1; CloseProxmark(); - PrintAndLogEx(SUCCESS, _BLUE_("All done.")); - PrintAndLogEx(SUCCESS, "\nHave a nice day!"); - return 0; + if (ret==0) + PrintAndLogEx(SUCCESS, _BLUE_("All done.")); + else + PrintAndLogEx(ERR, "Aborted on error."); + PrintAndLogEx(NORMAL, "\nHave a nice day!"); + return ret; } From 8e6258936caea0fda3c6b82d2038cfae66acac66 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 19 Jul 2019 13:18:35 +0200 Subject: [PATCH 25/40] make style --- armsrc/iso14443a.c | 8 ++++---- bootrom/bootrom.c | 8 ++++---- client/emv/emvcore.c | 2 +- client/flasher.c | 8 ++++---- tools/analyzesize.py | 1 - 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index bcd898c89..9c76a476d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1596,7 +1596,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing break; } } - + //iceman test if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); @@ -1937,7 +1937,7 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start //----------------------------------------------------------------------------- static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receivedResponsePar, uint16_t offset) { uint32_t c = 0; - + if (!iso14443a_active) return false; @@ -1968,7 +1968,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive return false; } } - + // timeout already in ms + 100ms guard time if (GetTickCount() - receive_timer > timeout + 100) break; @@ -2378,7 +2378,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { UartReset(); NextTransferTime = 2 * DELAY_ARM2AIR_AS_READER; iso14a_set_timeout(1060); // 106 * 10ms default - + iso14443a_active = true; } diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index bda7de2c6..0726f332d 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -164,9 +164,9 @@ void UsbPacketReceived(uint8_t *packet, int len) { offset = (AT91C_IFLASH_NB_OF_PAGES / 2) * AT91C_IFLASH_PAGE_SIZE / sizeof(uint32_t); } for (i = 0 + (64 * j); i < 64 + (64 * j); i++) { - flash_mem[offset+i] = c->d.asDwords[i]; + flash_mem[offset + i] = c->d.asDwords[i]; } - + /* Check that the address that we are supposed to write to is within our allowed region */ if (((flash_address + AT91C_IFLASH_PAGE_SIZE - 1) >= end_addr) || (flash_address < start_addr)) { /* Disallow write */ @@ -175,8 +175,8 @@ void UsbPacketReceived(uint8_t *packet, int len) { } else { efc_bank->EFC_FCR = MC_FLASH_COMMAND_KEY | - MC_FLASH_COMMAND_PAGEN(page_n) | - AT91C_MC_FCMD_START_PROG; + MC_FLASH_COMMAND_PAGEN(page_n) | + AT91C_MC_FCMD_START_PROG; } // Wait until flashing of page finishes diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 6657f2ce3..c814eb11e 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -539,7 +539,7 @@ int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, i--; } else { // (1) - card select error, (4) reply timeout, (200) - result length = 0 - if (res == 1 || res == 4 ||res == 200) { + if (res == 1 || res == 4 || res == 200) { if (!LeaveFieldON) DropFieldEx(channel); diff --git a/client/flasher.c b/client/flasher.c index 0db87cb94..25585e7cd 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -83,7 +83,7 @@ int main(int argc, char **argv) { int res; int ret = 0; flash_file_t files[MAX_FILES]; - char * filenames[MAX_FILES]; + char *filenames[MAX_FILES]; bool info = false; memset(files, 0, sizeof(files)); @@ -146,8 +146,8 @@ int main(int argc, char **argv) { if (info) goto finish; - for (int i = 0 ; i < num_files; ++i){ - res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail*ONE_KB); + for (int i = 0 ; i < num_files; ++i) { + res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail * ONE_KB); if (res < 0) { ret = -1; goto finish; @@ -174,7 +174,7 @@ finish: CloseProxmark(); - if (ret==0) + if (ret == 0) PrintAndLogEx(SUCCESS, _BLUE_("All done.")); else PrintAndLogEx(ERR, "Aborted on error."); diff --git a/tools/analyzesize.py b/tools/analyzesize.py index 70c35de26..4a5211c67 100755 --- a/tools/analyzesize.py +++ b/tools/analyzesize.py @@ -31,4 +31,3 @@ elif action.lower() == "diff": flash = text+data print_increase(flash, flash_ref, "Flash") print_increase(bss, bss_ref, "RAM") - \ No newline at end of file From f4ffa005c9783549192f04fcc0d5d6a77207e6f3 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 19 Jul 2019 14:00:26 +0200 Subject: [PATCH 26/40] Travis on osx: testing xcode9.2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b81af3d97..723d1c1a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ compiler: gcc matrix: include: - os: osx - osx_image: xcode9.1 # OS X 10.13.1 + osx_image: xcode9.2 # OS X 10.13 - os: linux dist: xenial sudo: required From 0b38c23ed3161153ad1735ec82decd5f3e35b6d5 Mon Sep 17 00:00:00 2001 From: Eloff Date: Fri, 19 Jul 2019 16:27:08 +0300 Subject: [PATCH 27/40] 'hf mf sim' - wrong access rights to write key B in trailer; typo; simple code optimization --- CHANGELOG.md | 1 + armsrc/iso14443a.c | 4 ++-- armsrc/mifaresim.c | 12 +++++------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c78d813db..95930c002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Fix 'hf mf sim' - wrong access rights to write key B in trailer (@McEloff) - Add 'hw standalone' to jump to standalone mode from command line or script (@doegox) - Add to 'hf 14a apdu' print apdu and compose apdu (@merlokk) - Change: buggy 'mem read' removed, 'mem save' renamed 'mem dump', can now display too (@doegox) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index b5e612ec9..f905fc536 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -741,7 +741,7 @@ static void Code4bitAnswerAsTag(uint8_t cmd) { //----------------------------------------------------------------------------- // Wait for commands from reader -// stop when button is pressed +// stop when button is pressed or client usb connection resets // or return TRUE when command is captured //----------------------------------------------------------------------------- static bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len) { @@ -1092,7 +1092,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { #define ORDER_SELECT_CL2 30 #define ORDER_EV1_COMP_WRITE 40 #define ORDER_RATS 70 - int order = ORDER_NONE; + uint8_t order = ORDER_NONE; int retval = PM3_SUCCESS; diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index d211b543b..25abcb10a 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -56,7 +56,7 @@ static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t act } case AC_KEYB_WRITE: { if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_KEYB_WRITE"); - return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x04)) + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_AC_READ: { @@ -457,8 +457,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { uint8_t *rats = NULL; uint8_t rats_len = 0; - uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; - //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 // This will be used in the reader-only attack. @@ -713,7 +711,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { crypto1_word(pcs, cuid ^ nonce, 0); // rAUTH_NT contains prepared nonce for authenticate EmSendCmd(rAUTH_NT, sizeof(rAUTH_NT)); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Reader authenticating for block %d (0x%02x) with key %c - nonce: %02X - ciud: %02X", receivedCmd_dec[1], receivedCmd_dec[1], (cardAUTHKEY == 0) ? 'A' : 'B', rAUTH_AT, cuid); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Reader authenticating for block %d (0x%02x) with key %c - nonce: %02X - ciud: %02X", receivedCmd_dec[1], receivedCmd_dec[1], (cardAUTHKEY == 0) ? 'A' : 'B', rAUTH_NT, cuid); } else { // nested authentication /* @@ -1044,9 +1042,9 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { } ans = prng_successor(nonce, 96); - num_to_bytes(ans, 4, rAUTH_AT); - mf_crypto1_encrypt(pcs, rAUTH_AT, 4, response_par); - EmSendCmdPar(rAUTH_AT, 4, response_par); + num_to_bytes(ans, 4, response); + mf_crypto1_encrypt(pcs, response, 4, response_par); + EmSendCmdPar(response, 4, response_par); if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("[MFEMUL_AUTH1] AUTH COMPLETED for sector %d with key %c. time=%d", From c056e56492db04a8b578ecb34241663927c82cb0 Mon Sep 17 00:00:00 2001 From: slurdge Date: Fri, 19 Jul 2019 18:08:59 +0200 Subject: [PATCH 28/40] Add a version command to the bootloader (not activated yet) --- bootrom/bootrom.c | 7 +++++++ include/pm3_cmd.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 0726f332d..491cafec7 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -126,6 +126,7 @@ void UsbPacketReceived(uint8_t *packet, int len) { DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM | DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH | DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO; + //to add later: DEVICE_INFO_FLAG_UNDERSTANDS_VERSION if (common_area.flags.osimage_present) arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; @@ -140,6 +141,12 @@ void UsbPacketReceived(uint8_t *packet, int len) { } break; + case CMD_BL_VERSION: { + dont_ack = 1; + arg0 = VERSION_1_0_0; + reply_old(CMD_BL_VERSION, arg0, 0, 0, 0, 0); + } + case CMD_SETUP_WRITE: { /* The temporary write buffer of the embedded flash controller is mapped to the * whole memory region, only the last 8 bits are decoded. diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index d271d3099..55204e1a4 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -208,6 +208,7 @@ typedef struct { #define CMD_HARDWARE_RESET 0x0004 #define CMD_START_FLASH 0x0005 #define CMD_CHIP_INFO 0x0006 +#define CMD_BL_VERSION 0x0007 #define CMD_NACK 0x00fe #define CMD_ACK 0x00ff @@ -536,6 +537,12 @@ typedef struct { /* Set if this device understands the chip info command */ #define DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO (1<<5) +/* Set if this device understands the version command */ +#define DEVICE_INFO_FLAG_UNDERSTANDS_VERSION (1<<6) + +// Different versions here. Each version should increse the number +#define VERSION_1_0_0 1 + /* CMD_START_FLASH may have three arguments: start of area to flash, end of area to flash, optional magic. The bootrom will not allow to overwrite itself unless this magic From 68aa9d631dc1b448eb9ad1311133871dd00f01a8 Mon Sep 17 00:00:00 2001 From: slurdge Date: Fri, 19 Jul 2019 18:21:20 +0200 Subject: [PATCH 29/40] Flasher support for versionning Only bootrom with version > 1.0.0 will allow 512K writes --- bootrom/bootrom.c | 2 +- client/flash.c | 24 +++++++++++++++++++++++- include/pm3_cmd.h | 5 ++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 491cafec7..030074177 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -143,7 +143,7 @@ void UsbPacketReceived(uint8_t *packet, int len) { case CMD_BL_VERSION: { dont_ack = 1; - arg0 = VERSION_1_0_0; + arg0 = BL_VERSION_1_0_0; reply_old(CMD_BL_VERSION, arg0, 0, 0, 0, 0); } diff --git a/client/flash.c b/client/flash.c index abd81a9de..2c990a80a 100644 --- a/client/flash.c +++ b/client/flash.c @@ -356,8 +356,30 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *chipinfo = resp.oldarg[0]; } + int version = BL_VERSION_INVALID; + if (state & DEVICE_INFO_FLAG_UNDERSTANDS_VERSION) { + SendCommandBL(CMD_BL_VERSION, 0, 0, 0, NULL, 0); + PacketResponseNG resp; + WaitForResponse(CMD_BL_VERSION, &resp); + version = resp.oldarg[0]; + if ((version < BL_VERSION_FIRST || version > BL_VERSION_LAST)) { + version = BL_VERSION_INVALID; + } + } else { + PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new CMD_BL_VERSION command")); + PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); + } + + bool allow_512k_writes = false; + if (version == BL_VERSION_INVALID) { + PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported an invalid version number")); + PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); + } else if (version >= BL_VERSION_1_0_0) { + allow_512k_writes = true; + } + uint32_t flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; - if (((*chipinfo & 0xF00) >> 8) > 9) { + if ((((*chipinfo & 0xF00) >> 8) > 9) && allow_512k_writes) { flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 55204e1a4..640a3e2bf 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -541,7 +541,10 @@ typedef struct { #define DEVICE_INFO_FLAG_UNDERSTANDS_VERSION (1<<6) // Different versions here. Each version should increse the number -#define VERSION_1_0_0 1 +#define BL_VERSION_INVALID 0 +#define BL_VERSION_1_0_0 1 +#define BL_VERSION_FIRST BL_VERSION_1_0_0 +#define BL_VERSION_LAST BL_VERSION_1_0_0 /* CMD_START_FLASH may have three arguments: start of area to flash, end of area to flash, optional magic. From daae8906675491d2b64bd7346f2fb41473243f82 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 19 Jul 2019 22:59:51 +0200 Subject: [PATCH 30/40] Bootrom & flasher changes: * use macros for versions * activate UNDERSTANDS_VERSION in bootrom * fix missing break; bug in bootrom * force flash_load to reject images > 256k if bootloader not up to date * move logic from flasher to flash --- CHANGELOG.md | 1 + bootrom/bootrom.c | 5 ++- client/flash.c | 107 +++++++++++++++++++++++++++++++++++++--------- client/flash.h | 3 +- client/flasher.c | 52 ++-------------------- include/pm3_cmd.h | 14 ++++-- 6 files changed, 105 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 001a53a34..b38e12e3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Fix support for flashing 512K units with old bootrom (@slurdge/@doegox) - Fix 'hf mf sim' - wrong access rights to write key B in trailer (@McEloff) - Add option -i to flasher to query Pm3 for its memory size (@doegox) - Add support for flashing 512K units (@slurdge) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 030074177..1a3dafc12 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -125,8 +125,8 @@ void UsbPacketReceived(uint8_t *packet, int len) { arg0 = DEVICE_INFO_FLAG_BOOTROM_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM | DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH | - DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO; - //to add later: DEVICE_INFO_FLAG_UNDERSTANDS_VERSION + DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO | + DEVICE_INFO_FLAG_UNDERSTANDS_VERSION; if (common_area.flags.osimage_present) arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; @@ -146,6 +146,7 @@ void UsbPacketReceived(uint8_t *packet, int len) { arg0 = BL_VERSION_1_0_0; reply_old(CMD_BL_VERSION, arg0, 0, 0, 0, 0); } + break; case CMD_SETUP_WRITE: { /* The temporary write buffer of the embedded flash controller is mapped to the diff --git a/client/flash.c b/client/flash.c index 2c990a80a..316b53066 100644 --- a/client/flash.c +++ b/client/flash.c @@ -17,6 +17,8 @@ #define BLOCK_SIZE 0x200 +#define FLASHER_VERSION BL_VERSION_1_0_0 + static const uint8_t elf_ident[] = { 0x7f, 'E', 'L', 'F', ELFCLASS32, @@ -24,6 +26,42 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; +static int chipid_to_mem_avail(uint32_t iChipID) { + int mem_avail = 0; + switch ((iChipID & 0xF00) >> 8) { + case 0: + mem_avail = 0; + break; + case 1: + mem_avail = 8; + break; + case 2: + mem_avail = 16; + break; + case 3: + mem_avail = 32; + break; + case 5: + mem_avail = 64; + break; + case 7: + mem_avail = 128; + break; + case 9: + mem_avail = 256; + break; + case 10: + mem_avail = 512; + break; + case 12: + mem_avail = 1024; + break; + case 14: + mem_avail = 2048; + } + return mem_avail; +} + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, uint16_t num_phdrs, uint32_t flash_end) { @@ -339,9 +377,19 @@ static int wait_for_ack(PacketResponseNG *ack) { return 0; } +static void flash_suggest_update_bootloader(void) { + PrintAndLogEx(ERR, _RED_("It is recommended that you first update your bootloader alone,")); + PrintAndLogEx(ERR, _RED_("reboot the Proxmark3 then only update the main firmware") "\n"); +} + +static void flash_suggest_update_flasher(void) { + PrintAndLogEx(ERR, _RED_("It is recommended that you first update your flasher")); +} + // Go into flashing mode -int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *chipinfo) { +int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed) { uint32_t state; + uint32_t chipinfo = 0; if (enter_bootloader(serial_port_name) < 0) return -1; @@ -353,7 +401,7 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t SendCommandBL(CMD_CHIP_INFO, 0, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_CHIP_INFO, &resp); - *chipinfo = resp.oldarg[0]; + chipinfo = resp.oldarg[0]; } int version = BL_VERSION_INVALID; @@ -362,32 +410,51 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t PacketResponseNG resp; WaitForResponse(CMD_BL_VERSION, &resp); version = resp.oldarg[0]; - if ((version < BL_VERSION_FIRST || version > BL_VERSION_LAST)) { + if ((BL_VERSION_MAJOR(version) < BL_VERSION_FIRST_MAJOR) || (BL_VERSION_MAJOR(version) > BL_VERSION_LAST_MAJOR)) { + // version info seems fishy version = BL_VERSION_INVALID; + PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported an invalid version number")); + flash_suggest_update_bootloader(); + // + } else if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(FLASHER_VERSION)) { + PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported a version older than this flasher")); + flash_suggest_update_bootloader(); + } else if (BL_VERSION_MAJOR(version) > BL_VERSION_MAJOR(FLASHER_VERSION)) { + PrintAndLogEx(ERR, _RED_("Note: Your bootloader is more recent than this flasher")); + flash_suggest_update_flasher(); } } else { PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new CMD_BL_VERSION command")); - PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); - } - - bool allow_512k_writes = false; - if (version == BL_VERSION_INVALID) { - PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported an invalid version number")); - PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); - } else if (version >= BL_VERSION_1_0_0) { - allow_512k_writes = true; + flash_suggest_update_bootloader(); } uint32_t flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; - if ((((*chipinfo & 0xF00) >> 8) > 9) && allow_512k_writes) { - flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; + *max_allowed = 256; + + int mem_avail = chipid_to_mem_avail(chipinfo); + if (mem_avail != 0) { + PrintAndLogEx(NORMAL, "Available memory on this board: %uK bytes\n", mem_avail); + if (mem_avail > 256) { + if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(BL_VERSION_1_0_0)) { + PrintAndLogEx(ERR, _RED_("Your bootloader does not support writing above 256k")); + flash_suggest_update_bootloader(); + } else { + flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; + *max_allowed = mem_avail; + } + } + } else { + PrintAndLogEx(NORMAL, "Available memory on this board: "_RED_("UNKNOWN")"\n"); + PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new CHIP_INFO command")); + flash_suggest_update_bootloader(); + } + + if (enable_bl_writes) { + PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", FLASH_START, flash_end); + } else { + PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", BOOTLOADER_END, flash_end); } - - PrintAndLogEx(INFO, "End of flash: 0x%08x", flash_end); - if (state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { - // This command is stupid. Why the heck does it care which area we're - // flashing, as long as it's not the bootloader area? The mind boggles. PacketResponseNG resp; if (enable_bl_writes) { @@ -398,7 +465,7 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t return wait_for_ack(&resp); } else { PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new START_FLASH command")); - PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); + flash_suggest_update_bootloader(); } return 0; } diff --git a/client/flash.h b/client/flash.h index cc0387bf3..4f05029dc 100644 --- a/client/flash.h +++ b/client/flash.h @@ -38,10 +38,9 @@ typedef struct { } flash_file_t; int flash_load(flash_file_t *ctx, const char *name, int can_write_bl, int flash_size); -int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *chipid); +int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); - #endif diff --git a/client/flasher.c b/client/flasher.c index 25585e7cd..97e3d7a1c 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -41,42 +41,6 @@ static void usage(char *argv0) { #endif } -int chipid_to_mem_avail(uint32_t iChipID) { - int mem_avail = 0; - switch ((iChipID & 0xF00) >> 8) { - case 0: - mem_avail = 0; - break; - case 1: - mem_avail = 8; - break; - case 2: - mem_avail = 16; - break; - case 3: - mem_avail = 32; - break; - case 5: - mem_avail = 64; - break; - case 7: - mem_avail = 128; - break; - case 9: - mem_avail = 256; - break; - case 10: - mem_avail = 512; - break; - case 12: - mem_avail = 1024; - break; - case 14: - mem_avail = 2048; - } - return mem_avail; -} - int main(int argc, char **argv) { int can_write_bl = 0; int num_files = 0; @@ -126,28 +90,18 @@ int main(int argc, char **argv) { return -1; } - uint32_t chipid = 0; - res = flash_start_flashing(can_write_bl, serial_port_name, &chipid); + uint32_t max_allowed = 0; + res = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed); if (res < 0) { ret = -1; goto finish; } - int mem_avail = chipid_to_mem_avail(chipid); - if (mem_avail != 0) { - PrintAndLogEx(NORMAL, "Available memory on this board: %uK bytes\n", mem_avail); - } else { - PrintAndLogEx(NORMAL, "Available memory on this board: "_RED_("UNKNOWN")"\n"); - PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new CHIP_INFO command")); - PrintAndLogEx(ERR, _RED_("It is recommended that you update your bootloader") "\n"); - mem_avail = 256; //we default to a low value - } - if (info) goto finish; for (int i = 0 ; i < num_files; ++i) { - res = flash_load(&files[i], filenames[i], can_write_bl, mem_avail * ONE_KB); + res = flash_load(&files[i], filenames[i], can_write_bl, max_allowed * ONE_KB); if (res < 0) { ret = -1; goto finish; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 640a3e2bf..39edb2b61 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -540,11 +540,17 @@ typedef struct { /* Set if this device understands the version command */ #define DEVICE_INFO_FLAG_UNDERSTANDS_VERSION (1<<6) -// Different versions here. Each version should increse the number +#define BL_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) +#define BL_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) +#define BL_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) +#define BL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) +// Some boundaries to distinguish valid versions from corrupted info +#define BL_VERSION_FIRST_MAJOR 1 +#define BL_VERSION_LAST_MAJOR 99 #define BL_VERSION_INVALID 0 -#define BL_VERSION_1_0_0 1 -#define BL_VERSION_FIRST BL_VERSION_1_0_0 -#define BL_VERSION_LAST BL_VERSION_1_0_0 +// Different versions here. Each version should increase the numbers +#define BL_VERSION_1_0_0 BL_MAKE_VERSION(1, 0, 0) + /* CMD_START_FLASH may have three arguments: start of area to flash, end of area to flash, optional magic. From 5c3676ad8133609edc63b3c6d23e04550bccd239 Mon Sep 17 00:00:00 2001 From: cjbrigato Date: Mon, 22 Jul 2019 22:56:06 +0200 Subject: [PATCH 31/40] ADD: Early SPIFFS implementation see #257, UPDATE HF_COLIN accordingly --- armsrc/Makefile | 2 + armsrc/Standalone/hf_colin.c | 105 +- armsrc/Standalone/hf_colin.h | 3 +- armsrc/appmain.c | 129 ++ armsrc/flashmem.c | 1 + armsrc/flashmem.h | 1 + armsrc/spiffs.c | 606 +++++++++ armsrc/spiffs.h | 861 +++++++++++++ armsrc/spiffs_cache.c | 319 +++++ armsrc/spiffs_check.c | 1008 +++++++++++++++ armsrc/spiffs_config.h | 388 ++++++ armsrc/spiffs_gc.c | 606 +++++++++ armsrc/spiffs_hydrogen.c | 1452 +++++++++++++++++++++ armsrc/spiffs_nucleus.c | 2365 ++++++++++++++++++++++++++++++++++ armsrc/spiffs_nucleus.h | 842 ++++++++++++ armsrc/string.c | 98 ++ armsrc/string.h | 4 + client/Makefile | 1 + client/cmddata.c | 6 +- client/cmdflashmem.c | 3 +- client/cmdflashmem.h | 1 + client/cmdflashmemspiffs.c | 457 +++++++ client/cmdflashmemspiffs.h | 30 + client/cmdhffelica.c | 2 +- client/cmdhficlass.c | 4 +- client/cmdhflegic.c | 6 +- client/cmdhfmf.c | 2 +- client/cmdhfmfu.c | 2 +- client/cmdlfcotag.c | 2 +- client/cmdlfem4x.c | 2 +- client/cmdlft55xx.c | 2 +- client/cmdtrace.c | 4 +- client/comms.c | 6 +- client/comms.h | 4 +- client/scripting.c | 4 +- include/pm3_cmd.h | 34 + 36 files changed, 9258 insertions(+), 104 deletions(-) create mode 100644 armsrc/spiffs.c create mode 100644 armsrc/spiffs.h create mode 100644 armsrc/spiffs_cache.c create mode 100644 armsrc/spiffs_check.c create mode 100644 armsrc/spiffs_config.h create mode 100644 armsrc/spiffs_gc.c create mode 100644 armsrc/spiffs_hydrogen.c create mode 100644 armsrc/spiffs_nucleus.c create mode 100644 armsrc/spiffs_nucleus.h create mode 100644 client/cmdflashmemspiffs.c create mode 100644 client/cmdflashmemspiffs.h diff --git a/armsrc/Makefile b/armsrc/Makefile index 6d3921084..105a880a7 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -34,6 +34,7 @@ SRC_CRAPTO1 = crypto1.c des.c desfire_key.c desfire_crypto.c mifaredesfire.c aes SRC_CRC = crc.c crc16.c crc32.c SRC_ICLASS = iclass.c optimized_cipher.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c +SRC_SPIFFS = spiffs.c spiffs_cache.c spiffs_check.c spiffs_gc.c spiffs_nucleus.c spiffs_hydrogen.c # SRC_BEE = bee.c # RDV40 related hardware support @@ -97,6 +98,7 @@ THUMBSRC = start.c \ $(SRC_SMARTCARD) \ $(SRC_FPC) \ $(SRC_HITAG) \ + $(SRC_SPIFFS) \ appmain.c \ printf.c \ commonutil.c \ diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index d74eaeb95..2189f1146 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Colin Brigato, 2016, 2017 +// Colin Brigato, 2016, 2017, 2018, 2019 // Christian Herrmann, 2017 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -13,6 +13,7 @@ #define MF1KSZ 1024 #define MF1KSZSIZE 64 #define AUTHENTICATION_TIMEOUT 848 +#define HFCOLIN_LASTTAG_SYMLINK "hf_colin/lasttag.bin" uint8_t cjuid[10]; uint32_t cjcuid; @@ -92,49 +93,26 @@ void ReadLastTagFromFlash() { LED_B_ON(); LED_C_ON(); LED_D_ON(); - uint32_t startidx = 0; uint16_t len = 1024; + size_t size = len; DbprintfEx(FLAG_NEWLINE, "Button HELD ! Using LAST Known TAG for Simulation..."); cjSetCursLeft(); - size_t size = len; uint8_t *mem = BigBuf_malloc(size); - FlashmemSetSpiBaudrate(24000000); + //this one will handle filetype (symlink or not) and resolving by itself + rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK,(uint8_t *)mem,len, RDV40_SPIFFS_SAFETY_SAFE); - if (!FlashInit()) { - return; - } - Flash_CheckBusy(BUSY_TIMEOUT); + emlSetMem(mem, 0, 64); - uint32_t start_time = GetTickCount(); - uint32_t delta_time = 0; - - for (size_t i = 0; i < len; i += size) { - len = MIN((len - i), size); - uint16_t isok = Flash_ReadDataCont(startidx + i, mem, len); - if (isok == len) { - emlSetMem(mem, 0, 64); - } else { - DbprintfEx(FLAG_NEWLINE, "FlashMem reading failed | %d | %d", len, isok); - cjSetCursLeft(); - FlashStop(); - SpinOff(100); - return; - } - } - delta_time = GetTickCountDelta(start_time); DbprintfEx(FLAG_NEWLINE, "[OK] Last tag recovered from FLASHMEM set to emulator"); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "%s[IN]%s %s%dms%s for TAG_FLASH_READ", _XGREEN_, _XWHITE_, _XYELLOW_, delta_time, _XWHITE_); - cjSetCursLeft(); - FlashStop(); SpinOff(0); return; } -void WriteTagToFlash(uint8_t index, size_t size) { +void WriteTagToFlash(uint32_t uid, size_t size) { SpinOff(0); LED_A_ON(); LED_B_ON(); @@ -142,61 +120,23 @@ void WriteTagToFlash(uint8_t index, size_t size) { LED_D_ON(); uint32_t len = size; - uint32_t bytes_sent = 0; - uint32_t bytes_remaining = len; - uint8_t data[(size * (16 * 64)) / 1024]; - uint8_t buff[PAGESIZE]; emlGetMem(data, 0, (size * 64) / 1024); + char dest[SPIFFS_OBJ_NAME_LEN]; + uint8_t buid[4]; + num_to_bytes(uid,4,buid); + sprintf(dest,"hf_colin/mf_%02x%02x%02x%02x.bin",buid[0],buid[1],buid[2],buid[3]); + + // TODO : by using safe function for multiple writes we are both breaking cache mecanisms and making useless and unoptimized mount operations + // we should manage at out level the mount status before and after the whole standalone mode + rdv40_spiffs_write((char *)dest,(uint8_t *)data,len, RDV40_SPIFFS_SAFETY_SAFE); + // lastag will only contain filename/path to last written tag file so we don't loose time or space. + rdv40_spiffs_make_symlink((char *)dest,(char *)HFCOLIN_LASTTAG_SYMLINK,RDV40_SPIFFS_SAFETY_SAFE); - FlashmemSetSpiBaudrate(48000000); - - if (!FlashInit()) { - return; - } - - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(0, 0); - - uint32_t start_time = GetTickCount(); - uint32_t delta_time = 0; - - while (bytes_remaining > 0) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - - uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - memcpy(buff, data + bytes_sent, bytes_in_packet); - - bytes_remaining -= bytes_in_packet; - uint16_t res = Flash_WriteDataCont(bytes_sent + (index * size), buff, bytes_in_packet); - bytes_sent += bytes_in_packet; - - uint8_t isok = (res == bytes_in_packet) ? 1 : 0; - - if (!isok) { - DbprintfEx(FLAG_NEWLINE, "FlashMem write FAILEd [offset %u]", bytes_sent); - cjSetCursLeft(); - SpinOff(100); - return; - } - - LED_A_INV(); - LED_B_INV(); - LED_C_INV(); - LED_D_INV(); - } - delta_time = GetTickCountDelta(start_time); - - DbprintfEx(FLAG_NEWLINE, "[OK] TAG WRITTEN TO FLASH ! [0-to offset %u]", bytes_sent); + DbprintfEx(FLAG_NEWLINE, "[OK] TAG WRITTEN TO FLASH !"); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "%s[IN]%s %s%dms%s for TAG_FLASH_WRITE", _XGREEN_, _XWHITE_, _XYELLOW_, delta_time, _XWHITE_); - cjSetCursLeft(); - FlashStop(); SpinOff(0); return; } @@ -429,7 +369,7 @@ failtag: //----------------------------------------------------------------------------- // also we could avoid first UID check for every block - // then let's expose this “optimal case” of “well known vigik schemes” : + // then let's expose this optimal case of well known vigik schemes : for (uint8_t type = 0; type < 2 && !err && !trapped; type++) { for (int sec = 0; sec < sectorsCnt && !err && !trapped; ++sec) { key = cjat91_saMifareChkKeys(sec * 4, type, NULL, size, &keyBlock[0], &key64); @@ -437,7 +377,7 @@ failtag: if (key == -1) { err = 1; allKeysFound = false; - // used in “portable” imlementation on microcontroller: it reports back the fail and open the standalone lock + // used in portable imlementation on microcontroller: it reports back the fail and open the standalone lock // reply_old(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); break; } else if (key == -2) { @@ -755,10 +695,9 @@ failtag: cjSetCursLeft(); cjSetCursLeft(); - WriteTagToFlash(0, 1024); + WriteTagToFlash(cjcuid, 1024); readysim: - // SIM ? cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "-> We launch Emulation ->"); @@ -1022,6 +961,8 @@ void saMifareMakeTag(void) { } } + +//TODO : make this work either for a Gen1a or for a block 0 direct write all transparently //----------------------------------------------------------------------------- // Matt's StandAlone mod. // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) diff --git a/armsrc/Standalone/hf_colin.h b/armsrc/Standalone/hf_colin.h index 9068e2d09..e669f0417 100644 --- a/armsrc/Standalone/hf_colin.h +++ b/armsrc/Standalone/hf_colin.h @@ -30,6 +30,7 @@ #include "vtsend.h" #include "apps.h" #include "printf.h" +#include "spiffs.h" #define _XRED_ "\x1b[31m" #define _XGREEN_ "\x1b[32m" @@ -44,7 +45,7 @@ int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, ui int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype); void saMifareMakeTag(void); int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -void WriteTagToFlash(uint8_t index, size_t size); +void WriteTagToFlash(uint32_t uid, size_t size); const char clearTerm[8] = {0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, '\0'}; diff --git a/armsrc/appmain.c b/armsrc/appmain.c index d9c17e3b3..59174153f 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -43,6 +43,7 @@ #ifdef WITH_FLASH #include "flashmem.h" +#include "spiffs.h" #endif //============================================================================= @@ -1589,6 +1590,134 @@ static void PacketReceived(PacketCommandNG *packet) { break; } #ifdef WITH_FLASH + case CMD_SPIFFS_TEST: { + test_spiffs(); + break; + } + case CMD_SPIFFS_MOUNT: { + rdv40_spiffs_lazy_mount(); + break; + } + case CMD_SPIFFS_UNMOUNT: { + rdv40_spiffs_lazy_unmount(); + break; + } + case CMD_SPIFFS_PRINT_TREE: { + rdv40_spiffs_safe_print_tree(false); + break; + } + case CMD_SPIFFS_PRINT_FSINFO: { + rdv40_spiffs_safe_print_fsinfos(); + break; + } + case CMD_SPIFFS_DOWNLOAD: { + LED_B_ON(); + uint8_t filename[32]; + uint8_t *pfilename = packet->data.asBytes; + memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs dump : %s", filename); + + //uint32_t size = 0; + //rdv40_spiffs_stat((char *)filename, (uint32_t *)size,RDV40_SPIFFS_SAFETY_SAFE); + uint32_t size = packet->oldarg[1]; + //uint8_t buff[size]; + + uint8_t *buff = BigBuf_malloc(size); + rdv40_spiffs_read_as_filetype((char *)filename,(uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); + + // arg0 = filename + // arg1 = size + // arg2 = RFU + + for (size_t i = 0; i < size; i += PM3_CMD_DATA_SIZE) { + size_t len = MIN((size - i), PM3_CMD_DATA_SIZE); + int result = reply_old(CMD_SPIFFS_DOWNLOADED, i, len, 0, buff + i, len); + if (result != PM3_SUCCESS) + Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result); + } + // Trigger a finish downloading signal with an ACK frame + reply_old(CMD_ACK, 1, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_STAT:{ + LED_B_ON(); + uint8_t filename[32]; + uint8_t *pfilename = packet->data.asBytes; + memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs STAT : %s", filename); + int changed = rdv40_spiffs_lazy_mount(); + uint32_t size = size_in_spiffs((char *)filename); + if (changed) rdv40_spiffs_lazy_unmount(); + reply_old(CMD_ACK, size, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_REMOVE:{ + LED_B_ON(); + uint8_t filename[32]; + uint8_t *pfilename = packet->data.asBytes; + memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs REMOVE : %s", filename); + rdv40_spiffs_remove((char *) filename,RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_RENAME:{ + LED_B_ON(); + uint8_t srcfilename[32]; + uint8_t destfilename[32]; + uint8_t *pfilename = packet->data.asBytes; + char *token; + token = strtok((char *)pfilename, ","); + strcpy((char*)srcfilename,token); + token = strtok(NULL,","); + strcpy((char *)destfilename,token); + if (DBGLEVEL > 1) Dbprintf("> Filename received as source for spiffs RENAME : %s", srcfilename); + if (DBGLEVEL > 1) Dbprintf("> Filename received as destination for spiffs RENAME : %s", destfilename); + rdv40_spiffs_rename((char *) srcfilename,(char *)destfilename,RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_COPY:{ + LED_B_ON(); + uint8_t srcfilename[32]; + uint8_t destfilename[32]; + uint8_t *pfilename = packet->data.asBytes; + char *token; + token = strtok((char *)pfilename, ","); + strcpy((char*)srcfilename,token); + token = strtok(NULL,","); + strcpy((char *)destfilename,token); + if (DBGLEVEL > 1) Dbprintf("> Filename received as source for spiffs COPY : %s", srcfilename); + if (DBGLEVEL > 1) Dbprintf("> Filename received as destination for spiffs COPY : %s", destfilename); + rdv40_spiffs_copy((char *) srcfilename,(char *)destfilename,RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_WRITE: { + LED_B_ON(); + uint8_t filename[32]; + uint32_t append = packet->oldarg[0]; + uint32_t size = packet->oldarg[1]; + uint8_t *data = packet->data.asBytes; + + //rdv40_spiffs_lazy_mount(); + + uint8_t *pfilename = packet->data.asBytes; + memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); + data+=SPIFFS_OBJ_NAME_LEN; + + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); + if (!append) { + rdv40_spiffs_write((char *) filename,(uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_append((char *) filename,(uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + } + reply_old(CMD_ACK, 1, 0, 0, 0, 0); + LED_B_OFF(); + break; + } case CMD_FLASHMEM_SET_SPIBAUDRATE: { FlashmemSetSpiBaudrate(packet->oldarg[0]); break; diff --git a/armsrc/flashmem.c b/armsrc/flashmem.c index ad4861ce3..c53169255 100644 --- a/armsrc/flashmem.c +++ b/armsrc/flashmem.c @@ -554,6 +554,7 @@ void Flashmem_print_status(void) { } void Flashmem_print_info(void) { + if (!FlashInit()) return; DbpString(_BLUE_("Flash memory dictionary loaded")); diff --git a/armsrc/flashmem.h b/armsrc/flashmem.h index 59d3bef9c..26d2d974d 100644 --- a/armsrc/flashmem.h +++ b/armsrc/flashmem.h @@ -142,5 +142,6 @@ uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); void Flashmem_print_info(void); +uint16_t FlashSendLastByte(uint32_t data); #endif diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c new file mode 100644 index 000000000..b5db53c42 --- /dev/null +++ b/armsrc/spiffs.c @@ -0,0 +1,606 @@ +//----------------------------------------------------------------------------- +// Colin J. Brigato, 2019 - [colin@brigato.fr] +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// SPIFFS api for RDV40 Integration by Colin Brigato +//----------------------------------------------------------------------------- + +#define SPIFFS_CFG_PHYS_SZ (1024 * 128) +#define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024) +#define SPIFFS_CFG_PHYS_ADDR (0) +#define SPIFFS_CFG_LOG_PAGE_SZ (256) +#define SPIFFS_CFG_LOG_BLOCK_SZ (4 * 1024) +#define LOG_PAGE_SIZE 256 +#define RDV40_SPIFFS_WORKBUF_SZ (LOG_PAGE_SIZE * 2) +// Experimental : 4 full pages(LOG_PAGE_SIZE + file descript size) of cache for +// Reading and writing if reading cache is stable, writing cache may need more +// testing regarding power loss, page consistency checks, Garbage collector +// Flushing handling... in doubt, use maximal safetylevel as, in most of the +// case, will ensure a flush by rollbacking to previous Unmounted state +#define RDV40_SPIFFS_CACHE_SZ ((LOG_PAGE_SIZE + 32) * 4) +#define SPIFFS_FD_SIZE (32) +#define RDV40_SPIFFS_MAX_FD (2) +#define RDV40_SPIFFS_FDBUF_SZ (SPIFFS_FD_SIZE * RDV40_SPIFFS_MAX_FD) + +#define RDV40_SPIFFS_LAZY_HEADER \ + int changed = 0; \ + if ((level == RDV40_SPIFFS_SAFETY_LAZY) || (level == RDV40_SPIFFS_SAFETY_SAFE)) { \ + changed = rdv40_spiffs_lazy_mount(); \ + } + +#define RDV40_SPIFFS_SAFE_FOOTER \ + if (level == RDV40_SPIFFS_SAFETY_SAFE) { \ + changed = rdv40_spiffs_lazy_mount_rollback(changed); \ + } \ + return changed; + +#define RDV40_SPIFFS_SAFE_FUNCTION(RDV40_SPIFFS_LLFUNCT) \ + RDV40_SPIFFS_LAZY_HEADER \ + RDV40_SPIFFS_LLFUNCT \ + RDV40_SPIFFS_SAFE_FOOTER + +#include "spiffs.h" + +///// FLASH LEVEL R/W/E operations for feeding SPIFFS Driver///////////////// +static s32_t rdv40_spiffs_llread(u32_t addr, u32_t size, u8_t *dst) { + + if (!Flash_ReadData(addr, dst, size)) { + return 128; + } + return SPIFFS_OK; +} + +static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) { + + if (!FlashInit()) { + return 129; + } + Flash_Write(addr, src, size); + return SPIFFS_OK; +} + +static s32_t rdv40_spiffs_llerase(u32_t addr, u32_t size) { + + if (!FlashInit()) { + return 130; + } + + uint32_t bytes_erased = 0, bytes_remaining = size; + while (bytes_remaining > 0) { + + addr += bytes_erased; + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + FlashSendByte(SECTORERASE); + Flash_TransferAdresse(addr); + FlashSendLastByte(0); + + bytes_remaining -= 4096; + bytes_erased += 4096; + } + + Flash_CheckBusy(BUSY_TIMEOUT); + FlashStop(); + + return SPIFFS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +////// SPIFFS LOW LEVEL OPERATIONS ///////////////////////////////////////////// +static u8_t spiffs_work_buf[RDV40_SPIFFS_WORKBUF_SZ]; +static u8_t spiffs_fds[RDV40_SPIFFS_FDBUF_SZ]; +static u8_t spiffs_cache_buf[RDV40_SPIFFS_CACHE_SZ]; + +static spiffs fs; + +static enum spiffs_mount_status { + RDV40_SPIFFS_UNMOUNTED, + RDV40_SPIFFS_MOUNTED, + RDV40_SPIFFS_UNKNOWN +} RDV40_SPIFFS_MOUNT_STATUS; + +int rdv40_spiffs_mounted() { + int ret = 0; + + switch (RDV40_SPIFFS_MOUNT_STATUS) { + case RDV40_SPIFFS_MOUNTED: + ret = 1; + break; + case RDV40_SPIFFS_UNMOUNTED: + case RDV40_SPIFFS_UNKNOWN: + default: + ret = 0; + } + + return ret; +} + +int rdv40_spiffs_mount() { + if (rdv40_spiffs_mounted()) { + Dbprintf("ERR: SPIFFS already mounted !"); + return SPIFFS_ERR_MOUNTED; + } + + spiffs_config cfg; + cfg.hal_read_f = rdv40_spiffs_llread; + cfg.hal_write_f = rdv40_spiffs_llwrite; + cfg.hal_erase_f = rdv40_spiffs_llerase; + + // uncached version + // int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds, + // sizeof(spiffs_fds), 0, 0, 0); cached version, experimental + int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds, sizeof(spiffs_fds), spiffs_cache_buf, + sizeof(spiffs_cache_buf), 0); + if (ret == SPIFFS_OK) { + RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_MOUNTED; + } + return ret; +} + +int rdv40_spiffs_unmount() { + if (!rdv40_spiffs_mounted()) { + Dbprintf("ERR: SPIFFS not mounted !"); + return SPIFFS_ERR_NOT_MOUNTED; + } + + SPIFFS_clearerr(&fs); + SPIFFS_unmount(&fs); + + int ret = SPIFFS_errno(&fs); + if (ret == SPIFFS_OK) { + RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_UNMOUNTED; + } + return ret; +} +//////////////////////////////////////////////////////////////////////////////// + +///// Base RDV40_SPIFFS_SAFETY_NORMAL operations//////////////////////////////// + +void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { + spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); + if (SPIFFS_write(&fs, fd, src, size) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); +} + +void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { + spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_APPEND | SPIFFS_RDWR, 0); + if (SPIFFS_write(&fs, fd, src, size) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); +} + +void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size) { + spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_RDWR, 0); + if (SPIFFS_read(&fs, fd, dst, size) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); +} + +void rename_in_spiffs(const char *old_filename, const char *new_filename) { + if (SPIFFS_rename(&fs, old_filename, new_filename) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); +} + +void remove_from_spiffs(const char *filename) { + if (SPIFFS_remove(&fs, filename) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); +} + +spiffs_stat stat_in_spiffs(const char *filename) { + spiffs_stat s; + if (SPIFFS_stat(&fs, filename, &s) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + return s; +} + +uint32_t size_in_spiffs(const char *filename) { + spiffs_stat s = stat_in_spiffs(filename); + return s.size; +} + +rdv40_spiffs_fsinfo info_of_spiffs() { + rdv40_spiffs_fsinfo fsinfo; + fsinfo.blockSize = SPIFFS_CFG_LOG_BLOCK_SZ; + fsinfo.pageSize = LOG_PAGE_SIZE; + fsinfo.maxOpenFiles = RDV40_SPIFFS_MAX_FD; + fsinfo.maxPathLenght = SPIFFS_OBJ_NAME_LEN; + if (SPIFFS_info(&fs, &fsinfo.totalBytes, &fsinfo.usedBytes) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + fsinfo.freeBytes = fsinfo.totalBytes - fsinfo.usedBytes; + // Rounding without float may be improved + fsinfo.usedPercent = ((100 * fsinfo.usedBytes) + (fsinfo.totalBytes / 2)) / fsinfo.totalBytes; + fsinfo.freePercent = (100 - fsinfo.usedPercent); + return fsinfo; +} + +int exists_in_spiffs(const char *filename) { + spiffs_stat stat; + int rc = SPIFFS_stat(&fs, filename, &stat); + return rc == SPIFFS_OK; +} + +RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) { + RDV40SpiFFSFileType filetype = RDV40_SPIFFS_FILETYPE_UNKNOWN; + char symlinked[SPIFFS_OBJ_NAME_LEN]; + sprintf(symlinked, "%s.lnk", filename); + if (exists_in_spiffs(filename)) { + filetype = RDV40_SPIFFS_FILETYPE_REAL; + } + if (exists_in_spiffs(symlinked)) { + if (filetype != RDV40_SPIFFS_FILETYPE_UNKNOWN) { + filetype = RDV40_SPIFFS_FILETYPE_BOTH; + } else { + filetype = RDV40_SPIFFS_FILETYPE_SYMLINK; + } + } + if (DBGLEVEL > 1) { + switch (filetype) { + case RDV40_SPIFFS_FILETYPE_REAL: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_REAL"); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_SYMLINK"); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_BOTH"); + break; + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_UNKNOWN"); + break; + } + } + return filetype; +} + +int is_valid_filename(const char *filename) { + if (filename == NULL) { + return false; + } + uint32_t len = strlen(filename); + return len > 0 && len < SPIFFS_OBJ_NAME_LEN; +} + +void copy_in_spiffs(const char *src, const char *dst) { + uint32_t size = size_in_spiffs((char *)src); + uint8_t *mem = BigBuf_malloc(size); + read_from_spiffs((char *)src, (uint8_t *)mem, size); + write_to_spiffs((char *)dst, (uint8_t *)mem, size); +} + +//////////////////////////////////////////////////////////////////////////////// + +////// Abstract Operations for base Safetyness ///////////////////////////////// +// +// mount if not already +// As an "hint" to the behavior one should adopt after his or her lazyness +// it will return 0 if the call was a noop, either because it did not need to +// change OR because it wasn't ABLE to change :) +// 1 if the mount status actually changed +// so you know what to do IN CASE you wished to set things "back to previous +// state" +int rdv40_spiffs_lazy_mount() { + int changed = 0; + if (!rdv40_spiffs_mounted()) { + changed = rdv40_spiffs_mount(); + /* if changed = 0 = SPIFFS_OK then all went well then the change + * actually occured :)*/ + changed = !changed; + } + return changed; +} + +// unmount if not already +int rdv40_spiffs_lazy_unmount() { + int changed = 0; + if (rdv40_spiffs_mounted()) { + changed = rdv40_spiffs_unmount(); + changed = !changed; + } + return changed; +} + +// Before further Reading, it is required to have in mind that UNMOUTING is +// important in some ways Because it is the ONLY operation which ensure that +// -> all Caches and writings are flushed to the FS +// -> all FD are properly closed +// -> Every best effort has been done to ensure consistency and integrity of the +// state reputated to be the actual state of the Filesystem. +//--- + +// This will "toggle" mount status +// on "changement" conditional +// so it is for the former lazy_ mounting function to actually rollback or not +// depending on the result of the previous This is super lazy implementation as +// it is either a toggle to previous state or again a noop as everything was a +// nonevent If you have a function which NEEDS mounting but you want to exit +// this function in the very state mounting was before your intervention all +// things can now be transparent like +/* +void my_lazy_spiffs_act(){ + uint8_t changed = rdv40_spiffs_lazy_mount(); + [..] Do what you have to do with spiffs + rdv40_spiffs_lazy_rollback(changed) +} +*/ +// The exact same goes for needed unmouting with eventual rollback, you just +// have to use lazy_unmount insted of lazy mount This way, you can ensure +// consistency in operation even with complex chain of mounting and unmounting +// Lets's say you are in a function which needs to mount if not already, and in +// the middle of itself calls function which indeed will need to unmount if not +// already. Well you better use safe or wrapped function which are made to +// rollback to previous state, so you can continue right after to do your +// things. +// +// As an extreme example: let's imagine that we have a function which is made +// to FORMAT the whole SPIFFS if a SPECIFIC content is written in the 4 first +// byte of that file. Also in such a case it should quickly create a bunch of +// skkeleton to get itself back to a known and wanted state. This behavior has +// to be done at every "manual" (not a lazy or internal event) mounting, just +// like an action upon boot. +/* + void my_spiffs_boot(){ + uint8_t resetret[4]; + // this lazy_mount since needed and can also report back the change on +state implied by eventual mount, if needed rdv40_spiffs_lazy_read((const char +*)".SHOULDRESET",(uint8_t *)resetret,4); if( resetret == "YESS" ) { uint8_t +changed = rdv40_spiffs_lazy_format(); // this will imply change only if we where +already mounted beforehand, was the case after our reading without further +rollback rdv40_spiffs_lazy_mount_rollback(changed); // so if we were mounted +just get back to this state. If not, just don't. + [...] + } + [...] +} +*/ +// Again : This will "toggle" spiffs mount status only if a "change" occured +// (and should be fed by the result of a spiffs_lazy* function) If everything +// went well, it will return SPIFFS_OK if everything went well, and a report +// back the chain a SPI_ERRNO if not. +int rdv40_spiffs_lazy_mount_rollback(int changed) { + if (!changed) + return SPIFFS_OK; + if (rdv40_spiffs_mounted()) + return rdv40_spiffs_unmount(); + return rdv40_spiffs_mount(); +} +/////////////////////////////////////////////////////////////////////////////// + +// High level functions with SatefetyLevel +// Beware that different safety level makes different return behavior +// +// RDV40_SPIFFS_SAFETY_NORMAL : will operate withtout further change on mount +// status RDV40_SPIFFS_SAFETY_LAZY : will ensure mount status already being in +// correct state before ops, +// will return !false if mount state had to change +// RDV40_SPIFFS_SAFETY_SAFE : will do same as RDV40_SPIFFS_SAFETY_LAZY +// will also safely rollback to previous state IF +// mount state had to change will return SPIFFS_OK / +// 0 / false if everything went well + +// TODO : this functions are common enought to be unified with a switchcase +// statement or some function taking function parameters +// TODO : forbid writing to a filename which already exists as lnk ! +// TODO : forbid writing to a filename.lnk which already exists without lnk ! +int rdv40_spiffs_write(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + write_to_spiffs((char *)filename, (uint8_t *)src, size); // + ) +} + +int rdv40_spiffs_append(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + append_to_spiffs((char *)filename, (uint8_t *)src, size); // + ) +} + +// todo integrate reading symlinks transparently +int rdv40_spiffs_read(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + read_from_spiffs((char *)filename, (uint8_t *)dst, size); // + ) +} + +// TODO : forbid writing to a filename which already exists as lnk ! +// TODO : forbid writing to a filename.lnk which already exists without lnk ! +int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + rename_in_spiffs((char *)old_filename, (char *)new_filename); // + ) +} +int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + remove_from_spiffs((char *)filename); // + ) +} + +int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + copy_in_spiffs((char *)src, (char *)dst); // + ) +} + +int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + *buf = size_in_spiffs((char *)filename); // + ) +} + +int rdv40_spiffs_getfsinfo(rdv40_spiffs_fsinfo *fsinfo, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + *fsinfo = info_of_spiffs(); // + ) +} + +// test for symlink from filename +int rdv40_spiffs_is_symlink(const char *s) { + int ret = 0; + + if (s != NULL) { + size_t size = strlen(s); + + if (size >= 4 && s[size - 4] == '.' && s[size - 3] == 'l' && s[size - 2] == 'n' && s[size - 1] == 'k') { + ret = 1; + } + } + + return ret; +} + +// since FILENAME can't be longer than 32Bytes as of hard configuration, we're +// safe with Such maximum. So the "size" variable is actually the known/intended +// size of DESTINATION file, may it be known (may we provide a "stat from +// symlinkk ?") +// ATTENTION : you must NOT provide the whole filename (so please do not include the .lnk extension) +// TODO : integrate in read_function +int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + char linkdest[SPIFFS_OBJ_NAME_LEN]; // + char linkfilename[SPIFFS_OBJ_NAME_LEN]; // + sprintf(linkfilename, "%s.lnk", filename); + if (DBGLEVEL > 1) Dbprintf("Linkk real filename is destination is : %s", linkfilename); + read_from_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("Symlink destination is : %s", linkdest); + read_from_spiffs((char *)linkdest, (uint8_t *)dst, size); // + ) +} + +// BEWARE ! This function is DESTRUCTIVE as it will UPDATE an existing symlink +// Since it creates a .lnk extension file it may be minor to mistake the order of arguments +// Still please use this function with care. +// Also, remind that it will NOT check if destination filename actually exists +// As a mnenotechnic, think about the "ln" unix command, which order is the same as "cp" unix command +// in regard of arguments orders. +// Eg : +// rdv40_spiffs_make_symlink((uint8_t *)"hello", (uint8_t *)"world", RDV40_SPIFFS_SAFETY_SAFE) +// will generate a file named "world.lnk" with the path to file "hello" written in +// wich you can then read back with : +// rdv40_spiffs_read_as_symlink((uint8_t *)"world",(uint8_t *) buffer, orig_file_size, RDV40_SPIFFS_SAFETY_SAFE); +// TODO : FORBID creating a symlink with a basename (before.lnk) which already exists as a file ! +int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + char linkfilename[SPIFFS_OBJ_NAME_LEN]; // + sprintf(linkfilename, "%s.lnk", filename); + write_to_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); // + ) +} + +// filename and filename.lnk will both the existence-checked +// if filename exists, read will be used, if filename.lnk exists, read_as_symlink will be used +// Both existence is not handled right now and should not happen or create a default fallback behavior +// Still, this case won't happend when the write(s) functions will check for both symlink and real file +// preexistance, avoiding a link being created if filename exists, or avoiding a file being created if +// symlink exists with same name +int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); // + switch (filetype) { + case RDV40_SPIFFS_FILETYPE_REAL: + rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + default:; + } // + ) +} + +// TODO regarding reads/write and symlinks : +// Provide a higher level readFile function which +// - don't need a size to be provided, getting it from STAT call and using bigbuff malloc +// - send back the whole readed file as return Result +// Maybe a good think to implement a VFS api here. + +//////////////////////////////////////////////////////////////////////////////// + +///////// MISC HIGH LEVEL FUNCTIONS //////////////////////////////////////////// + +void rdv40_spiffs_safe_print_fsinfos() { + rdv40_spiffs_fsinfo fsinfo; + rdv40_spiffs_getfsinfo(&fsinfo, RDV40_SPIFFS_SAFETY_SAFE); + DbpString(_BLUE_("Flash Memory FileSystem Infos (SPIFFS)")); + Dbprintf("-------------------------------------"); + Dbprintf("* Filesystem Logical Block Size.........%d bytes", fsinfo.blockSize); + Dbprintf("* Filesystem Logical Page Size..........%d bytes", fsinfo.pageSize); + Dbprintf("--"); + Dbprintf("* Filesystem Max Open Files.............%d file descriptors", fsinfo.maxOpenFiles); + Dbprintf("* Filesystem Max Path Lenght............%d chars", fsinfo.maxPathLenght); + Dbprintf("--"); + Dbprintf("Filesystem\tSize\tUsed\tAvailable\tUse%\tMounted on"); + Dbprintf("spiffs\t%dB\t%dB\t%dB\t\t%d%\t/", fsinfo.totalBytes, fsinfo.usedBytes, fsinfo.freeBytes, + fsinfo.usedPercent); +} + +// this function is safe and WILL rollback since it is only a PRINTING function, +// not a function intended to give any sort of struct to manipulate the FS +// objects +// TODO : Fake the Directory availability by spliting strings , buffering, +// maintaining prefix list sorting, unique_checking, THEN outputing precomputed +// tree Other solutio nwould be to add directory support to SPIFFS, but that we +// dont want, as prefix are way easier and lighter in every aspect. +void rdv40_spiffs_safe_print_tree(uint8_t banner) { + + int changed = rdv40_spiffs_lazy_mount(); + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + if (banner) { + DbpString(_BLUE_("Flash Memory FileSystem tree (SPIFFS)")); + Dbprintf("-------------------------------------"); + } + SPIFFS_opendir(&fs, "/", &d); + Dbprintf(" \t \t/"); + while ((pe = SPIFFS_readdir(&d, pe))) { + + char resolvedlink[11 + SPIFFS_OBJ_NAME_LEN]; + if (rdv40_spiffs_is_symlink((const char *)pe->name)) { + char linkdest[SPIFFS_OBJ_NAME_LEN]; + read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); + sprintf(resolvedlink, "(.lnk) --> %s", linkdest); + // Kind of stripping the .lnk extension + strtok((char *)pe->name, "."); + } else { + memset(resolvedlink, 0, sizeof(resolvedlink)); + } + + Dbprintf("[%04x]\t %ibytes \t|-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); + } + SPIFFS_closedir(&d); + + rdv40_spiffs_lazy_mount_rollback(changed); +} + +void test_spiffs() { + Dbprintf("---------------------------"); + Dbprintf("Testing SPIFFS functionning"); + Dbprintf("---------------------------"); + Dbprintf("(all test are made using lazy safetylevel)"); + Dbprintf("* Mounting filesystem (lazy)......."); + int changed = rdv40_spiffs_lazy_mount(); + Dbprintf("* Printing tree.............."); + rdv40_spiffs_safe_print_tree(false); + Dbprintf("* Writing 'I love Proxmark' in a testspiffs.txt"); + // Since We lazy_mounted manually before hand, the wrte safety level will + // just imply noops + rdv40_spiffs_write((char *)"testspiffs.txt", (uint8_t *)"I love Proxmark", 15, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("* Printing tree again......."); + rdv40_spiffs_safe_print_tree(false); + Dbprintf("* Making a symlink to testspiffs.txt"); + rdv40_spiffs_make_symlink((char *)"testspiffs.txt", (char *)"linktotestspiffs.txt", RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("* Printing tree again......."); + rdv40_spiffs_safe_print_tree(false); + // TODO READBACK, rename,print tree read back, remove, print tree; + Dbprintf("* Rollbacking The mount status IF things have changed"); + rdv40_spiffs_lazy_mount_rollback(changed); + Dbprintf("All done"); + return; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h new file mode 100644 index 000000000..e0bbbe68c --- /dev/null +++ b/armsrc/spiffs.h @@ -0,0 +1,861 @@ +/* + * 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" + +typedef enum spiffs_safety_level { RDV40_SPIFFS_SAFETY_NORMAL, RDV40_SPIFFS_SAFETY_LAZY, RDV40_SPIFFS_SAFETY_SAFE } RDV40SpiFFSSafetyLevel; + +typedef enum spiffs_file_type { + RDV40_SPIFFS_FILETYPE_REAL, + RDV40_SPIFFS_FILETYPE_SYMLINK, + RDV40_SPIFFS_FILETYPE_BOTH, + RDV40_SPIFFS_FILETYPE_UNKNOWN +} RDV40SpiFFSFileType; + +typedef struct rdv40_spiffs_fsinfo + { + uint32_t blockSize; + uint32_t pageSize; + uint32_t maxOpenFiles; + uint32_t maxPathLenght; + uint32_t totalBytes, usedBytes, freeBytes; + uint32_t usedPercent, freePercent; + } rdv40_spiffs_fsinfo; + +int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); + + +int rdv40_spiffs_lazy_unmount(); +int rdv40_spiffs_lazy_mount(); +int rdv40_spiffs_lazy_mount_rollback(int changed); +int rdv40_spiffs_write(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_read(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); +void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size); +void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size); +void test_spiffs(); +void rdv40_spiffs_safe_print_tree(uint8_t banner); +int rdv40_spiffs_unmount(); +int rdv40_spiffs_mount(); +int rdv40_spiffs_is_symlink(const char *s); +void rdv40_spiffs_safe_print_fsinfos(); +int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level); +void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size); +int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_append(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level); +uint32_t size_in_spiffs(const char *filename); + +#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_SEEK_BOUNDS -10040 + + +#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_ */ diff --git a/armsrc/spiffs_cache.c b/armsrc/spiffs_cache.c new file mode 100644 index 000000000..ad9995eae --- /dev/null +++ b/armsrc/spiffs_cache.c @@ -0,0 +1,319 @@ +/* + * 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<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->ucache.spix.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<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { + u8_t *mem = spiffs_get_cache_page(fs, cache, ix); + SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->ucache.spix.pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + +#if SPIFFS_CACHE_WR + 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 +#endif + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + } + cache->cpage_use_map &= ~(1 << ix); + cp->flags = 0; + } + + 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<cpage_use_map |= (1<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); + _SPIFFS_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->ucache.spix.pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix); + + 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); + _SPIFFS_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); + _SPIFFS_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<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->ucache.swrc.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->ucache.swrc.obj_id = fd->obj_id; + fd->cache_page = cp; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); + 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->ucache.swrc.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; + _SPIFFS_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 diff --git a/armsrc/spiffs_check.c b/armsrc/spiffs_check.c new file mode 100644 index 000000000..2b3b3a14d --- /dev/null +++ b/armsrc/spiffs_check.c @@ -0,0 +1,1008 @@ +/* + * 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; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~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(flags), &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",NULL); + } + } + } + } + } 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",NULL); + } 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",NULL); + 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",NULL); + 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",NULL); + 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",NULL); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~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(flags), &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 diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h new file mode 100644 index 000000000..5e214380b --- /dev/null +++ b/armsrc/spiffs_config.h @@ -0,0 +1,388 @@ +/* + * 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 +//#include +// +#include "printf.h" +#include "string.h" +#include "flashmem.h" + +void Dbprintf(const char *fmt, ...); + +//#include +//#include +// ----------- >8 ------------ + + + typedef int s32_t; + typedef uint32_t u32_t; + typedef int16_t s16_t; + typedef uint16_t u16_t; + typedef int8_t s8_t; + typedef uint8_t u8_t; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_DBGF(str) SPIFFS_DBG(str,NULL) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(_f, ...) +#define SPIFFS_GC_DBGF(str) SPIFFS_GC_DBG(str,NULL) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_CACHE_DBGF(str) SPIFFS_CACHE_DBG(str,NULL) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(_f, ...) //SPIFFS_CHECK_DBG(_f, ## __VA_ARGS__) +#define SPIFFS_CHECK_DBGF(str) SPIFFS_CHECK_DBG(str,NULL) +#endif +// Set spiffs debug output call for all api invocations. +#ifndef SPIFFS_API_DBG +#define SPIFFS_API_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_API_DBGF(str) SPIFFS_API_DBG(str,NULL) +#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 (32) +#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 (0) +#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 (0) +#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 (0) +#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) +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#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 (1) +#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*128) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) +#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) (4*1024) +#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 4 +#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 + +// By default SPIFFS in some cases relies on the property of NOR flash that bits +// cannot be set from 0 to 1 by writing and that controllers will ignore such +// bit changes. This results in fewer reads as SPIFFS can in some cases perform +// blind writes, with all bits set to 1 and only those it needs reset set to 0. +// Most of the chips and controllers allow this behavior, so the default is to +// use this technique. If your controller is one of the rare ones that don't, +// turn this option on and SPIFFS will perform a read-modify-write instead. +#ifndef SPIFFS_NO_BLIND_WRITES +#define SPIFFS_NO_BLIND_WRITES 0 +#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(...) Dbprintf(__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_ */ diff --git a/armsrc/spiffs_gc.c b/armsrc/spiffs_gc.c new file mode 100644 index 000000000..9da80e375 --- /dev/null +++ b/armsrc/spiffs_gc.c @@ -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_DBGF("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_DBGF("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_DBGF("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_DBGF("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 diff --git a/armsrc/spiffs_hydrogen.c b/armsrc/spiffs_hydrogen.c new file mode 100644 index 000000000..adc107270 --- /dev/null +++ b/armsrc/spiffs_hydrogen.c @@ -0,0 +1,1452 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "printf.h" + +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif + +#if SPIFFS_BUFFER_HELP +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { + return num_descs * sizeof(spiffs_fd); +} +#if SPIFFS_CACHE +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); +} +#endif +#endif + +u8_t SPIFFS_mounted(spiffs *fs) { + return SPIFFS_CHECK_MOUNT(fs); +} + +s32_t SPIFFS_format(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_probe_fs(spiffs_config *config) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = spiffs_probe(config); + return res; +} + +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +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) { + SPIFFS_API_DBG("%s " + " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi + " addr:"_SPIPRIad + " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi + "\n", + __func__, + SPIFFS_CFG_PHYS_SZ(fs), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + SPIFFS_CFG_LOG_BLOCK_SZ(fs), + SPIFFS_CFG_PHYS_ERASE_SZ(fs), + SPIFFS_CFG_PHYS_ADDR(fs), + fd_space_size, cache_size); + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void*); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); + if (addr_lsb) { + fd_space += (ptr_size-addr_lsb); + fd_space_size -= (ptr_size-addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); + if (addr_lsb) { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size-addr_lsb); + cache = cache_8; + cache_size -= (ptr_size-addr_lsb); + } + if (cache_size & (ptr_size-1)) { + cache_size -= (cache_size & (ptr_size-1)); + } + +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res; + +#if SPIFFS_USE_MAGIC + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + fs->config_magic = SPIFFS_CONFIG_MAGIC; + + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); + + fs->check_cb_f = check_cb_f; + + fs->mounted = 1; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +void SPIFFS_unmount(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + 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) { +#if SPIFFS_CACHE + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); +#endif + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->mounted = 0; + + SPIFFS_UNLOCK(fs); +} + +s32_t SPIFFS_errno(spiffs *fs) { + return fs->err_code; +} + +void SPIFFS_clearerr(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); + fs->err_code = SPIFFS_OK; +} + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; (void)path; (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags); + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + +#if SPIFFS_READ_ONLY + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); +#endif // SPIFFS_READ_ONLY + + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { +#if !SPIFFS_READ_ONLY + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; +#endif // !SPIFFS_READ_ONLY + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; +} + + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} +#endif // !SPIFFS_READ_ONLY + +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->flags & SPIFFS_O_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size); + } +#endif + } + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->ucache.swrc.offset || // writing before cache + offset > fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size || // writing after cache + offset + len > fd->cache_page->ucache.swrc.offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->ucache.swrc.offset = offset; + fd->cache_page->ucache.swrc.size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->ucache.swrc.offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); +#ifdef _SPIFFS_TEST + { + intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache; + intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache; + intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + if (__a1 > __b || __a2 > __b) { + printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); + ERREXIT(); + } + } +#endif + _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->ucache.swrc.size = MAX(fd->cache_page->ucache.swrc.size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset+offs; + break; + case SPIFFS_SEEK_END: + offs = file_size + offs; + break; + } + if (offs < 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); + } + if (offs > file_size) { + fd->fdoffset = file_size; + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return offs; +} + +s32_t SPIFFS_remove(spiffs *fs, const char *path) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0,0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_cache_fd_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + + return res; +} + +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_page_ix pix; + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_stat_pix(fs, pix, 0, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +// Checks if there are any cached writes for the object id associated with +// given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + if (res < SPIFFS_OK) { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } +#endif + + return res; +} +#endif + +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs,res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); +#if SPIFFS_CACHE + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { + SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); +#if SPIFFS_READ_ONLY + (void)fs; (void)old_path; (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + } else if (res == SPIFFS_OK) { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, + 0, 0, &pix_dummy); +#if SPIFFS_TEMPORAL_FD_CACHE + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } +#endif + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_OBJ_META_LEN +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)name; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} +#endif // SPIFFS_OBJ_META_LEN + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); + (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; +} + +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } else { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; +} + +s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; +} + +s32_t SPIFFS_check(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_lookup_consistency_check(fs, 0); + + res = spiffs_object_index_consistency_check(fs); + + res = spiffs_page_consistency_check(fs); + + res = spiffs_obj_lu_scan(fs); + + SPIFFS_UNLOCK(fs); + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) { + *total = total_data_pages * data_page_size; + } + + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages); +#if SPIFFS_READ_ONLY + (void)fs; (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_quick(fs, max_free_pages); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size); +#if SPIFFS_READ_ONLY + (void)fs; (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_check(fs, size); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; +} + +#if SPIFFS_IX_MAP + +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) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); +} + +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); +} + +#endif // SPIFFS_IX_MAP + +#if SPIFFS_TEST_VISUALISATION +s32_t SPIFFS_vis(spiffs *fs) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + 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; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + 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 (cur_entry == 0) { + spiffs_printf(_SPIPRIbl" ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id)-1) { + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); + } else { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); + return res; +} +#endif diff --git a/armsrc/spiffs_nucleus.c b/armsrc/spiffs_nucleus.c new file mode 100644 index 000000000..1c913921b --- /dev/null +++ b/armsrc/spiffs_nucleus.c @@ -0,0 +1,2365 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "printf.h" + +static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); +#endif + return res; +} + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); +#endif + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_CACHE + +s32_t spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) { + return SPIFFS_HAL_READ(fs, addr, len, dst); +} + +s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) { + return SPIFFS_HAL_WRITE(fs, addr, len, src); +} + +#endif + +#if !SPIFFS_READ_ONLY +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) { + (void)fh; + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id with visitor. +// Iterate over object lookup pages in each block until a given object id entry is found. +// When found, the visitor function is called with block index, entry index and user data. +// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be +// ended and visitor's return code is returned to caller. +// If no visitor is given (0) the search returns on first entry with matching object id. +// If no match is found in all look up, SPIFFS_VIS_END is returned. +// @param fs the file system +// @param starting_block the starting block to start search in +// @param starting_lu_entry the look up index entry to start search in +// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, +// SPIFFS_VIS_NO_WRAP +// @param obj_id argument object id +// @param v visitor callback function +// @param user_const_p any const pointer, passed to the callback visitor function +// @param user_var_p any pointer, passed to the callback visitor function +// @param block_ix reported block index where match was found +// @param lu_entry reported look up index where match was found +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 res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_lu_entry; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // 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 && // for non-last obj lookup pages + cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page + { + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (lu_entry) *lu_entry = cur_entry; + if (v) { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], + cur_block, + cur_entry, + user_const_p, + user_var_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + 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); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } else { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + return SPIFFS_VIS_END; +} + +#if !SPIFFS_READ_ONLY +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_USE_MAGIC + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); +#endif + + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +s32_t spiffs_probe( + spiffs_config *cfg) { + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; + + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; + + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); +#if SPIFFS_HAL_CALLBACK_EXTRA + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#else + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#endif + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } + + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1]+1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; + } + + return SPIFFS_ERR_PROBE_NOT_A_FS; +} +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + + +static s32_t spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)bix; + (void)user_const_p; + (void)user_var_p; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; + } + + return SPIFFS_VIS_COUNTINUE; +} + + +// Scans thru all obj lu and counts free, deleted and used pages +// Find the maximum block erase count +// Checks magic if enabled +s32_t spiffs_obj_lu_scan( + spiffs *fs) { + s32_t res; + spiffs_block_ix bix; + int entry; +#if SPIFFS_USE_MAGIC + spiffs_block_ix unerased_bix = (spiffs_block_ix)-1; +#endif + + // find out erase count + // if enabled, check magic + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) { +#if SPIFFS_USE_MAGIC + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&magic); + + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs, bix)) { + if (unerased_bix == (spiffs_block_ix)-1) { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } else { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } +#endif + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; + } + + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { + // clean system, set counter to zero + erase_count_final = 0; + } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) { + // wrap, take min + erase_count_final = erase_count_min+1; + } else { + erase_count_final = erase_count_max+1; + } + + fs->max_erase_count = erase_count_final; + +#if SPIFFS_USE_MAGIC + if (unerased_bix != (spiffs_block_ix)-1) { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); +#if SPIFFS_READ_ONLY + res = SPIFFS_ERR_RO_ABORTED_OPERATION; +#else + res = spiffs_erase_block(fs, unerased_bix); +#endif // SPIFFS_READ_ONLY + SPIFFS_CHECK_RES(res); + } +#endif + + // count blocks + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + SPIFFS_CHECK_RES(res); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Find free object lookup entry +// Iterate over object lookup pages in each block until a free object id entry is found +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 res; + if (!fs->cleaning && fs->free_blocks < 2) { + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) { + return SPIFFS_ERR_FULL; + } + } + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; + if (*lu_entry == 0) { + fs->free_blocks--; + } + } + if (res == SPIFFS_ERR_FULL) { + SPIFFS_DBGF("fs full\n"); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id +// Iterate over object lookup pages in each block until a given object id entry is found +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 res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; +} + + +static s32_t spiffs_obj_lu_find_id_and_span_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == *((spiffs_span_ix*)user_var_p) && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) { + return SPIFFS_OK; + } else { + return SPIFFS_VIS_COUNTINUE; + } +} + +// Find object lookup entry containing given id and span index +// Iterate over object lookup pages in each block until a given object id entry is found +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 res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Find object lookup entry containing given id and span index in page headers only +// Iterate over object lookup pages in each block until a given object id entry is found +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 res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if SPIFFS_IX_MAP + +// update index map of given fd with given object index data +static void spiffs_update_ix_map(spiffs *fs, + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { +#if SPIFFS_SINGLETON + (void)fs; +#endif + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { + return; + } + + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } else { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix)-1) { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; + } +} + +typedef struct { + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; +} spiffs_ix_map_populate_state; + +static s32_t spiffs_populate_ix_map_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); + + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; +} + +// populates index map, from vector entry start to vector entry end, inclusive +s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); + vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); + if (vec_entry_start > vec_entry_end) { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; + + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + return res; +} + +#endif + + +#if !SPIFFS_READ_ONLY +// Allocates a free defined page with given obj_id +// Occupies object lookup entry and page +// data may be NULL; where only page header is stored, len and page_offs is ignored +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 res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // 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); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. +// If page data is null, provided header is used for metainfo and page data is physically copied. +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 res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // 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); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + 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), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + 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); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Deletes a page and removes it from object lookup. +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) { + s32_t res; + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Create an object index header page with empty index and undefined length +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 res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + 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); + SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// update object index header with any combination of name/size/index +// new_objix_hdr_data may be null, if so the object index header page is loaded +// name may be null, if so name is not changed +// size may be null, if so size is not changed +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) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) { + strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); + } +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id_raw, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) { +#if SPIFFS_IX_MAP == 0 + (void)objix; +#endif + // update index caches in all file descriptors + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]){"UPD", "NEW", "DEL", "MOV", "HUP","???"}[MIN(ev,5)], + obj_id_raw, spix, new_pix, new_size); + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file +#if !SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->file_nbr == 0) continue; // fd closed +#endif + if (spix == 0) { // object index header update + if (ev != SPIFFS_EV_IX_DEL) { +#if SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->score == 0) continue; // never used fd +#endif + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", + SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + // update size and offsets for fds to this file + cur_fd->size = new_size; + u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; +#if SPIFFS_CACHE_WR + if (act_new_size > 0 && cur_fd->cache_page) { + act_new_size = MAX(act_new_size, cur_fd->cache_page->ucache.swrc.offset + cur_fd->cache_page->ucache.swrc.size); + } +#endif + if (cur_fd->offset > act_new_size) { + cur_fd->offset = act_new_size; + } + if (cur_fd->fdoffset > act_new_size) { + cur_fd->fdoffset = act_new_size; + } +#if SPIFFS_CACHE_WR + if (cur_fd->cache_page && cur_fd->cache_page->ucache.swrc.offset > act_new_size+1) { + SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif + } + } else { + // removing file +#if SPIFFS_CACHE_WR + if (cur_fd->file_nbr && cur_fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif + SPIFFS_DBG(" callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } // object index header update + if (cur_fd->cursor_objix_spix == spix) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } + } + } // fd update loop + +#if SPIFFS_IX_MAP + + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } + } + +#endif + + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) { + op = SPIFFS_CB_CREATED; + } else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) { + op = SPIFFS_CB_UPDATED; + } else if (ev == SPIFFS_EV_IX_DEL) { + op = SPIFFS_CB_DELETED; + } else { + SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); + } +} + +// Open object by id +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; + + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + + return res; +} + +// Open object by page index +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + (void)mode; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); + + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; + + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Append to object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); + + if (offset > fd->size) { + SPIFFS_DBGF("append: offset reversed to size\n"); + offset = fd->size; + } + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) { + SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); + } + SPIFFS_CHECK_RES(res); + + 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_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset+written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + offset+written, new_objix_hdr_page, 0, written); + } + fd->size = offset+written; + fd->offset = offset+written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + fd->size = offset+written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); + } else { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); + } + + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset+written; + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset+written; + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id + , offset+written, new_objix_hdr_page, 0, written, res2); + SPIFFS_CHECK_RES(res2); + } else { + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset+written; + SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; +} // spiffs_object_append +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Modify object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + 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_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); + } else { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; + + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) break; + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; +} // spiffs_object_modify +#endif // !SPIFFS_READ_ONLY + +static s32_t spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_var_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { + return SPIFFS_OK; + } + } + + return SPIFFS_VIS_COUNTINUE; +} + +// Finds object index header page by name +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 res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + name, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if !SPIFFS_READ_ONLY +// Truncates object to new size. If new size is null, object may be removed totally +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove_full) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { + // no op + return res; + } + + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); + SPIFFS_CHECK_RES(res); + } + + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + 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_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove_full && new_size == 0) { + u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + SPIFFS_CHECK_RES(res); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix)-1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) { + SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } + + SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); + + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { + SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); + break; + } + + if (res == SPIFFS_OK) { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) { + SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); + break; + } + } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { + res = SPIFFS_OK; + } + + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) { + // update object index header page + if (cur_size == 0) { + if (remove_full) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBGF("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; +} // spiffs_object_truncate +#endif // !SPIFFS_READ_ONLY + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + 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; + + while (cur_offset < offset + len) { +#if SPIFFS_IX_MAP + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; + } else { +#endif + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + objix_pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } +#endif + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size - cur_offset); + SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); + if (len_to_read <= 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; +} + +#if !SPIFFS_READ_ONLY +typedef struct { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; + const u8_t *conflicting_name; +} spiffs_free_obj_id_state; + +static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { + spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); + const u8_t *conflicting_name = (const u8_t*)user_const_p; + + // if conflicting name parameter is given, also check if this name is found in object index hdrs + if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + int res; + spiffs_page_object_ix_header objix_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + } + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t bit_ix = (id-min_obj_id) & 7; + int byte_ix = (id-min_obj_id) >> 3; + if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1<conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + if (id >= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } + } + return SPIFFS_VIS_COUNTINUE; +} + +// Scans thru all object lookup for object index header pages. If total possible number of +// object ids cannot fit into a work buffer, these are grouped. When a group containing free +// object ids is found, the object lu is again scanned for object ids within group and bitmasked. +// Finally, the bitmask is searched for a free id +s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { + s32_t res = SPIFFS_OK; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + state.conflicting_name = conflicting_name; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { + // possible to represent in bitmap + u32_t i, j; + SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1<work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBGF("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_TEMPORAL_FD_CACHE +// djb2 hash +static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { + hash = (hash * 33) ^ c; + } + return hash; +} +#endif + +s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { +#if SPIFFS_TEMPORAL_FD_CACHE + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t)-1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + + if (name) { + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (cur_fd->score > 1) { // score == 0 indicates never used fd + cur_fd->score--; + } + } + } + } + + // find the free fd with least score or name match + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (name && cur_fd->name_hash == name_hash) { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t)-1) { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } else { + cur_fd->score = 0xffff; + } + } else { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix+1; + *fd = cur_fd; + return SPIFFS_OK; + } else { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } +#else + (void)name; + 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->file_nbr = i+1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; +#endif +} + +s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f-1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; +#if SPIFFS_IX_MAP + fd->ix_map = 0; +#endif + return SPIFFS_OK; +} + +s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f-1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; +} + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path) { + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + 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->score > 0 && cur_fd->name_hash == old_hash) { + cur_fd->name_hash = new_hash; + } + } +} +#endif diff --git a/armsrc/spiffs_nucleus.h b/armsrc/spiffs_nucleus.h new file mode 100644 index 000000000..255c5aa08 --- /dev/null +++ b/armsrc/spiffs_nucleus.h @@ -0,0 +1,842 @@ +/* + * 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 defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) + /* For GCC, clang and TI compilers */ +#define SPIFFS_PACKED __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +#define SPIFFS_PACKED + +#else + /* Unknown compiler */ +#define SPIFFS_PACKED +#endif + + + +#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))) ) + +#if SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) +#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) +#else +#define SPIFFS_FH_OFFS(fs, fh) (fh) +#define SPIFFS_FH_UNOFFS(fs, fh) (fh) +#endif + + +#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 spix { + // read cache page index + spiffs_page_ix pix; + } spix; +#if SPIFFS_CACHE_WR + // type write cache + struct swrc { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + } swrc; +#endif + } ucache; +} 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 (cached) + 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 SPIFFS_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 SPIFFS_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 SPIFFS_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); + +// memcpy macro, +// checked in test builds, otherwise plain memcpy (unless already defined) +#ifdef _SPIFFS_TEST +#define _SPIFFS_MEMCPY(__d, __s, __l) do { \ + intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \ + intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \ + intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \ + intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \ + if (__a1 <= __b2 && __b1 <= __a2) { \ + printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \ + ERREXIT(); \ + } \ + memcpy((__d),(__s),(__l)); \ +} while (0) +#else +#ifndef _SPIFFS_MEMCPY +#define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0) +#endif +#endif //_SPIFFS_TEST + +#endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/armsrc/string.c b/armsrc/string.c index 5c154643c..02392ec40 100644 --- a/armsrc/string.c +++ b/armsrc/string.c @@ -106,3 +106,101 @@ void itoa(int n, char s[]) { } //////////////////////////////////////// END 'itoa' CODE + + + +char *strcpy(char *dst, const char *src) + { + char *save = dst; + + for (; (*dst = *src) != '\0'; ++src, ++dst); + return save; + } + +char *strncpy(char *dst, const char *src, size_t n) + { + if (n != 0) { + char *d = dst; + const char *s = src; + + do { + if ((*d++ = *s++) == 0) { + /* NUL pad the remaining n-1 bytes */ + while (--n) { + *d++ = 0; + } + break; + } + } while (--n); + } + return dst; + } + +int strcmp(const char *s1, const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == 0) { + return (0); + } + } + return (*(unsigned char *) s1 - *(unsigned char *) --s2); +} + +char* __strtok_r(char*, const char*, char**); + +char* __strtok_r(char* s, const char* delim, char** last) +{ + char *spanp, *tok; + int c, sc; + + if(s == NULL && (s = *last) == NULL) + return (NULL); + +/* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for(spanp = (char*)delim; (sc = *spanp++) != 0;) + { + if(c == sc) + goto cont; + } + + if(c == 0) + { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for(;;) + { + c = *s++; + spanp = (char*)delim; + do + { + if((sc = *spanp++) == c) + { + if(c == 0) + s = NULL; + else + s[-1] = '\0'; + *last = s; + return (tok); + } + } while(sc != 0); + } + /* NOTREACHED */ +} + +char* strtok(char* s, const char* delim) +{ + static char* last; + + return (__strtok_r(s, delim, &last)); +} diff --git a/armsrc/string.h b/armsrc/string.h index f6f094c48..9a899cfc7 100644 --- a/armsrc/string.h +++ b/armsrc/string.h @@ -23,5 +23,9 @@ char *strncat(char *dest, const char *src, unsigned int n); char *strcat(char *dest, const char *src); void strreverse(char s[]); void itoa(int n, char s[]); +char *strcpy(char *dst, const char *src); +char* strncpy(char* destination, const char* source, size_t num); +int strcmp(const char *s1, const char *s2); +char* strtok(char* s, const char* delim); #endif /* __STRING_H */ diff --git a/client/Makefile b/client/Makefile index a1109a497..440f2b888 100644 --- a/client/Makefile +++ b/client/Makefile @@ -227,6 +227,7 @@ CMDSRCS = crapto1/crapto1.c \ cmdlfvisa2000.c \ cmdtrace.c \ cmdflashmem.c \ + cmdflashmemspiffs.c \ cmdsmartcard.c \ cmdusart.c \ cmdparser.c \ diff --git a/client/cmddata.c b/client/cmddata.c index ba6790c93..8a220a39d 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -865,7 +865,7 @@ static int CmdBitsamples(const char *Cmd) { int cnt = 0; uint8_t got[12288]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -1397,7 +1397,7 @@ static int CmdHexsamples(const char *Cmd) { return PM3_EINVARG; } - if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, requested, offset,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ESOFT; } @@ -1470,7 +1470,7 @@ int getSamples(uint32_t n, bool silent) { if (!silent) PrintAndLogEx(NORMAL, "Reading %d bytes from device memory\n", n); PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, got, n, 0, &response, 10000, true)) { + if (!GetFromDevice(BIG_BUF, got, n, 0,NULL,0, &response, 10000, true)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } diff --git a/client/cmdflashmem.c b/client/cmdflashmem.c index 9546b421f..a3a957552 100644 --- a/client/cmdflashmem.c +++ b/client/cmdflashmem.c @@ -315,7 +315,7 @@ static int CmdFlashMemDump(const char *Cmd) { } PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")"bytes from flashmem", len); - if (!GetFromDevice(FLASH_MEM, dump, len, start_index, NULL, -1, true)) { + if (!GetFromDevice(FLASH_MEM, dump, len, start_index,NULL,0, NULL, -1, true)) { PrintAndLogEx(FAILED, "ERROR; downloading from flashmemory"); free(dump); return PM3_EFLASH; @@ -574,6 +574,7 @@ static int CmdFlashMemInfo(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "High level SPI FileSystem Flash manipulation [rdv40]"}, {"spibaud", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate [rdv40]"}, {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information [rdv40]"}, {"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory [rdv40]"}, diff --git a/client/cmdflashmem.h b/client/cmdflashmem.h index 790d42a5d..b08164837 100644 --- a/client/cmdflashmem.h +++ b/client/cmdflashmem.h @@ -24,6 +24,7 @@ #include "util_posix.h" // msclock #include "loclass/fileutils.h" //saveFile #include "comms.h" //getfromdevice +#include "cmdflashmemspiffs.h" // spiffs commands typedef enum { DICTIONARY_NONE = 0, diff --git a/client/cmdflashmemspiffs.c b/client/cmdflashmemspiffs.c new file mode 100644 index 000000000..c6ae55211 --- /dev/null +++ b/client/cmdflashmemspiffs.c @@ -0,0 +1,457 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Flash memory commands +//----------------------------------------------------------------------------- +#include "cmdflashmemspiffs.h" + +#include "mbedtls/base64.h" +#include "mbedtls/rsa.h" +#include "mbedtls/sha1.h" + +static int CmdHelp(const char *Cmd); + +static int CmdFlashMemSpiFFSMount(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSUnmount(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTest(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_TEST, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTree(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSInfo(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0); + return PM3_SUCCESS; +} + +static int usage_flashmemspiffs_remove(void) { + PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem"); + PrintAndLogEx(NORMAL, " Usage: mem spiffs remove "); + return PM3_SUCCESS; +} + +static int usage_flashmemspiffs_rename(void) { + PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem"); + PrintAndLogEx(NORMAL, " Usage: mem spiffs rename "); + return PM3_SUCCESS; +} + +static int usage_flashmemspiffs_copy(void) { + PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem"); + PrintAndLogEx(NORMAL, " Usage: mem spiffs copy "); + return PM3_SUCCESS; +} + +static int usage_flashmemspiffs_dump(void) { + PrintAndLogEx(NORMAL, "Dumps flash memory on device into a file or in console"); + PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence"); + PrintAndLogEx(NORMAL, " Usage: mem spiffs dump o [f [e]] [p]"); + PrintAndLogEx(NORMAL, " o : filename in SPIFFS"); + PrintAndLogEx(NORMAL, " f : file name to save to"); + PrintAndLogEx(NORMAL, " p : print dump in console"); + PrintAndLogEx(NORMAL, " e : also save in EML format (good for tags save and dictonnary files)"); + PrintAndLogEx(NORMAL, " You must specify at lease option f or option p, both if you wish"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " mem spiffs dump o hf_colin/lasttag f lasttag e"); + PrintAndLogEx(NORMAL, " mem spiffs dump o hf_colin/lasttag p"); + return PM3_SUCCESS; +} + +static int usage_flashmemspiffs_load(void) { + PrintAndLogEx(NORMAL, "Uploads binary-wise file into device filesystem"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); + PrintAndLogEx(NORMAL, "Warning: mem area to be written must have been wiped first"); + PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)"); + PrintAndLogEx(NORMAL, " o : destination filename"); + PrintAndLogEx(NORMAL, " f : local filename"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " mem spiffs load f myfile o myapp.conf"); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRemove(const char *Cmd) { + + char filename[32] = {0}; + bool errors = false; + + /*char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') { + return usage_flashmemspiffs_remove(); + }*/ + + if (param_getstr(Cmd, 0, filename, 32) >= 32) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + } + + // check null filename ? + if (errors) { + usage_flashmemspiffs_remove(); + return PM3_EINVARG; + } + + SendCommandMIX(CMD_SPIFFS_REMOVE, 0, 0, 0, (uint8_t *)filename, 32); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRename(const char *Cmd) { + + char srcfilename[32] = {0}; + char destfilename[32] = {0}; + bool errors = false; + + /*char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') { + return usage_flashmemspiffs_rename(); + }*/ + + if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (srcfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (destfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + // check null filename ? + if (errors) { + usage_flashmemspiffs_rename(); + return PM3_EINVARG; + } + + char data[65]; + sprintf(data, "%s,%s", srcfilename, destfilename); + SendCommandMIX(CMD_SPIFFS_RENAME, 0, 0, 0, (uint8_t *)data, 65); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCopy(const char *Cmd) { + + char srcfilename[32] = {0}; + char destfilename[32] = {0}; + bool errors = false; + + /*char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') { + return usage_flashmemspiffs_copy(); + }*/ + + if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (srcfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (destfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + // check null filename ? + if (errors) { + usage_flashmemspiffs_copy(); + return PM3_EINVARG; + } + + char data[65]; + sprintf(data, "%s,%s", srcfilename, destfilename); + SendCommandMIX(CMD_SPIFFS_COPY, 0, 0, 0, (uint8_t *)data, 65); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSDump(const char *Cmd) { + + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = false; + bool print = false; + uint32_t start_index = 0, len = FLASH_MEM_MAX_SIZE; + char destfilename[32] = {0}; + bool eml = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_flashmemspiffs_dump(); + /*case 'l': + len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); + cmdp += 2; + break;*/ + case 'o': + param_getstr(Cmd, cmdp + 1, destfilename, 32); + cmdp += 2; + break; + case 'p': + print = true; + cmdp += 1; + break; + case 'e': + eml = true; + cmdp += 1; + break; + case 'f': + // File handling + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + if ((filename[0] == '\0') && (!print)) { + PrintAndLogEx(FAILED, "No print asked and Local dump Filename missing or invalid"); + errors = true; + } + + if (destfilenamse[0] == '\0') { + PrintAndLogEx(FAILED, "SPIFFS Filename missing or invalid"); + errors = true; + } + + // Validations + if (errors || cmdp == 0) { + usage_flashmemspiffs_dump(); + return PM3_EINVARG; + } + + // get size from spiffs itself ! + SendCommandMIX(CMD_SPIFFS_STAT, 0, 0, 0, (uint8_t *)destfilename, 32); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + len = resp.oldarg[0]; + + uint8_t *dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogDevice(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") "bytes from spiffs (flashmem)", len); + if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)destfilename, 32, NULL, -1, true)) { + PrintAndLogEx(FAILED, "ERROR; downloading from spiffs(flashmemory)"); + free(dump); + return PM3_EFLASH; + } + + if (print) { + print_hex_break(dump, len, 32); + } + + if (filename[0] != '\0') { + saveFile(filename, "", dump, len); + if (eml) { + saveFileEML(filename, dump, len, 16); + } + } + + free(dump); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSLoad(const char *Cmd) { + + uint32_t append = 0; + char filename[FILE_PATH_SIZE] = {0}; + char destfilename[32] = {0}; + bool errors = false; + uint8_t cmdp = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_flashmemspiffs_load(); + case 'f': + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + case 'o': + param_getstr(Cmd, cmdp + 1, destfilename, 32); + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + if (destfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Destination Filename missing or invalid"); + errors = true; + } + + // Validations + if (errors || cmdp == 0) { + usage_flashmemspiffs_load(); + return PM3_EINVARG; + } + + size_t datalen = 0; + int res = 0; + uint8_t *data = calloc(FLASH_MEM_MAX_SIZE, sizeof(uint8_t)); + + res = loadFile(filename, "", data, FLASH_MEM_MAX_SIZE, &datalen); + // int res = loadFileEML( filename, data, &datalen); + if (res) { + free(data); + return PM3_EFILE; + } + + if (datalen > FLASH_MEM_MAX_SIZE) { + PrintAndLogDevice(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + + uint8_t *newdata = realloc(data, datalen); + if (newdata == NULL) { + free(data); + return PM3_EMALLOC; + } else { + data = newdata; + } + + // We want to mount before multiple operation so the lazy writes/append will not + // trigger a mount + umount each loop iteration (lazy ops device side) + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + + // Send to device + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = datalen; + + // fast push mode + conn.block_after_ACK = true; + + // SendCommandMIX(CMD_SPIFFS_COPY, 0, 0, 0, (uint8_t *)data, 65); + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + clearCommandBuffer(); + + char fdata[32 + bytes_in_packet]; + memset(fdata, 0, sizeof(fdata)); + memcpy(fdata, destfilename, 32); + memcpy(fdata + 32, data + bytes_sent, bytes_in_packet); + // sprintf(fdata, "%s%s", destfilename, data + bytes_sent); + + if (bytes_sent > 0) + append = 1; + + SendCommandOLD(CMD_SPIFFS_WRITE, append, bytes_in_packet, 0, fdata, 32 + bytes_in_packet); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + conn.block_after_ACK = false; + free(data); + return PM3_ETIMEOUT; + } + + uint8_t isok = resp.oldarg[0] & 0xFF; + if (!isok) { + conn.block_after_ACK = false; + PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); + return PM3_EFLASH; + } + } + + conn.block_after_ACK = false; + free(data); + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u") "bytes to file "_GREEN_("%s"), datalen, destfilename); + + // We want to unmount after these to set things back to normal but more than this + // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, + "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, + {"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, + {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, + {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Functionning (require wiping pages 0 and 1)"}, + {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, + {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, + {NULL, NULL, NULL, NULL}}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdFlashMemSpiFFS(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/cmdflashmemspiffs.h b/client/cmdflashmemspiffs.h new file mode 100644 index 000000000..b3589ce86 --- /dev/null +++ b/client/cmdflashmemspiffs.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Flash memory commands +//----------------------------------------------------------------------------- + +#ifndef CMDFLASHMEMSPIFFS_H__ +#define CMDFLASHMEMSPIFFS_H__ + +#include +#include +#include +#include +#include "pmflash.h" +#include "common.h" +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "util.h" +#include "util_posix.h" // msclock +#include "loclass/fileutils.h" //saveFile +#include "comms.h" //getfromdevice + +int CmdFlashMemSpiFFS(const char *Cmd); + +#endif diff --git a/client/cmdhffelica.c b/client/cmdhffelica.c index 91d171149..3e6001ee1 100644 --- a/client/cmdhffelica.c +++ b/client/cmdhffelica.c @@ -383,7 +383,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { return 1; } - if (!GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, trace, tracelen, 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(trace); return 0; diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index f8d57556f..96b9a0ee5 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -990,7 +990,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { } // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocksRead * 8, startindex, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocksRead * 8, startindex,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return 0; } @@ -1033,7 +1033,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { blocksRead = (sizeof(tag_data) - gotBytes) / 8; } // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocksRead * 8, startindex, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocksRead * 8, startindex,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return 0; } diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index 03c5ecfcd..031f5c95a 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -781,7 +781,7 @@ int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uin PrintAndLogEx(WARNING, "Fail, only managed to read %u bytes", *outlen); // copy data from device - if (!GetFromDevice(BIG_BUF_EML, out, *outlen, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, out, *outlen, 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); return 4; } @@ -930,7 +930,7 @@ static int CmdLegicDump(const char *Cmd) { PrintAndLogEx(WARNING, "Fail, only managed to read 0x%02X bytes of 0x%02X", readlen, dumplen); // copy data from device - if (!GetFromDevice(BIG_BUF_EML, data, readlen, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, data, readlen, 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(data); return 4; @@ -1200,7 +1200,7 @@ static int CmdLegicESave(const char *Cmd) { // download emulator memory PrintAndLogEx(SUCCESS, "Reading emulator memory..."); - if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(data); return 4; diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index e75224cd2..c227b1a83 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2693,7 +2693,7 @@ static int CmdHF14AMfESave(const char *Cmd) { memset(dump, 0, bytes); PrintAndLogEx(INFO, "downloading from emulator memory"); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 91e223427..935c178f2 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -1931,7 +1931,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { bufferSize = sizeof(data); } - if (!GetFromDevice(BIG_BUF, data, bufferSize, startindex, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, data, bufferSize, startindex,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return 1; } diff --git a/client/cmdlfcotag.c b/client/cmdlfcotag.c index 0f5e7a835..fa6ff01a8 100644 --- a/client/cmdlfcotag.c +++ b/client/cmdlfcotag.c @@ -91,7 +91,7 @@ static int CmdCOTAGRead(const char *Cmd) { } case 1: { - if (!GetFromDevice(BIG_BUF, DemodBuffer, COTAG_BITS, 0, NULL, 1000, false)) { + if (!GetFromDevice(BIG_BUF, DemodBuffer, COTAG_BITS, 0,NULL,0, NULL, 1000, false)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index aaf8c0db4..b594eb4bc 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -986,7 +986,7 @@ static bool downloadSamplesEM() { // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) uint8_t got[6000]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return false; } diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 9a23e660b..1af8defe4 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -1804,7 +1804,7 @@ static int CmdResetRead(const char *Cmd) { } uint8_t got[BIGBUF_SIZE - 1]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } diff --git a/client/cmdtrace.c b/client/cmdtrace.c index 914bf06d6..e9a609184 100644 --- a/client/cmdtrace.c +++ b/client/cmdtrace.c @@ -759,7 +759,7 @@ int CmdTraceList(const char *Cmd) { if (isOnline) { // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, trace, PM3_CMD_DATA_SIZE, 0, &response, 4000, true)) { + if (!GetFromDevice(BIG_BUF, trace, PM3_CMD_DATA_SIZE, 0,NULL,0, &response, 4000, true)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return 1; } @@ -773,7 +773,7 @@ int CmdTraceList(const char *Cmd) { return 2; } trace = p; - if (!GetFromDevice(BIG_BUF, trace, traceLen, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, trace, traceLen, 0,NULL,0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(trace); return 3; diff --git a/client/comms.c b/client/comms.c index 304948a4d..45771c60d 100644 --- a/client/comms.c +++ b/client/comms.c @@ -739,7 +739,7 @@ bool WaitForResponse(uint32_t cmd, PacketResponseNG *response) { * @param show_warning display message after 2 seconds * @return true if command was returned, otherwise false */ -bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, PacketResponseNG *response, size_t ms_timeout, bool show_warning) { +bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, uint8_t *data, uint32_t datalen, PacketResponseNG *response, size_t ms_timeout, bool show_warning) { if (dest == NULL) return false; if (bytes == 0) return true; @@ -760,6 +760,10 @@ bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint3 SendCommandMIX(CMD_DOWNLOAD_EML_BIGBUF, start_index, bytes, 0, NULL, 0); return dl_it(dest, bytes, start_index, response, ms_timeout, show_warning, CMD_DOWNLOADED_EML_BIGBUF); } + case SPIFFS: { + SendCommandMIX(CMD_SPIFFS_DOWNLOAD, start_index, bytes, 0, data, datalen); + return dl_it(dest, bytes, start_index, response, ms_timeout, show_warning, CMD_SPIFFS_DOWNLOADED); + } case FLASH_MEM: { SendCommandMIX(CMD_FLASHMEM_DOWNLOAD, start_index, bytes, 0, NULL, 0); return dl_it(dest, bytes, start_index, response, ms_timeout, show_warning, CMD_FLASHMEM_DOWNLOADED); diff --git a/client/comms.h b/client/comms.h index 6ec14331d..bc036f008 100644 --- a/client/comms.h +++ b/client/comms.h @@ -37,6 +37,7 @@ typedef enum { BIG_BUF_EML, FLASH_MEM, SIM_MEM, + SPIFFS } DeviceMemType_t; typedef struct { @@ -72,7 +73,8 @@ bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms bool WaitForResponseTimeout(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout); bool WaitForResponse(uint32_t cmd, PacketResponseNG *response); -bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, PacketResponseNG *response, size_t ms_timeout, bool show_warning); +//bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, PacketResponseNG *response, size_t ms_timeout, bool show_warning); +bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, uint8_t *data, uint32_t datalen, PacketResponseNG *response, size_t ms_timeout, bool show_warning); #endif diff --git a/client/scripting.c b/client/scripting.c index 572899e2d..9e735b0db 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -223,7 +223,7 @@ static int l_GetFromBigBuf(lua_State *L) { return returnToLuaWithError(L, "Allocating memory failed"); } - if (!GetFromDevice(BIG_BUF, data, len, startindex, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, data, len, startindex,NULL,0, NULL, 2500, false)) { free(data); return returnToLuaWithError(L, "command execution time out"); } @@ -263,7 +263,7 @@ static int l_GetFromFlashMem(lua_State *L) { if (!data) return returnToLuaWithError(L, "Allocating memory failed"); - if (!GetFromDevice(FLASH_MEM, data, len, startindex, NULL, -1, false)) { + if (!GetFromDevice(FLASH_MEM, data, len, startindex,NULL,0, NULL, -1, false)) { free(data); return returnToLuaWithError(L, "command execution time out"); } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 39edb2b61..99a858ff0 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -238,6 +238,40 @@ typedef struct { #define CMD_FLASHMEM_INFO 0x0125 #define CMD_FLASHMEM_SET_SPIBAUDRATE 0x0126 +// RDV40, High level flashmem SPIFFS Manipulation +// ALL function will have a lazy or Safe version +// that will be handled as argument of safety level [0..2] respectiveley normal / lazy / safe +// However as how design is, MOUNT and UNMOUNT only need/have lazy as safest level so a safe level will still execute a lazy version +// see spiffs.c for more about the normal/lazy/safety information) +#define CMD_SPIFFS_MOUNT 0x0130 +#define CMD_SPIFFS_UNMOUNT 0x0131 +#define CMD_SPIFFS_WRITE 0x0132 +// We take +0x1000 when having a variant of similar function (todo : make it an argument!) +#define CMD_SPIFFS_APPEND 0x1132 + +#define CMD_SPIFFS_READ 0x0133 +//We use no open/close instruvtion, as they are handled internally. +#define CMD_SPIFFS_REMOVE 0x0134 +#define CMD_SPIFFS_RM CMD_SPIFFS_REMOVE +#define CMD_SPIFFS_RENAME 0x0135 +#define CMD_SPIFFS_MV CMD_SPIFFS_RENAME +#define CMD_SPIFFS_COPY 0x0136 +#define CMD_SPIFFS_CP CMD_SPIFFS_COPY +#define CMD_SPIFFS_STAT 0x0137 +#define CMD_SPIFFS_FSTAT 0x0138 +#define CMD_SPIFFS_INFO 0x0139 +#define CMD_SPIFFS_FORMAT CMD_FLASHMEM_WIPE +// This take a +0x2000 as they are high level helper and special functions +// As the others, they may have safety level argument if it makkes sense +#define CMD_SPIFFS_PRINT_TREE 0x2130 +#define CMD_SPIFFS_GET_TREE 0x2131 +#define CMD_SPIFFS_TEST 0x2132 +#define CMD_SPIFFS_PRINT_FSINFO 0x2133 +#define CMD_SPIFFS_DOWNLOAD 0x2134 +#define CMD_SPIFFS_DOWNLOADED 0x2135 +// more ? + + // RDV40, Smart card operations #define CMD_SMART_RAW 0x0140 #define CMD_SMART_UPGRADE 0x0141 From 70cd0d844b4578a0e92e3a8989a3aec1f4be6127 Mon Sep 17 00:00:00 2001 From: cjbrigato Date: Tue, 23 Jul 2019 07:39:34 +0200 Subject: [PATCH 32/40] FIX : Tereifying typo --- client/cmdflashmemspiffs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdflashmemspiffs.c b/client/cmdflashmemspiffs.c index c6ae55211..e06f492a0 100644 --- a/client/cmdflashmemspiffs.c +++ b/client/cmdflashmemspiffs.c @@ -255,7 +255,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { errors = true; } - if (destfilenamse[0] == '\0') { + if (destfilename[0] == '\0') { PrintAndLogEx(FAILED, "SPIFFS Filename missing or invalid"); errors = true; } From 9beabaabf528f5e39e399e86d9b9f4610135d268 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 21:33:52 +0200 Subject: [PATCH 33/40] make style --- armsrc/Standalone/hf_colin.c | 12 +- armsrc/appmain.c | 110 +- armsrc/spiffs.c | 71 +- armsrc/spiffs.h | 249 ++- armsrc/spiffs_cache.c | 422 ++-- armsrc/spiffs_check.c | 1694 ++++++++-------- armsrc/spiffs_config.h | 12 +- armsrc/spiffs_gc.c | 988 +++++----- armsrc/spiffs_hydrogen.c | 2012 +++++++++---------- armsrc/spiffs_nucleus.c | 3537 +++++++++++++++++----------------- armsrc/spiffs_nucleus.h | 167 +- armsrc/string.c | 135 +- armsrc/string.h | 4 +- client/cmddata.c | 6 +- client/cmdflashmem.c | 2 +- client/cmdflashmemspiffs.c | 99 +- client/cmdhffelica.c | 2 +- client/cmdhficlass.c | 4 +- client/cmdhflegic.c | 6 +- client/cmdhfmf.c | 2 +- client/cmdhfmfu.c | 2 +- client/cmdlfcotag.c | 2 +- client/cmdlfem4x.c | 2 +- client/cmdlft55xx.c | 2 +- client/cmdtrace.c | 4 +- client/comms.c | 4 +- client/flash.c | 4 +- client/scripting.c | 4 +- include/pm3_cmd.h | 4 +- 29 files changed, 4784 insertions(+), 4778 deletions(-) diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 2189f1146..bd184fad6 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -102,7 +102,7 @@ void ReadLastTagFromFlash() { uint8_t *mem = BigBuf_malloc(size); //this one will handle filetype (symlink or not) and resolving by itself - rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK,(uint8_t *)mem,len, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK, (uint8_t *)mem, len, RDV40_SPIFFS_SAFETY_SAFE); emlSetMem(mem, 0, 64); @@ -126,14 +126,14 @@ void WriteTagToFlash(uint32_t uid, size_t size) { char dest[SPIFFS_OBJ_NAME_LEN]; uint8_t buid[4]; - num_to_bytes(uid,4,buid); - sprintf(dest,"hf_colin/mf_%02x%02x%02x%02x.bin",buid[0],buid[1],buid[2],buid[3]); - + num_to_bytes(uid, 4, buid); + sprintf(dest, "hf_colin/mf_%02x%02x%02x%02x.bin", buid[0], buid[1], buid[2], buid[3]); + // TODO : by using safe function for multiple writes we are both breaking cache mecanisms and making useless and unoptimized mount operations // we should manage at out level the mount status before and after the whole standalone mode - rdv40_spiffs_write((char *)dest,(uint8_t *)data,len, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_write((char *)dest, (uint8_t *)data, len, RDV40_SPIFFS_SAFETY_SAFE); // lastag will only contain filename/path to last written tag file so we don't loose time or space. - rdv40_spiffs_make_symlink((char *)dest,(char *)HFCOLIN_LASTTAG_SYMLINK,RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_make_symlink((char *)dest, (char *)HFCOLIN_LASTTAG_SYMLINK, RDV40_SPIFFS_SAFETY_SAFE); DbprintfEx(FLAG_NEWLINE, "[OK] TAG WRITTEN TO FLASH !"); cjSetCursLeft(); diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 59174153f..7128c4a2f 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1591,13 +1591,13 @@ static void PacketReceived(PacketCommandNG *packet) { } #ifdef WITH_FLASH case CMD_SPIFFS_TEST: { - test_spiffs(); - break; + test_spiffs(); + break; } case CMD_SPIFFS_MOUNT: { - rdv40_spiffs_lazy_mount(); - break; - } + rdv40_spiffs_lazy_mount(); + break; + } case CMD_SPIFFS_UNMOUNT: { rdv40_spiffs_lazy_unmount(); break; @@ -1606,24 +1606,24 @@ static void PacketReceived(PacketCommandNG *packet) { rdv40_spiffs_safe_print_tree(false); break; } - case CMD_SPIFFS_PRINT_FSINFO: { - rdv40_spiffs_safe_print_fsinfos(); - break; - } + case CMD_SPIFFS_PRINT_FSINFO: { + rdv40_spiffs_safe_print_fsinfos(); + break; + } case CMD_SPIFFS_DOWNLOAD: { LED_B_ON(); uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; - memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs dump : %s", filename); - - //uint32_t size = 0; + memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs dump : %s", filename); + + //uint32_t size = 0; //rdv40_spiffs_stat((char *)filename, (uint32_t *)size,RDV40_SPIFFS_SAFETY_SAFE); uint32_t size = packet->oldarg[1]; //uint8_t buff[size]; uint8_t *buff = BigBuf_malloc(size); - rdv40_spiffs_read_as_filetype((char *)filename,(uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); // arg0 = filename // arg1 = size @@ -1640,59 +1640,59 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } - case CMD_SPIFFS_STAT:{ + case CMD_SPIFFS_STAT: { LED_B_ON(); uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; - memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs STAT : %s", filename); + memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs STAT : %s", filename); int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)filename); if (changed) rdv40_spiffs_lazy_unmount(); - reply_old(CMD_ACK, size, 0, 0, 0, 0); + reply_old(CMD_ACK, size, 0, 0, 0, 0); LED_B_OFF(); break; } - case CMD_SPIFFS_REMOVE:{ - LED_B_ON(); - uint8_t filename[32]; - uint8_t *pfilename = packet->data.asBytes; - memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs REMOVE : %s", filename); - rdv40_spiffs_remove((char *) filename,RDV40_SPIFFS_SAFETY_SAFE); - LED_B_OFF(); - break; - } - case CMD_SPIFFS_RENAME:{ + case CMD_SPIFFS_REMOVE: { LED_B_ON(); - uint8_t srcfilename[32]; - uint8_t destfilename[32]; + uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; - char *token; - token = strtok((char *)pfilename, ","); - strcpy((char*)srcfilename,token); - token = strtok(NULL,","); - strcpy((char *)destfilename,token); - if (DBGLEVEL > 1) Dbprintf("> Filename received as source for spiffs RENAME : %s", srcfilename); - if (DBGLEVEL > 1) Dbprintf("> Filename received as destination for spiffs RENAME : %s", destfilename); - rdv40_spiffs_rename((char *) srcfilename,(char *)destfilename,RDV40_SPIFFS_SAFETY_SAFE); - LED_B_OFF(); + memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs REMOVE : %s", filename); + rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); break; } - case CMD_SPIFFS_COPY:{ + case CMD_SPIFFS_RENAME: { LED_B_ON(); uint8_t srcfilename[32]; uint8_t destfilename[32]; uint8_t *pfilename = packet->data.asBytes; char *token; token = strtok((char *)pfilename, ","); - strcpy((char*)srcfilename,token); - token = strtok(NULL,","); - strcpy((char *)destfilename,token); + strcpy((char *)srcfilename, token); + token = strtok(NULL, ","); + strcpy((char *)destfilename, token); + if (DBGLEVEL > 1) Dbprintf("> Filename received as source for spiffs RENAME : %s", srcfilename); + if (DBGLEVEL > 1) Dbprintf("> Filename received as destination for spiffs RENAME : %s", destfilename); + rdv40_spiffs_rename((char *) srcfilename, (char *)destfilename, RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_COPY: { + LED_B_ON(); + uint8_t srcfilename[32]; + uint8_t destfilename[32]; + uint8_t *pfilename = packet->data.asBytes; + char *token; + token = strtok((char *)pfilename, ","); + strcpy((char *)srcfilename, token); + token = strtok(NULL, ","); + strcpy((char *)destfilename, token); if (DBGLEVEL > 1) Dbprintf("> Filename received as source for spiffs COPY : %s", srcfilename); if (DBGLEVEL > 1) Dbprintf("> Filename received as destination for spiffs COPY : %s", destfilename); - rdv40_spiffs_copy((char *) srcfilename,(char *)destfilename,RDV40_SPIFFS_SAFETY_SAFE); - LED_B_OFF(); + rdv40_spiffs_copy((char *) srcfilename, (char *)destfilename, RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); break; } case CMD_SPIFFS_WRITE: { @@ -1702,18 +1702,18 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t size = packet->oldarg[1]; uint8_t *data = packet->data.asBytes; - //rdv40_spiffs_lazy_mount(); + //rdv40_spiffs_lazy_mount(); uint8_t *pfilename = packet->data.asBytes; - memcpy(filename,pfilename,SPIFFS_OBJ_NAME_LEN); - data+=SPIFFS_OBJ_NAME_LEN; + memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); + data += SPIFFS_OBJ_NAME_LEN; - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); - if (!append) { - rdv40_spiffs_write((char *) filename,(uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); - } else { - rdv40_spiffs_append((char *) filename,(uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); - } + if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); + if (!append) { + rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_append((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + } reply_old(CMD_ACK, 1, 0, 0, 0, 0); LED_B_OFF(); break; diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index b5db53c42..0fecc556c 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -107,13 +107,13 @@ int rdv40_spiffs_mounted() { int ret = 0; switch (RDV40_SPIFFS_MOUNT_STATUS) { - case RDV40_SPIFFS_MOUNTED: - ret = 1; - break; - case RDV40_SPIFFS_UNMOUNTED: - case RDV40_SPIFFS_UNKNOWN: - default: - ret = 0; + case RDV40_SPIFFS_MOUNTED: + ret = 1; + break; + case RDV40_SPIFFS_UNMOUNTED: + case RDV40_SPIFFS_UNKNOWN: + default: + ret = 0; } return ret; @@ -240,18 +240,18 @@ RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) { } if (DBGLEVEL > 1) { switch (filetype) { - case RDV40_SPIFFS_FILETYPE_REAL: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_REAL"); - break; - case RDV40_SPIFFS_FILETYPE_SYMLINK: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_SYMLINK"); - break; - case RDV40_SPIFFS_FILETYPE_BOTH: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_BOTH"); - break; - case RDV40_SPIFFS_FILETYPE_UNKNOWN: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_UNKNOWN"); - break; + case RDV40_SPIFFS_FILETYPE_REAL: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_REAL"); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_SYMLINK"); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_BOTH"); + break; + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_UNKNOWN"); + break; } } return filetype; @@ -426,13 +426,13 @@ int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level) { int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - *buf = size_in_spiffs((char *)filename); // + *buf = size_in_spiffs((char *)filename); // ) } int rdv40_spiffs_getfsinfo(rdv40_spiffs_fsinfo *fsinfo, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - *fsinfo = info_of_spiffs(); // + *fsinfo = info_of_spiffs(); // ) } @@ -465,9 +465,9 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD if (DBGLEVEL > 1) Dbprintf("Linkk real filename is destination is : %s", linkfilename); read_from_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); if (DBGLEVEL > 1) Dbprintf("Symlink destination is : %s", linkdest); - read_from_spiffs((char *)linkdest, (uint8_t *)dst, size); // - ) -} + read_from_spiffs((char *)linkdest, (uint8_t *)dst, size); // + ) + } // BEWARE ! This function is DESTRUCTIVE as it will UPDATE an existing symlink // Since it creates a .lnk extension file it may be minor to mistake the order of arguments @@ -498,17 +498,18 @@ int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyL int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); // - switch (filetype) { - case RDV40_SPIFFS_FILETYPE_REAL: - rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); - break; - case RDV40_SPIFFS_FILETYPE_SYMLINK: - rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); - break; - case RDV40_SPIFFS_FILETYPE_BOTH: - case RDV40_SPIFFS_FILETYPE_UNKNOWN: - default:; - } // + switch (filetype) { + case RDV40_SPIFFS_FILETYPE_REAL: + rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + default: + ; + } // ) } diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index e0bbbe68c..b9308f4dd 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -22,15 +22,14 @@ typedef enum spiffs_file_type { RDV40_SPIFFS_FILETYPE_UNKNOWN } RDV40SpiFFSFileType; -typedef struct rdv40_spiffs_fsinfo - { - uint32_t blockSize; - uint32_t pageSize; - uint32_t maxOpenFiles; - uint32_t maxPathLenght; - uint32_t totalBytes, usedBytes, freeBytes; - uint32_t usedPercent, freePercent; - } rdv40_spiffs_fsinfo; +typedef struct rdv40_spiffs_fsinfo { + uint32_t blockSize; + uint32_t pageSize; + uint32_t maxOpenFiles; + uint32_t maxPathLenght; + uint32_t totalBytes, usedBytes, freeBytes; + uint32_t usedPercent, freePercent; +} rdv40_spiffs_fsinfo; int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); @@ -145,39 +144,39 @@ typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); /* file system check callback report operation */ typedef enum { - SPIFFS_CHECK_LOOKUP = 0, - SPIFFS_CHECK_INDEX, - SPIFFS_CHECK_PAGE + 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_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); + 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); + 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 + /* 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 */ @@ -243,141 +242,141 @@ typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, // 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; + // 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; + // 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; + // 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; + // 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; + // 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; + // 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; + // 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; + // 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; + // 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; + u32_t stats_gc_runs; #endif #if SPIFFS_CACHE - // cache memory - void *cache; - // cache size - u32_t cache_size; + // cache memory + void *cache; + // cache size + u32_t cache_size; #if SPIFFS_CACHE_STATS - u32_t cache_hits; - u32_t cache_misses; + 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; + // 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]; + 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]; + 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; + 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]; + u8_t meta[SPIFFS_OBJ_META_LEN]; #endif }; typedef struct { - spiffs *fs; - spiffs_block_ix block; - int entry; + 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; + // 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 @@ -434,9 +433,9 @@ s32_t SPIFFS_probe_fs(spiffs_config *config); * @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); + 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 @@ -779,7 +778,7 @@ s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); * 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); + u32_t offset, u32_t len, spiffs_page_ix *map_buf); /** * Unmaps the index lookup from this filehandle. All future readings will diff --git a/armsrc/spiffs_cache.c b/armsrc/spiffs_cache.c index ad9995eae..f86133be4 100644 --- a/armsrc/spiffs_cache.c +++ b/armsrc/spiffs_cache.c @@ -12,109 +12,109 @@ // 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<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && - cp->ucache.spix.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 *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->ucache.spix.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; + //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<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && - (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { - u8_t *mem = spiffs_get_cache_page(fs, cache, ix); - SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); - res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->ucache.spix.pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); - } + 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); + SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->ucache.spix.pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } #if SPIFFS_CACHE_WR - 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 + 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 #endif - { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + } + cache->cpage_use_map &= ~(1 << ix); + cp->flags = 0; } - cache->cpage_use_map &= ~(1 << ix); - cp->flags = 0; - } - return res; + 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); + 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 ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { + // at least one free cpage + return SPIFFS_OK; } - } - if (cand_ix >= 0) { - res = spiffs_cache_page_free(fs, cand_ix, 1); - } + // 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; + } + } - return res; + 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<cpage_use_map |= (1<last_access = cache->last_access; - //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i); - return cp; + spiffs_cache *cache = spiffs_get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) { + // out of cache memory + return 0; } - } - // out of cache entries - 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); - } + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) { + spiffs_cache_page_free(fs, cp->ix, 0); + } } // ------------------------------ @@ -127,57 +127,57 @@ s32_t spiffs_phys_rd( 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); - _SPIFFS_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); + (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) { - cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; - cp->ucache.spix.pix = SPIFFS_PADDR_TO_PAGE(fs, addr); - SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix); - - 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); - _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + // 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); + _SPIFFS_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; - } + 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->ucache.spix.pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix); + + 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); + _SPIFFS_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; + return res; } // writes to spi flash and/or the cache @@ -188,132 +188,132 @@ s32_t spiffs_phys_wr( 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); + (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 (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); - } + 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); - _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); - cache->last_access++; - cp->last_access = cache->last_access; + 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); + 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 { - return SPIFFS_OK; + // no cache page, no write cache - just write thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); } - } 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); + 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<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && - cp->ucache.swrc.obj_id == fd->obj_id) { - return cp; + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { + // all cpages free, no cpage cannot be assigned to obj_id + return 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) && + cp->ucache.swrc.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; - } + // 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->ucache.swrc.obj_id = fd->obj_id; - fd->cache_page = cp; - SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); - return cp; + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->ucache.swrc.obj_id = fd->obj_id; + fd->cache_page = cp; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); + 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; + 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); + spiffs_cache_page_free(fs, cp->ix, 0); - cp->ucache.swrc.obj_id = 0; + cp->ucache.swrc.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; + 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; - } + 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)); + 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; - _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); - spiffs_cache *c = spiffs_get_cache(fs); + spiffs_cache *c = spiffs_get_cache(fs); - memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(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; - } + 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 diff --git a/armsrc/spiffs_check.c b/armsrc/spiffs_check.c index 2b3b3a14d..90fba8a41 100644 --- a/armsrc/spiffs_check.c +++ b/armsrc/spiffs_check.c @@ -44,466 +44,465 @@ // 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; + 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); + // 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); + // 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); - } + // 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); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); - return res; + 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; + 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; + 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); + // 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); + // 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; + } + // 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; - } + // 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; - } + // 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); + 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; + 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_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; #if SPIFFS_NO_BLIND_WRITES - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), - sizeof(flags), &flags); - SPIFFS_CHECK_RES(res); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); #endif - flags &= ~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(flags), &flags); - return res; + flags &= ~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(flags), &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",NULL); - } - } - } - } - } 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",NULL); - } 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",NULL); - 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",NULL); - 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; + 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; - } else { - SPIFFS_CHECK_RES(res); - if (ref_pix != cur_pix) { - SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n",NULL); - 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 { - // page referenced by object index but not final - // just finalize - SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n",NULL); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); - u8_t flags = 0xff; -#if SPIFFS_NO_BLIND_WRITES - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), - sizeof(flags), &flags); - SPIFFS_CHECK_RES(res); -#endif - flags &= ~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(flags), &flags); + // 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); - 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); - } + 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", NULL); + } + } + } + } + } 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); - return 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", NULL); + } 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", NULL); + 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", NULL); + 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", NULL); + 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", NULL); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~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(flags), &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); + 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); + 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); + // 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; + 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); + 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; + 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; + (void)check_all_objects; + s32_t res = SPIFFS_OK; - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + 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); + 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_VIS_END) { + res = SPIFFS_OK; + } - if (res != SPIFFS_OK) { - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); - } + 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); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); - return res; + return res; } //--------------------------------------- @@ -520,355 +519,354 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { // * 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; + 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; + 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)); + // 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); + 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; + // 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, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_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 - } + + 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)); } - } - } 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 ((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++; } - - 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 - } + // next block + cur_block++; } - } - } + // check consistency bitmap + if (!restart) { + spiffs_page_ix objix_pix; + spiffs_page_ix rpix; - 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; + 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; + 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; } //--------------------------------------- @@ -877,106 +875,106 @@ s32_t spiffs_page_consistency_check(spiffs *fs) { // 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; + 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; + 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; + 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); + 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); + 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); + // 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); - } - } // span index - } // valid object index id - return res_c; + 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. @@ -984,25 +982,25 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o // 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; + 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 diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h index 5e214380b..99557f41f 100644 --- a/armsrc/spiffs_config.h +++ b/armsrc/spiffs_config.h @@ -25,12 +25,12 @@ void Dbprintf(const char *fmt, ...); // ----------- >8 ------------ - typedef int s32_t; - typedef uint32_t u32_t; - typedef int16_t s16_t; - typedef uint16_t u16_t; - typedef int8_t s8_t; - typedef uint8_t u8_t; +typedef int s32_t; +typedef uint32_t u32_t; +typedef int16_t s16_t; +typedef uint16_t u16_t; +typedef int8_t s8_t; +typedef uint8_t u8_t; // compile time switches diff --git a/armsrc/spiffs_gc.c b/armsrc/spiffs_gc.c index 9da80e375..76ff094c9 100644 --- a/armsrc/spiffs_gc.c +++ b/armsrc/spiffs_gc.c @@ -9,21 +9,21 @@ static s32_t spiffs_gc_erase_block( spiffs *fs, spiffs_block_ix bix) { - s32_t res; + s32_t res; - SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); - res = spiffs_erase_block(fs, bix); - SPIFFS_CHECK_RES(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); + { + 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; + return res; } // Searches for blocks where all entries are deleted - if one is found, @@ -31,77 +31,77 @@ static s32_t spiffs_gc_erase_block( // 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; + 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_DBGF("gc_quick: running\n"); + SPIFFS_GC_DBGF("gc_quick: running\n"); #if SPIFFS_GC_STATS - fs->stats_gc_runs++; + fs->stats_gc_runs++; #endif - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + 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; + // 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; + 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++; - } // 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; } - - 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; + return res; } // Checks if garbage collecting is necessary. If so a candidate block is found, @@ -109,127 +109,127 @@ s32_t spiffs_gc_quick( 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; + 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; - } + 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); + 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_DBGF("gc_check: no candidates, return\n"); - return (s32_t)needed_pages < free_pages ? SPIFFS_OK : 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_DBGF("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++; + 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); + 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_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); - res = spiffs_gc_erase_block(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_DBGF("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 (prev_free_pages <= 0 && prev_free_pages == free_pages) { - // abort early to reduce wear, at least tried once - SPIFFS_GC_DBGF("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; - } + 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); + 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; + 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; + 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; + // 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 @@ -238,127 +238,127 @@ s32_t spiffs_gc_find_candidate( 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; + 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)); + // 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)); + // 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)); + // 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; + *block_candidates = cand_blocks; - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + 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; + // 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++; + 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++; - } // 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); + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block - 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; + return res; } typedef enum { - FIND_OBJ_DATA, - MOVE_OBJ_DATA, - MOVE_OBJ_IX, - FINISHED + 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_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 @@ -375,232 +375,232 @@ typedef struct { // 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; + 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); + SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); - memset(&gc, 0, sizeof(spiffs_gc)); - gc.state = FIND_OBJ_DATA; + 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_DBGF("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); - } + 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); } - 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 + + 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_DBGF("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; + return res; } #endif // !SPIFFS_READ_ONLY diff --git a/armsrc/spiffs_hydrogen.c b/armsrc/spiffs_hydrogen.c index adc107270..dee72fb5f 100644 --- a/armsrc/spiffs_hydrogen.c +++ b/armsrc/spiffs_hydrogen.c @@ -15,1007 +15,1019 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); #if SPIFFS_BUFFER_HELP u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { - return num_descs * sizeof(spiffs_fd); + return num_descs * sizeof(spiffs_fd); } #if SPIFFS_CACHE u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { - return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); } #endif #endif u8_t SPIFFS_mounted(spiffs *fs) { - return SPIFFS_CHECK_MOUNT(fs); + return SPIFFS_CHECK_MOUNT(fs); } s32_t SPIFFS_format(spiffs *fs) { #if SPIFFS_READ_ONLY - (void)fs; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - if (SPIFFS_CHECK_MOUNT(fs)) { - fs->err_code = SPIFFS_ERR_MOUNTED; - return -1; - } - - s32_t res; - SPIFFS_LOCK(fs); - - spiffs_block_ix bix = 0; - while (bix < fs->block_count) { - fs->max_erase_count = 0; - res = spiffs_erase_block(fs, bix); - if (res != SPIFFS_OK) { - res = SPIFFS_ERR_ERASE_FAIL; + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - bix++; - } - SPIFFS_UNLOCK(fs); + s32_t res; + SPIFFS_LOCK(fs); - return 0; + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; #endif // SPIFFS_READ_ONLY } #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 s32_t SPIFFS_probe_fs(spiffs_config *config) { - SPIFFS_API_DBG("%s\n", __func__); - s32_t res = spiffs_probe(config); - return res; + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = spiffs_probe(config); + return res; } #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 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) { - SPIFFS_API_DBG("%s " - " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi - " addr:"_SPIPRIad - " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi - "\n", - __func__, - SPIFFS_CFG_PHYS_SZ(fs), - SPIFFS_CFG_LOG_PAGE_SZ(fs), - SPIFFS_CFG_LOG_BLOCK_SZ(fs), - SPIFFS_CFG_PHYS_ERASE_SZ(fs), - SPIFFS_CFG_PHYS_ADDR(fs), - fd_space_size, cache_size); - void *user_data; - SPIFFS_LOCK(fs); - user_data = fs->user_data; - memset(fs, 0, sizeof(spiffs)); - _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); - fs->user_data = user_data; - fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); - fs->work = &work[0]; - fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; - memset(fd_space, 0, fd_space_size); - // align fd_space pointer to pointer size byte boundary - u8_t ptr_size = sizeof(void*); - u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); - if (addr_lsb) { - fd_space += (ptr_size-addr_lsb); - fd_space_size -= (ptr_size-addr_lsb); - } - fs->fd_space = fd_space; - fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) { + SPIFFS_API_DBG("%s " + " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi + " addr:"_SPIPRIad + " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi + "\n", + __func__, + SPIFFS_CFG_PHYS_SZ(fs), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + SPIFFS_CFG_LOG_BLOCK_SZ(fs), + SPIFFS_CFG_PHYS_ERASE_SZ(fs), + SPIFFS_CFG_PHYS_ADDR(fs), + fd_space_size, cache_size); + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void *); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size - 1); + if (addr_lsb) { + fd_space += (ptr_size - addr_lsb); + fd_space_size -= (ptr_size - addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size / sizeof(spiffs_fd)); - // align cache pointer to 4 byte boundary - addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); - if (addr_lsb) { - u8_t *cache_8 = (u8_t *)cache; - cache_8 += (ptr_size-addr_lsb); - cache = cache_8; - cache_size -= (ptr_size-addr_lsb); - } - if (cache_size & (ptr_size-1)) { - cache_size -= (cache_size & (ptr_size-1)); - } + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size - 1); + if (addr_lsb) { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size - addr_lsb); + cache = cache_8; + cache_size -= (ptr_size - addr_lsb); + } + if (cache_size & (ptr_size - 1)) { + cache_size -= (cache_size & (ptr_size - 1)); + } #if SPIFFS_CACHE - fs->cache = cache; - fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; - spiffs_cache_init(fs); + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs) * 32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs) * 32 : cache_size; + spiffs_cache_init(fs); #endif - s32_t res; + s32_t res; #if SPIFFS_USE_MAGIC - res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - fs->config_magic = SPIFFS_CONFIG_MAGIC; + fs->config_magic = SPIFFS_CONFIG_MAGIC; - res = spiffs_obj_lu_scan(fs); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); - SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); - SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); - SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); - SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); - SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); - SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); - SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); - fs->check_cb_f = check_cb_f; + fs->check_cb_f = check_cb_f; - fs->mounted = 1; + fs->mounted = 1; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return 0; + return 0; } void SPIFFS_unmount(spiffs *fs) { - SPIFFS_API_DBG("%s\n", __func__); - if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; - SPIFFS_LOCK(fs); - 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) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + 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) { #if SPIFFS_CACHE - (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); #endif - spiffs_fd_return(fs, cur_fd->file_nbr); + spiffs_fd_return(fs, cur_fd->file_nbr); + } } - } - fs->mounted = 0; + fs->mounted = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); } s32_t SPIFFS_errno(spiffs *fs) { - return fs->err_code; + return fs->err_code; } void SPIFFS_clearerr(spiffs *fs) { - SPIFFS_API_DBG("%s\n", __func__); - fs->err_code = SPIFFS_OK; + SPIFFS_API_DBG("%s\n", __func__); + fs->err_code = SPIFFS_OK; } s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { - SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_DBG("%s '%s'\n", __func__, path); #if SPIFFS_READ_ONLY - (void)fs; (void)path; (void)mode; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)path; + (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; #else - (void)mode; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); - spiffs_obj_id obj_id; - s32_t res; + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; - res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t *)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t *)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY } spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { - SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags); - (void)mode; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags); + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); - spiffs_fd *fd; - spiffs_page_ix pix; + spiffs_fd *fd; + spiffs_page_ix pix; #if SPIFFS_READ_ONLY - // not valid flags in read only mode - flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); #endif // SPIFFS_READ_ONLY - s32_t res = spiffs_fd_find_new(fs, &fd, path); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); - if ((flags & SPIFFS_O_CREAT) == 0) { - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - if (res == SPIFFS_OK && - (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { - // creat and excl and file exists - fail - res = SPIFFS_ERR_FILE_EXISTS; - spiffs_fd_return(fs, fd->file_nbr); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { #if !SPIFFS_READ_ONLY - spiffs_obj_id obj_id; - // no need to enter conflicting name here, already looked for it above - res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - flags &= ~SPIFFS_O_TRUNC; + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t *)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; #endif // !SPIFFS_READ_ONLY - } else { + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); + spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if !SPIFFS_READ_ONLY - if (flags & SPIFFS_O_TRUNC) { - res = spiffs_object_truncate(fd, 0, 0); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } #endif // !SPIFFS_READ_ONLY - fd->fdoffset = 0; + fd->fdoffset = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return SPIFFS_FH_OFFS(fs, fd->file_nbr); + return SPIFFS_FH_OFFS(fs, fd->file_nbr); } spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { - SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; + spiffs_fd *fd; - s32_t res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); -#if !SPIFFS_READ_ONLY - if (flags & SPIFFS_O_TRUNC) { - res = spiffs_object_truncate(fd, 0, 0); + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); + spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #endif // !SPIFFS_READ_ONLY - fd->fdoffset = 0; + fd->fdoffset = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return SPIFFS_FH_OFFS(fs, fd->file_nbr); + return SPIFFS_FH_OFFS(fs, fd->file_nbr); } spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { - SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; + spiffs_fd *fd; - s32_t res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { - res = SPIFFS_ERR_NOT_A_FILE; - spiffs_fd_return(fs, fd->file_nbr); + s32_t res = spiffs_fd_find_new(fs, &fd, 0); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); - if (res == SPIFFS_ERR_IS_FREE || - res == SPIFFS_ERR_DELETED || - res == SPIFFS_ERR_NOT_FINALIZED || - res == SPIFFS_ERR_NOT_INDEX || - res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { - res = SPIFFS_ERR_NOT_A_FILE; - } - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } -#if !SPIFFS_READ_ONLY - if (flags & SPIFFS_O_TRUNC) { - res = spiffs_object_truncate(fd, 0, 0); + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); + spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #endif // !SPIFFS_READ_ONLY - fd->fdoffset = 0; + fd->fdoffset = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return SPIFFS_FH_OFFS(fs, fd->file_nbr); + return SPIFFS_FH_OFFS(fs, fd->file_nbr); } static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; + spiffs_fd *fd; + s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if ((fd->flags & SPIFFS_O_RDONLY) == 0) { - res = SPIFFS_ERR_NOT_READABLE; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { - // special case for zero sized files - res = SPIFFS_ERR_END_OF_OBJECT; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); + spiffs_fflush_cache(fs, fh); #endif - if (fd->fdoffset + len >= fd->size) { - // reading beyond file size - s32_t avail = fd->size - fd->fdoffset; - if (avail <= 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); - } - res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); - if (res == SPIFFS_ERR_END_OF_OBJECT) { - fd->fdoffset += avail; - SPIFFS_UNLOCK(fs); - return avail; + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t *)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } } else { - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - len = avail; + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t *)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } - } else { - // reading within file size - res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - fd->fdoffset += len; + fd->fdoffset += len; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return len; + return len; } s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { - SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); - s32_t res = spiffs_hydro_read(fs, fh, buf, len); - if (res == SPIFFS_ERR_END_OF_OBJECT) { - res = 0; - } - return res; + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; } #if !SPIFFS_READ_ONLY static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { - (void)fs; - s32_t res = SPIFFS_OK; - s32_t remaining = len; - if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { - s32_t m_len = MIN((s32_t)(fd->size - offset), len); - res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); - SPIFFS_CHECK_RES(res); - remaining -= m_len; - u8_t *buf_8 = (u8_t *)buf; - buf_8 += m_len; - buf = buf_8; - offset += m_len; - } - if (remaining > 0) { - res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); - SPIFFS_CHECK_RES(res); - } - return len; + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; } #endif // !SPIFFS_READ_ONLY s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { - SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); #if SPIFFS_READ_ONLY - (void)fs; (void)fh; (void)buf; (void)len; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)fh; + (void)buf; + (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; - u32_t offset; + spiffs_fd *fd; + s32_t res; + u32_t offset; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - if ((fd->flags & SPIFFS_O_APPEND)) { - fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; - } - offset = fd->fdoffset; - -#if SPIFFS_CACHE_WR - if (fd->cache_page == 0) { - // see if object id is associated with cache already - fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); - } -#endif - if (fd->flags & SPIFFS_O_APPEND) { - if (fd->size == SPIFFS_UNDEFINED_LEN) { - offset = 0; - } else { - offset = fd->size; + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } + + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + offset = fd->fdoffset; + #if SPIFFS_CACHE_WR - if (fd->cache_page) { - offset = MAX(offset, fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size); + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); } #endif - } - -#if SPIFFS_CACHE_WR - if ((fd->flags & SPIFFS_O_DIRECT) == 0) { - if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { - // small write, try to cache it - u8_t alloc_cpage = 1; - if (fd->cache_page) { - // have a cached page for this fd already, check cache page boundaries - if (offset < fd->cache_page->ucache.swrc.offset || // writing before cache - offset > fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size || // writing after cache - offset + len > fd->cache_page->ucache.swrc.offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page - { - // boundary violation, write back cache first and allocate new - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); - res = spiffs_hydro_write(fs, fd, - spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), - fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); - spiffs_cache_fd_release(fs, fd->cache_page); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + if (fd->flags & SPIFFS_O_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; } else { - // writing within cache - alloc_cpage = 0; + offset = fd->size; } - } - - if (alloc_cpage) { - fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); +#if SPIFFS_CACHE_WR if (fd->cache_page) { - fd->cache_page->ucache.swrc.offset = offset; - fd->cache_page->ucache.swrc.size = 0; - SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id); - } - } - - if (fd->cache_page) { - u32_t offset_in_cpage = offset - fd->cache_page->ucache.swrc.offset; - SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, - offset, offset_in_cpage, len); - spiffs_cache *cache = spiffs_get_cache(fs); - u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); -#ifdef _SPIFFS_TEST - { - intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache; - intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache; - intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); - if (__a1 > __b || __a2 > __b) { - printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); - ERREXIT(); - } + offset = MAX(offset, fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size); } #endif - _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); - fd->cache_page->ucache.swrc.size = MAX(fd->cache_page->ucache.swrc.size, offset_in_cpage + len); - fd->fdoffset += len; - SPIFFS_UNLOCK(fs); - return len; - } else { - res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - fd->fdoffset += len; - SPIFFS_UNLOCK(fs); - return res; - } - } else { - // big write, no need to cache it - but first check if there is a cached write already - if (fd->cache_page) { - // write back cache first - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); - res = spiffs_hydro_write(fs, fd, - spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), - fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); - spiffs_cache_fd_release(fs, fd->cache_page); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - // data written below - } } - } + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->ucache.swrc.offset || // writing before cache + offset > fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size || // writing after cache + offset + len > fd->cache_page->ucache.swrc.offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) { // writing beyond cache page + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->ucache.swrc.offset = offset; + fd->cache_page->ucache.swrc.size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->ucache.swrc.offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); +#ifdef _SPIFFS_TEST + { + intptr_t __a1 = (u8_t *)&cpage_data[offset_in_cpage] - (u8_t *)cache; + intptr_t __a2 = (u8_t *)&cpage_data[offset_in_cpage] + len - (u8_t *)cache; + intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + if (__a1 > __b || __a2 > __b) { + printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); + ERREXIT(); + } + } +#endif + _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->ucache.swrc.size = MAX(fd->cache_page->ucache.swrc.size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } + } #endif - res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - fd->fdoffset += len; + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; #endif // SPIFFS_READ_ONLY } s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { - SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char *[]) {"SET", "CUR", "END", "???"}[MIN(whence, 3)]); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); + spiffs_fflush_cache(fs, fh); #endif - s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; - switch (whence) { - case SPIFFS_SEEK_CUR: - offs = fd->fdoffset+offs; - break; - case SPIFFS_SEEK_END: - offs = file_size + offs; - break; - } - if (offs < 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); - } - if (offs > file_size) { - fd->fdoffset = file_size; - res = SPIFFS_ERR_END_OF_OBJECT; - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); - spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - if (fd->cursor_objix_spix != objix_spix) { - spiffs_page_ix pix; - res = spiffs_obj_lu_find_id_and_span( - fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset + offs; + break; + case SPIFFS_SEEK_END: + offs = file_size + offs; + break; + } + if (offs < 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); + } + if (offs > file_size) { + fd->fdoffset = file_size; + res = SPIFFS_ERR_END_OF_OBJECT; + } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - fd->cursor_objix_spix = objix_spix; - fd->cursor_objix_pix = pix; - } - fd->fdoffset = offs; - SPIFFS_UNLOCK(fs); + spiffs_span_ix data_spix = (offs > 0 ? (offs - 1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; - return offs; + SPIFFS_UNLOCK(fs); + + return offs; } s32_t SPIFFS_remove(spiffs *fs, const char *path) { - SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_DBG("%s '%s'\n", __func__, path); #if SPIFFS_READ_ONLY - (void)fs; (void)path; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); - spiffs_fd *fd; - spiffs_page_ix pix; - s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; - res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_open_by_page(fs, pix, fd, 0,0); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_truncate(fd, 0, 1); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY } s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); #if SPIFFS_READ_ONLY - (void)fs; (void)fh; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #if SPIFFS_CACHE_WR - spiffs_cache_fd_release(fs, fd->cache_page); + spiffs_cache_fd_release(fs, fd->cache_page); #endif - res = spiffs_object_truncate(fd, 0, 1); + res = spiffs_object_truncate(fd, 0, 1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return 0; + return 0; #endif // SPIFFS_READ_ONLY } static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { - (void)fh; - spiffs_page_object_ix_header objix_hdr; - spiffs_obj_id obj_id; - s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, - SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - SPIFFS_API_CHECK_RES(fs, res); + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); - u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + - SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); - res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, - obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); - SPIFFS_API_CHECK_RES(fs, res); + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); - s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - s->type = objix_hdr.type; - s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; - s->pix = pix; - strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); #if SPIFFS_OBJ_META_LEN - _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); #endif - return res; + return res; } s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { - SPIFFS_API_DBG("%s '%s'\n", __func__, path); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); - s32_t res; - spiffs_page_ix pix; + s32_t res; + spiffs_page_ix pix; - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_stat_pix(fs, pix, 0, s); + res = spiffs_stat_pix(fs, pix, 0, s); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; } s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; + spiffs_fd *fd; + s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); + spiffs_fflush_cache(fs, fh); #endif - res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; } // Checks if there are any cached writes for the object id associated with // given filehandle. If so, these writes are flushed. #if SPIFFS_CACHE == 1 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { - (void)fs; - (void)fh; - s32_t res = SPIFFS_OK; + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; #if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); - if ((fd->flags & SPIFFS_O_DIRECT) == 0) { - if (fd->cache_page == 0) { - // see if object id is associated with cache already - fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + if (res < SPIFFS_OK) { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } } - if (fd->cache_page) { - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); - res = spiffs_hydro_write(fs, fd, - spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), - fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); - if (res < SPIFFS_OK) { - fs->err_code = res; - } - spiffs_cache_fd_release(fs, fd->cache_page); - } - } #endif - return res; + return res; } #endif s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); - (void)fh; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - s32_t res = SPIFFS_OK; + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; #if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR - SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs,res); - SPIFFS_UNLOCK(fs); + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); #endif - return res; + return res; } s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); - s32_t res = SPIFFS_OK; - SPIFFS_LOCK(fs); + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); #if SPIFFS_CACHE - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - res = spiffs_fd_return(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; } s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { - SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); + SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); #if SPIFFS_READ_ONLY - (void)fs; (void)old_path; (void)new_path; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)old_path; + (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || - strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); - spiffs_page_ix pix_old, pix_dummy; - spiffs_fd *fd; + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; - s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - } else if (res == SPIFFS_OK) { - res = SPIFFS_ERR_CONFLICTING_NAME; - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + } else if (res == SPIFFS_OK) { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, - 0, 0, &pix_dummy); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t *)new_path, + 0, 0, &pix_dummy); #if SPIFFS_TEMPORAL_FD_CACHE - if (res == SPIFFS_OK) { - spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); - } + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } #endif - spiffs_fd_return(fs, fd->file_nbr); + spiffs_fd_return(fs, fd->file_nbr); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; #endif // SPIFFS_READ_ONLY } #if SPIFFS_OBJ_META_LEN s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { #if SPIFFS_READ_ONLY - (void)fs; (void)name; (void)meta; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)name; + (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_page_ix pix, pix_dummy; - spiffs_fd *fd; + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; - s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); - res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); - if (res != SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, - 0, &pix_dummy); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_UNLOCK(fs); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - SPIFFS_UNLOCK(fs); - - return res; + return res; #endif // SPIFFS_READ_ONLY } s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { #if SPIFFS_READ_ONLY - (void)fs; (void)fh; (void)meta; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)fh; + (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - s32_t res; - spiffs_fd *fd; - spiffs_page_ix pix_dummy; + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, - 0, &pix_dummy); + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); - SPIFFS_UNLOCK(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - return res; + SPIFFS_UNLOCK(fs); + + return res; #endif // SPIFFS_READ_ONLY } #endif // SPIFFS_OBJ_META_LEN spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { - SPIFFS_API_DBG("%s\n", __func__); - (void)name; + SPIFFS_API_DBG("%s\n", __func__); + (void)name; - if (!SPIFFS_CHECK_CFG((fs))) { - (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; - return 0; - } + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } - if (!SPIFFS_CHECK_MOUNT(fs)) { - fs->err_code = SPIFFS_ERR_NOT_MOUNTED; - return 0; - } + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } - d->fs = fs; - d->block = 0; - d->entry = 0; - return d; + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; } static s32_t spiffs_read_dir_v( @@ -1025,428 +1037,430 @@ static s32_t spiffs_read_dir_v( int ix_entry, const void *user_const_p, void *user_var_p) { - (void)user_const_p; - s32_t res; - spiffs_page_object_ix_header objix_hdr; - if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || - (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { - return SPIFFS_VIS_COUNTINUE; - } + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - if (res != SPIFFS_OK) return res; - if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && - objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; - e->obj_id = obj_id; - strcpy((char *)e->name, (char *)objix_hdr.name); - e->type = objix_hdr.type; - e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; - e->pix = pix; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent *)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; #if SPIFFS_OBJ_META_LEN - _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); #endif - return SPIFFS_OK; - } - return SPIFFS_VIS_COUNTINUE; + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; } struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { - SPIFFS_API_DBG("%s\n", __func__); - if (!SPIFFS_CHECK_MOUNT(d->fs)) { - d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; - return 0; - } - SPIFFS_LOCK(d->fs); + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); - spiffs_block_ix bix; - int entry; - s32_t res; - struct spiffs_dirent *ret = 0; + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; - res = spiffs_obj_lu_find_entry_visitor(d->fs, - d->block, - d->entry, - SPIFFS_VIS_NO_WRAP, - 0, - spiffs_read_dir_v, - 0, - e, - &bix, - &entry); - if (res == SPIFFS_OK) { - d->block = bix; - d->entry = entry + 1; - e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; - ret = e; - } else { - d->fs->err_code = res; - } - SPIFFS_UNLOCK(d->fs); - return ret; + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } else { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; } s32_t SPIFFS_closedir(spiffs_DIR *d) { - SPIFFS_API_DBG("%s\n", __func__); - SPIFFS_API_CHECK_CFG(d->fs); - SPIFFS_API_CHECK_MOUNT(d->fs); - return 0; + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; } s32_t SPIFFS_check(spiffs *fs) { - SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_API_DBG("%s\n", __func__); #if SPIFFS_READ_ONLY - (void)fs; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; #else - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - res = spiffs_lookup_consistency_check(fs, 0); + res = spiffs_lookup_consistency_check(fs, 0); - res = spiffs_object_index_consistency_check(fs); + res = spiffs_object_index_consistency_check(fs); - res = spiffs_page_consistency_check(fs); + res = spiffs_page_consistency_check(fs); - res = spiffs_obj_lu_scan(fs); + res = spiffs_obj_lu_scan(fs); - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; #endif // SPIFFS_READ_ONLY } s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { - SPIFFS_API_DBG("%s\n", __func__); - s32_t res = SPIFFS_OK; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); - u32_t blocks = fs->block_count; - u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); - u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); - u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page - if (total) { - *total = total_data_pages * data_page_size; - } + if (total) { + *total = total_data_pages * data_page_size; + } - if (used) { - *used = fs->stats_p_allocated * data_page_size; - } + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; } s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { - SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages); + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages); #if SPIFFS_READ_ONLY - (void)fs; (void)max_free_pages; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; #else - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - res = spiffs_gc_quick(fs, max_free_pages); + res = spiffs_gc_quick(fs, max_free_pages); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY } s32_t SPIFFS_gc(spiffs *fs, u32_t size) { - SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size); + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size); #if SPIFFS_READ_ONLY - (void)fs; (void)size; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; #else - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - res = spiffs_gc_check(fs, size); + res = spiffs_gc_check(fs, size); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY } s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; } s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - res = fd->fdoffset; + res = fd->fdoffset; - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; } s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { - SPIFFS_API_DBG("%s\n", __func__); - SPIFFS_LOCK(fs); - fs->file_cb_f = cb_func; - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; } #if SPIFFS_IX_MAP 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) { - SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + u32_t offset, u32_t len, spiffs_page_ix *map_buf) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if (fd->ix_map) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); - } + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } - map->map_buf = map_buf; - map->offset = offset; - // nb: spix range includes last - map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); - memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); - fd->ix_map = map; + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; - // scan for pixes - res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; } s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if (fd->ix_map == 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); - } + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } - fd->ix_map = 0; + fd->ix_map = 0; - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; } s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { - SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset); - s32_t res = SPIFFS_OK; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if (fd->ix_map == 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); - } - - spiffs_ix_map *map = fd->ix_map; - - s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; - map->offset = offset; - - // move existing pixes if within map offs - if (spix_diff != 0) { - // move vector - int i; - const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last - map->start_spix += spix_diff; - map->end_spix += spix_diff; - if (spix_diff >= vec_len) { - // moving beyond range - memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); - // populate_ix_map is inclusive - res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } else if (spix_diff > 0) { - // diff positive - for (i = 0; i < vec_len - spix_diff; i++) { - map->map_buf[i] = map->map_buf[i + spix_diff]; - } - // memset is non-inclusive - memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); - // populate_ix_map is inclusive - res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } else { - // diff negative - for (i = vec_len - 1; i >= -spix_diff; i--) { - map->map_buf[i] = map->map_buf[i + spix_diff]; - } - // memset is non-inclusive - memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); - // populate_ix_map is inclusive - res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); } - } + spiffs_ix_map *map = fd->ix_map; - SPIFFS_UNLOCK(fs); - return res; + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; } s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { - SPIFFS_API_CHECK_CFG(fs); - // always add one extra page, the offset might change to the middle of a page - return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs)) / SPIFFS_DATA_PAGE_SIZE(fs); } s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { - SPIFFS_API_CHECK_CFG(fs); - return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); } #endif // SPIFFS_IX_MAP #if SPIFFS_TEST_VISUALISATION s32_t SPIFFS_vis(spiffs *fs) { - s32_t res = SPIFFS_OK; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - 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; - spiffs_block_ix bix = 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; + spiffs_block_ix bix = 0; - while (bix < fs->block_count) { - // check each object lookup page - int obj_lookup_page = 0; - int cur_entry = 0; + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; - 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 (cur_entry == 0) { - spiffs_printf(_SPIPRIbl" ", bix); - } else if ((cur_entry & 0x3f) == 0) { - spiffs_printf(" "); - } - if (obj_id == SPIFFS_OBJ_ID_FREE) { - spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); - } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { - spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); - } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ - spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + 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 (cur_entry == 0) { + spiffs_printf(_SPIPRIbl" ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id) - 1) { + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); } else { - spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + spiffs_printf("\tera_cnt: N/A\n"); } - cur_entry++; - if ((cur_entry & 0x3f) == 0) { - spiffs_printf("\n"); - } - } // per entry - obj_lookup_page++; - } // per object lookup page - spiffs_obj_id erase_count; - res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_ERASE_COUNT_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&erase_count); - SPIFFS_CHECK_RES(res); + bix++; + } // per block - if (erase_count != (spiffs_obj_id)-1) { - spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); - } else { - spiffs_printf("\tera_cnt: N/A\n"); - } - - bix++; - } // per block - - spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); - spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); - spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); - spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); - spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); - spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); - SPIFFS_UNLOCK(fs); - u32_t total, used; - SPIFFS_info(fs, &total, &used); - spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); - return res; + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); + return res; } #endif diff --git a/armsrc/spiffs_nucleus.c b/armsrc/spiffs_nucleus.c index 1c913921b..7951a7952 100644 --- a/armsrc/spiffs_nucleus.c +++ b/armsrc/spiffs_nucleus.c @@ -3,60 +3,60 @@ #include "printf.h" static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { - s32_t res = SPIFFS_OK; - if (pix == (spiffs_page_ix)-1) { - // referring to page 0xffff...., bad object index - return SPIFFS_ERR_INDEX_REF_FREE; - } - if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - // referring to an object lookup page, bad object index - return SPIFFS_ERR_INDEX_REF_LU; - } - if (pix > SPIFFS_MAX_PAGES(fs)) { - // referring to a bad page - return SPIFFS_ERR_INDEX_REF_INVALID; - } + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix) - 1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } #if SPIFFS_PAGE_CHECK - spiffs_page_header ph; - res = _spiffs_rd( - fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, pix), - sizeof(spiffs_page_header), - (u8_t *)&ph); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); #endif - return res; + return res; } #if !SPIFFS_READ_ONLY static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { - s32_t res = SPIFFS_OK; - if (pix == (spiffs_page_ix)-1) { - // referring to page 0xffff...., bad object index - return SPIFFS_ERR_INDEX_FREE; - } - if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - // referring to an object lookup page, bad object index - return SPIFFS_ERR_INDEX_LU; - } - if (pix > SPIFFS_MAX_PAGES(fs)) { - // referring to a bad page - return SPIFFS_ERR_INDEX_INVALID; - } + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix) - 1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } #if SPIFFS_PAGE_CHECK - spiffs_page_header ph; - res = _spiffs_rd( - fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, pix), - sizeof(spiffs_page_header), - (u8_t *)&ph); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); #endif - return res; + return res; } #endif // !SPIFFS_READ_ONLY @@ -67,7 +67,7 @@ s32_t spiffs_phys_rd( u32_t addr, u32_t len, u8_t *dst) { - return SPIFFS_HAL_READ(fs, addr, len, dst); + return SPIFFS_HAL_READ(fs, addr, len, dst); } s32_t spiffs_phys_wr( @@ -75,7 +75,7 @@ s32_t spiffs_phys_wr( u32_t addr, u32_t len, u8_t *src) { - return SPIFFS_HAL_WRITE(fs, addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } #endif @@ -87,20 +87,20 @@ s32_t spiffs_phys_cpy( u32_t dst, u32_t src, u32_t len) { - (void)fh; - s32_t res; - u8_t b[SPIFFS_COPY_BUFFER_STACK]; - while (len > 0) { - u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); - SPIFFS_CHECK_RES(res); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); - SPIFFS_CHECK_RES(res); - len -= chunk_size; - src += chunk_size; - dst += chunk_size; - } - return SPIFFS_OK; + (void)fh; + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; } #endif // !SPIFFS_READ_ONLY @@ -133,187 +133,186 @@ s32_t spiffs_obj_lu_find_entry_visitor( void *user_var_p, spiffs_block_ix *block_ix, int *lu_entry) { - s32_t res = SPIFFS_OK; - s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); - spiffs_block_ix cur_block = starting_block; - u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - int cur_entry = starting_lu_entry; - 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 = starting_lu_entry; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - // wrap initial - if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { - cur_entry = 0; - cur_block++; - cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); - if (cur_block >= fs->block_count) { - if (flags & SPIFFS_VIS_NO_WRAP) { - return SPIFFS_VIS_END; - } else { - // block wrap - cur_block = 0; - cur_block_addr = 0; - } - } - } - - // check each block - while (res == SPIFFS_OK && entry_count > 0) { - int obj_lookup_page = cur_entry / entries_per_page; - // 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 && // for non-last obj lookup pages - cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page - { - if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { - if (block_ix) *block_ix = cur_block; - if (lu_entry) *lu_entry = cur_entry; - if (v) { - res = v( - fs, - (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], - cur_block, - cur_entry, - user_const_p, - user_var_p); - if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { - if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { - 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); - SPIFFS_CHECK_RES(res); - } - res = SPIFFS_OK; - cur_entry++; - entry_count--; - continue; + // wrap initial + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; } else { - return res; + // block wrap + cur_block = 0; + cur_block_addr = 0; } - } else { - return SPIFFS_OK; - } } - entry_count--; - cur_entry++; - } // per entry - obj_lookup_page++; - } // per object lookup page - cur_entry = 0; - cur_block++; - cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); - if (cur_block >= fs->block_count) { - if (flags & SPIFFS_VIS_NO_WRAP) { - return SPIFFS_VIS_END; - } else { - // block wrap - cur_block = 0; - cur_block_addr = 0; - } } - } // per block - SPIFFS_CHECK_RES(res); + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // 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 && // for non-last obj lookup pages + cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) { // for last obj lookup page + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry - entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (lu_entry) *lu_entry = cur_entry; + if (v) { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry - entry_offset], + cur_block, + cur_entry, + user_const_p, + user_var_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + 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); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } else { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } // per block - return SPIFFS_VIS_END; + SPIFFS_CHECK_RES(res); + + return SPIFFS_VIS_END; } #if !SPIFFS_READ_ONLY s32_t spiffs_erase_block( spiffs *fs, spiffs_block_ix bix) { - s32_t res; - u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); - s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); - // here we ignore res, just try erasing the block - while (size > 0) { - SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); - size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); - } - fs->free_blocks++; + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; - // register erase count for this block - res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_ERASE_COUNT_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); - SPIFFS_CHECK_RES(res); + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); #if SPIFFS_USE_MAGIC - // finally, write magic - spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); - res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_MAGIC_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&magic); - SPIFFS_CHECK_RES(res); + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); #endif - fs->max_erase_count++; - if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { - fs->max_erase_count = 0; - } + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } - return res; + return res; } #endif // !SPIFFS_READ_ONLY #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 s32_t spiffs_probe( spiffs_config *cfg) { - s32_t res; - u32_t paddr; - spiffs dummy_fs; // create a dummy fs struct just to be able to use macros - _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); - dummy_fs.block_count = 0; + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; - // Read three magics, as one block may be in an aborted erase state. - // At least two of these must contain magic and be in decreasing order. - spiffs_obj_id magic[3]; - spiffs_obj_id bix_count[3]; + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; - spiffs_block_ix bix; - for (bix = 0; bix < 3; bix++) { - paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); #if SPIFFS_HAL_CALLBACK_EXTRA - // not any proper fs to report here, so callback with null - // (cross fingers that no-one gets angry) - res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); #else - res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); #endif - bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); - SPIFFS_CHECK_RES(res); - } + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } - // check that we have sane number of blocks - if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; - // check that the order is correct, take aborted erases in calculation - // first block aborted erase - if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { - return (bix_count[1]+1) * cfg->log_block_size; - } - // second block aborted erase - if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { - return bix_count[0] * cfg->log_block_size; - } - // third block aborted erase - if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { - return bix_count[0] * cfg->log_block_size; - } - // no block has aborted erase - if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { - return bix_count[0] * cfg->log_block_size; - } + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1] + 1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; + } - return SPIFFS_ERR_PROBE_NOT_A_FS; + return SPIFFS_ERR_PROBE_NOT_A_FS; } #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 @@ -325,21 +324,21 @@ static s32_t spiffs_obj_lu_scan_v( int ix_entry, const void *user_const_p, void *user_var_p) { - (void)bix; - (void)user_const_p; - (void)user_var_p; - if (obj_id == SPIFFS_OBJ_ID_FREE) { - if (ix_entry == 0) { - fs->free_blocks++; - // todo optimize further, return SPIFFS_NEXT_BLOCK + (void)bix; + (void)user_const_p; + (void)user_var_p; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; } - } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { - fs->stats_p_deleted++; - } else { - fs->stats_p_allocated++; - } - return SPIFFS_VIS_COUNTINUE; + return SPIFFS_VIS_COUNTINUE; } @@ -348,100 +347,100 @@ static s32_t spiffs_obj_lu_scan_v( // Checks magic if enabled s32_t spiffs_obj_lu_scan( spiffs *fs) { - s32_t res; - spiffs_block_ix bix; - int entry; + s32_t res; + spiffs_block_ix bix; + int entry; #if SPIFFS_USE_MAGIC - spiffs_block_ix unerased_bix = (spiffs_block_ix)-1; + spiffs_block_ix unerased_bix = (spiffs_block_ix) - 1; #endif - // find out erase count - // if enabled, check magic - bix = 0; - spiffs_obj_id erase_count_final; - spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; - spiffs_obj_id erase_count_max = 0; - while (bix < fs->block_count) { + // find out erase count + // if enabled, check magic + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) { #if SPIFFS_USE_MAGIC - spiffs_obj_id magic; - res = _spiffs_rd(fs, - SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_MAGIC_PADDR(fs, bix) , - sizeof(spiffs_obj_id), (u8_t *)&magic); + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); - SPIFFS_CHECK_RES(res); - if (magic != SPIFFS_MAGIC(fs, bix)) { - if (unerased_bix == (spiffs_block_ix)-1) { - // allow one unerased block as it might be powered down during an erase - unerased_bix = bix; - } else { - // more than one unerased block, bail out - SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); - } - } + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs, bix)) { + if (unerased_bix == (spiffs_block_ix) - 1) { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } else { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } #endif - spiffs_obj_id erase_count; - res = _spiffs_rd(fs, - SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) , - sizeof(spiffs_obj_id), (u8_t *)&erase_count); - SPIFFS_CHECK_RES(res); - if (erase_count != SPIFFS_OBJ_ID_FREE) { - erase_count_min = MIN(erase_count_min, erase_count); - erase_count_max = MAX(erase_count_max, erase_count); + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; } - bix++; - } - if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { - // clean system, set counter to zero - erase_count_final = 0; - } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) { - // wrap, take min - erase_count_final = erase_count_min+1; - } else { - erase_count_final = erase_count_max+1; - } + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { + // clean system, set counter to zero + erase_count_final = 0; + } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE) / 2) { + // wrap, take min + erase_count_final = erase_count_min + 1; + } else { + erase_count_final = erase_count_max + 1; + } - fs->max_erase_count = erase_count_final; + fs->max_erase_count = erase_count_final; #if SPIFFS_USE_MAGIC - if (unerased_bix != (spiffs_block_ix)-1) { - // found one unerased block, remedy - SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); + if (unerased_bix != (spiffs_block_ix) - 1) { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); #if SPIFFS_READ_ONLY - res = SPIFFS_ERR_RO_ABORTED_OPERATION; + res = SPIFFS_ERR_RO_ABORTED_OPERATION; #else - res = spiffs_erase_block(fs, unerased_bix); + res = spiffs_erase_block(fs, unerased_bix); #endif // SPIFFS_READ_ONLY - SPIFFS_CHECK_RES(res); - } + SPIFFS_CHECK_RES(res); + } #endif - // count blocks + // count blocks - fs->free_blocks = 0; - fs->stats_p_allocated = 0; - fs->stats_p_deleted = 0; + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; - res = spiffs_obj_lu_find_entry_visitor(fs, - 0, - 0, - 0, - 0, - spiffs_obj_lu_scan_v, - 0, - 0, - &bix, - &entry); + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } - SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_RES(res); - return res; + return res; } #if !SPIFFS_READ_ONLY @@ -453,31 +452,31 @@ s32_t spiffs_obj_lu_find_free( int starting_lu_entry, spiffs_block_ix *block_ix, int *lu_entry) { - s32_t res; - if (!fs->cleaning && fs->free_blocks < 2) { - res = spiffs_gc_quick(fs, 0); - if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { - res = SPIFFS_OK; + s32_t res; + if (!fs->cleaning && fs->free_blocks < 2) { + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) { + return SPIFFS_ERR_FULL; + } } - SPIFFS_CHECK_RES(res); - if (fs->free_blocks < 2) { - return SPIFFS_ERR_FULL; + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; + if (*lu_entry == 0) { + fs->free_blocks--; + } } - } - res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, - SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); - if (res == SPIFFS_OK) { - fs->free_cursor_block_ix = *block_ix; - fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; - if (*lu_entry == 0) { - fs->free_blocks--; + if (res == SPIFFS_ERR_FULL) { + SPIFFS_DBGF("fs full\n"); } - } - if (res == SPIFFS_ERR_FULL) { - SPIFFS_DBGF("fs full\n"); - } - return res; + return res; } #endif // !SPIFFS_READ_ONLY @@ -490,12 +489,12 @@ s32_t spiffs_obj_lu_find_id( spiffs_obj_id obj_id, spiffs_block_ix *block_ix, int *lu_entry) { - s32_t res = spiffs_obj_lu_find_entry_visitor( - fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } - return res; + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; } @@ -506,21 +505,21 @@ static s32_t spiffs_obj_lu_find_id_and_span_v( int ix_entry, const void *user_const_p, void *user_var_p) { - s32_t res; - spiffs_page_header ph; - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); - SPIFFS_CHECK_RES(res); - if (ph.obj_id == obj_id && - ph.span_ix == *((spiffs_span_ix*)user_var_p) && - (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && - !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && - (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) { - return SPIFFS_OK; - } else { - return SPIFFS_VIS_COUNTINUE; - } + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == *((spiffs_span_ix *)user_var_p) && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_const_p == 0 || *((const spiffs_page_ix *)user_const_p) != pix)) { + return SPIFFS_OK; + } else { + return SPIFFS_VIS_COUNTINUE; + } } // Find object lookup entry containing given id and span index @@ -531,35 +530,35 @@ s32_t spiffs_obj_lu_find_id_and_span( spiffs_span_ix spix, spiffs_page_ix exclusion_pix, spiffs_page_ix *pix) { - s32_t res; - spiffs_block_ix bix; - int entry; + s32_t res; + spiffs_block_ix bix; + int entry; - res = spiffs_obj_lu_find_entry_visitor(fs, - fs->cursor_block_ix, - fs->cursor_obj_lu_entry, - SPIFFS_VIS_CHECK_ID, - obj_id, - spiffs_obj_lu_find_id_and_span_v, - exclusion_pix ? &exclusion_pix : 0, - &spix, - &bix, - &entry); + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } - SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_RES(res); - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } - fs->cursor_block_ix = bix; - fs->cursor_obj_lu_entry = entry; + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; - return res; + return res; } // Find object lookup entry containing given id and span index in page headers only @@ -570,97 +569,97 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr( spiffs_span_ix spix, spiffs_page_ix exclusion_pix, spiffs_page_ix *pix) { - s32_t res; - spiffs_block_ix bix; - int entry; + s32_t res; + spiffs_block_ix bix; + int entry; - res = spiffs_obj_lu_find_entry_visitor(fs, - fs->cursor_block_ix, - fs->cursor_obj_lu_entry, - SPIFFS_VIS_CHECK_PH, - obj_id, - spiffs_obj_lu_find_id_and_span_v, - exclusion_pix ? &exclusion_pix : 0, - &spix, - &bix, - &entry); + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } - SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_RES(res); - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } - fs->cursor_block_ix = bix; - fs->cursor_obj_lu_entry = entry; + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; - return res; + return res; } #if SPIFFS_IX_MAP // update index map of given fd with given object index data static void spiffs_update_ix_map(spiffs *fs, - spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { #if SPIFFS_SINGLETON - (void)fs; + (void)fs; #endif - spiffs_ix_map *map = fd->ix_map; - spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); - spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); - // check if updated ix is within map range - if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { - return; - } - - // update memory mapped page index buffer to new pages - - // get range of updated object index map data span indices - spiffs_span_ix objix_data_spix_start = - SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); - spiffs_span_ix objix_data_spix_end = objix_data_spix_start + - (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); - - // calc union of object index range and index map range array - spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); - spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); - - while (map_spix < map_spix_end) { - spiffs_page_ix objix_data_pix; - if (objix_spix == 0) { - // get data page from object index header page - objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; - } else { - // get data page from object index page - objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { + return; } - if (objix_data_pix == (spiffs_page_ix)-1) { - // reached end of object, abort - break; + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } else { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix) - 1) { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; } - - map->map_buf[map_spix - map->start_spix] = objix_data_pix; - SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", - fd->obj_id, map_spix - map->start_spix, - map->start_spix, map->end_spix, - objix->p_hdr.span_ix, - objix_data_pix); - - map_spix++; - } } typedef struct { - spiffs_fd *fd; - u32_t remaining_objix_pages_to_visit; - spiffs_span_ix map_objix_start_spix; - spiffs_span_ix map_objix_end_spix; + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; } spiffs_ix_map_populate_state; static s32_t spiffs_populate_ix_map_v( @@ -670,78 +669,78 @@ static s32_t spiffs_populate_ix_map_v( int ix_entry, const void *user_const_p, void *user_var_p) { - (void)user_const_p; - s32_t res; - spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - // load header to check it - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); - - // check if hdr is ok, and if objix range overlap with ix map range - if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && - objix->p_hdr.span_ix >= state->map_objix_start_spix && - objix->p_hdr.span_ix <= state->map_objix_end_spix) { - // ok, load rest of object index + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), - (u8_t *)objix + sizeof(spiffs_page_object_ix)); + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); - spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); - state->remaining_objix_pages_to_visit--; - SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", - state->fd->obj_id, - state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, - state->remaining_objix_pages_to_visit); - } + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); - if (res == SPIFFS_OK) { - res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; - } - return res; + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; } // populates index map, from vector entry start to vector entry end, inclusive s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { - s32_t res; - spiffs_ix_map *map = fd->ix_map; - spiffs_ix_map_populate_state state; - vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); - vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); - if (vec_entry_start > vec_entry_end) { - return SPIFFS_ERR_IX_MAP_BAD_RANGE; - } - state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); - state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); - state.remaining_objix_pages_to_visit = - state.map_objix_end_spix - state.map_objix_start_spix + 1; - state.fd = fd; + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); + vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); + if (vec_entry_start > vec_entry_end) { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; - res = spiffs_obj_lu_find_entry_visitor( - fs, - SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), - SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), - SPIFFS_VIS_CHECK_ID, - fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, - spiffs_populate_ix_map_v, - 0, - &state, - 0, - 0); + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } - return res; + return res; } #endif @@ -760,50 +759,50 @@ s32_t spiffs_page_allocate_data( u32_t page_offs, u8_t finalize, spiffs_page_ix *pix) { - s32_t res = SPIFFS_OK; - spiffs_block_ix bix; - int entry; + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; - // 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); - - // occupy page in object lookup - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); - SPIFFS_CHECK_RES(res); - - fs->stats_p_allocated++; - - // write page header - ph->flags &= ~SPIFFS_PH_FLAG_USED; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); - SPIFFS_CHECK_RES(res); - - // write page data - if (data) { - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + // 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); - } - // finalize header if necessary - if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { - ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&ph->flags); + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t *)ph); SPIFFS_CHECK_RES(res); - } - // return written page - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } - return res; + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; } #endif // !SPIFFS_READ_ONLY @@ -818,57 +817,57 @@ s32_t spiffs_page_move( spiffs_page_header *page_hdr, spiffs_page_ix src_pix, spiffs_page_ix *dst_pix) { - s32_t res; - u8_t was_final = 0; - spiffs_page_header *p_hdr; - spiffs_block_ix bix; - int entry; - spiffs_page_ix free_pix; + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; - // 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); - - if (dst_pix) *dst_pix = free_pix; - - p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; - if (page_data) { - // got page data - was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; - // write unfinalized page - p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; - p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; - 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), page_data); - } else { - // copy page data - res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); - } - SPIFFS_CHECK_RES(res); - - // mark entry in destination object lookup - 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); - - fs->stats_p_allocated++; - - if (was_final) { - // mark finalized in destination page - p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fh, - SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&p_hdr->flags); + // 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); - } - // mark source deleted - res = spiffs_page_delete(fs, src_pix); - return res; + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + 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), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + 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); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; } #endif // !SPIFFS_READ_ONLY @@ -877,34 +876,34 @@ s32_t spiffs_page_move( s32_t spiffs_page_delete( spiffs *fs, spiffs_page_ix pix) { - s32_t res; - // mark deleted entry in source object lookup - spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, - 0, - SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), - sizeof(spiffs_obj_id), - (u8_t *)&d_obj_id); - SPIFFS_CHECK_RES(res); + s32_t res; + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); - fs->stats_p_deleted++; - fs->stats_p_allocated--; + fs->stats_p_deleted++; + fs->stats_p_allocated--; - // mark deleted in source page - u8_t flags = 0xff; + // mark deleted in source page + u8_t flags = 0xff; #if SPIFFS_NO_BLIND_WRITES - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), - sizeof(flags), &flags); - SPIFFS_CHECK_RES(res); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); #endif - flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, - 0, - SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), - sizeof(flags), &flags); + flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); - return res; + return res; } #endif // !SPIFFS_READ_ONLY @@ -917,58 +916,58 @@ s32_t spiffs_object_create( const u8_t meta[], spiffs_obj_type type, spiffs_page_ix *objix_hdr_pix) { - s32_t res = SPIFFS_OK; - spiffs_block_ix bix; - spiffs_page_object_ix_header oix_hdr; - int entry; + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; - res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_CHECK_RES(res); + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); - obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + 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); - SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + // 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); + SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); - // occupy page in object lookup - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); - SPIFFS_CHECK_RES(res); + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); - fs->stats_p_allocated++; + fs->stats_p_allocated++; - // write empty object index page - oix_hdr.p_hdr.obj_id = obj_id; - oix_hdr.p_hdr.span_ix = 0; - oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); - oix_hdr.type = type; - oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page - strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char *)oix_hdr.name, (const char *)name, SPIFFS_OBJ_NAME_LEN); #if SPIFFS_OBJ_META_LEN - if (meta) { - _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); - } else { - memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); - } + if (meta) { + _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } #else - (void) meta; + (void) meta; #endif - // update page - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, - SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); - if (objix_hdr_pix) { - *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } - return res; + return res; } #endif // !SPIFFS_READ_ONLY @@ -987,55 +986,55 @@ s32_t spiffs_object_update_index_hdr( const u8_t meta[], u32_t size, spiffs_page_ix *new_pix) { - s32_t res = SPIFFS_OK; - spiffs_page_object_ix_header *objix_hdr; - spiffs_page_ix new_objix_hdr_pix; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; - obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; - if (new_objix_hdr_data) { - // object index header page already given to us, no need to load it - objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; - } else { - // read object index header page - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - objix_hdr = (spiffs_page_object_ix_header *)fs->work; - } - - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); - - // change name - if (name) { - strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); - } -#if SPIFFS_OBJ_META_LEN - if (meta) { - _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); - } -#else - (void) meta; -#endif - if (size) { - objix_hdr->size = size; - } - - // move and update page - res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); - - if (res == SPIFFS_OK) { - if (new_pix) { - *new_pix = new_objix_hdr_pix; + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; } - // callback on object index update - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, - new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, - obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); - if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster - } - return res; + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) { + strncpy((char *)objix_hdr->name, (const char *)name, SPIFFS_OBJ_NAME_LEN); + } +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t *)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; } #endif // !SPIFFS_READ_ONLY @@ -1048,107 +1047,107 @@ void spiffs_cb_object_event( spiffs_page_ix new_pix, u32_t new_size) { #if SPIFFS_IX_MAP == 0 - (void)objix; + (void)objix; #endif - // update index caches in all file descriptors - spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; - u32_t i; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]){"UPD", "NEW", "DEL", "MOV", "HUP","???"}[MIN(ev,5)], - obj_id_raw, spix, new_pix, new_size); - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file + // update index caches in all file descriptors + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]) {"UPD", "NEW", "DEL", "MOV", "HUP", "???"}[MIN(ev, 5)], + obj_id_raw, spix, new_pix, new_size); + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file #if !SPIFFS_TEMPORAL_FD_CACHE - if (cur_fd->file_nbr == 0) continue; // fd closed + if (cur_fd->file_nbr == 0) continue; // fd closed #endif - if (spix == 0) { // object index header update - if (ev != SPIFFS_EV_IX_DEL) { + if (spix == 0) { // object index header update + if (ev != SPIFFS_EV_IX_DEL) { #if SPIFFS_TEMPORAL_FD_CACHE - if (cur_fd->score == 0) continue; // never used fd + if (cur_fd->score == 0) continue; // never used fd #endif - SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", - SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); - cur_fd->objix_hdr_pix = new_pix; - if (new_size != 0) { - // update size and offsets for fds to this file - cur_fd->size = new_size; - u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", + SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + // update size and offsets for fds to this file + cur_fd->size = new_size; + u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; #if SPIFFS_CACHE_WR - if (act_new_size > 0 && cur_fd->cache_page) { - act_new_size = MAX(act_new_size, cur_fd->cache_page->ucache.swrc.offset + cur_fd->cache_page->ucache.swrc.size); - } + if (act_new_size > 0 && cur_fd->cache_page) { + act_new_size = MAX(act_new_size, cur_fd->cache_page->ucache.swrc.offset + cur_fd->cache_page->ucache.swrc.size); + } #endif - if (cur_fd->offset > act_new_size) { - cur_fd->offset = act_new_size; - } - if (cur_fd->fdoffset > act_new_size) { - cur_fd->fdoffset = act_new_size; - } + if (cur_fd->offset > act_new_size) { + cur_fd->offset = act_new_size; + } + if (cur_fd->fdoffset > act_new_size) { + cur_fd->fdoffset = act_new_size; + } #if SPIFFS_CACHE_WR - if (cur_fd->cache_page && cur_fd->cache_page->ucache.swrc.offset > act_new_size+1) { - SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); - spiffs_cache_fd_release(fs, cur_fd->cache_page); - } + if (cur_fd->cache_page && cur_fd->cache_page->ucache.swrc.offset > act_new_size + 1) { + SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } #endif + } + } else { + // removing file +#if SPIFFS_CACHE_WR + if (cur_fd->file_nbr && cur_fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif + SPIFFS_DBG(" callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } // object index header update + if (cur_fd->cursor_objix_spix == spix) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } } - } else { - // removing file -#if SPIFFS_CACHE_WR - if (cur_fd->file_nbr && cur_fd->cache_page) { - SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); - spiffs_cache_fd_release(fs, cur_fd->cache_page); - } -#endif - SPIFFS_DBG(" callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); - cur_fd->file_nbr = 0; - cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; - } - } // object index header update - if (cur_fd->cursor_objix_spix == spix) { - if (ev != SPIFFS_EV_IX_DEL) { - SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); - cur_fd->cursor_objix_pix = new_pix; - } else { - cur_fd->cursor_objix_pix = 0; - } - } - } // fd update loop + } // fd update loop #if SPIFFS_IX_MAP - // update index maps - if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - // check fd opened, having ix map, match obj id - if (cur_fd->file_nbr == 0 || - cur_fd->ix_map == 0 || - (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; - SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); - spiffs_update_ix_map(fs, cur_fd, spix, objix); + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } } - } #endif - // callback to user if object index header - if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { - spiffs_fileop_type op; - if (ev == SPIFFS_EV_IX_NEW) { - op = SPIFFS_CB_CREATED; - } else if (ev == SPIFFS_EV_IX_UPD || - ev == SPIFFS_EV_IX_MOV || - ev == SPIFFS_EV_IX_UPD_HDR) { - op = SPIFFS_CB_UPDATED; - } else if (ev == SPIFFS_EV_IX_DEL) { - op = SPIFFS_CB_DELETED; - } else { - SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); - return; // bail out + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) { + op = SPIFFS_CB_CREATED; + } else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) { + op = SPIFFS_CB_UPDATED; + } else if (ev == SPIFFS_EV_IX_DEL) { + op = SPIFFS_CB_DELETED; + } else { + SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); } - fs->file_cb_f(fs, op, obj_id, new_pix); - } } // Open object by id @@ -1158,15 +1157,15 @@ s32_t spiffs_object_open_by_id( spiffs_fd *fd, spiffs_flags flags, spiffs_mode mode) { - s32_t res = SPIFFS_OK; - spiffs_page_ix pix; + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; - res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); - SPIFFS_CHECK_RES(res); + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); - res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); - return res; + return res; } // Open object by page index @@ -1176,281 +1175,281 @@ s32_t spiffs_object_open_by_page( spiffs_fd *fd, spiffs_flags flags, spiffs_mode mode) { - (void)mode; - s32_t res = SPIFFS_OK; - spiffs_page_object_ix_header oix_hdr; - spiffs_obj_id obj_id; + (void)mode; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); - SPIFFS_CHECK_RES(res); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); - spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); - int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); - fd->fs = fs; - fd->objix_hdr_pix = pix; - fd->size = oix_hdr.size; - fd->offset = 0; - fd->cursor_objix_pix = pix; - fd->cursor_objix_spix = 0; - fd->obj_id = obj_id; - fd->flags = flags; + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; - SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); - SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); - return res; + return res; } #if !SPIFFS_READ_ONLY // Append to object // keep current object index (header) page in fs->work buffer s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { - spiffs *fs = fd->fs; - s32_t res = SPIFFS_OK; - u32_t written = 0; + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; - SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); + SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); - if (offset > fd->size) { - SPIFFS_DBGF("append: offset reversed to size\n"); - offset = fd->size; - } + if (offset > fd->size) { + SPIFFS_DBGF("append: offset reversed to size\n"); + offset = fd->size; + } - res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta - if (res != SPIFFS_OK) { - SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); - } - SPIFFS_CHECK_RES(res); + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) { + SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); + } + SPIFFS_CHECK_RES(res); - 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_page_header p_hdr; + 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_page_header p_hdr; - spiffs_span_ix cur_objix_spix = 0; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; - spiffs_page_ix new_objix_hdr_page; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; - spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - spiffs_page_ix data_page; - u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); - // write all data - while (res == SPIFFS_OK && written < len) { - // calculate object index page span index - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - // handle storing and loading of object indices - if (cur_objix_spix != prev_objix_spix) { - // new object index page - // within this clause we return directly if something fails, object index mess-up - if (written > 0) { - // store previous object index page, unless first pass - SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, - cur_objix_pix, prev_objix_spix, written); - if (prev_objix_spix == 0) { - // this is an update to object index header page - objix_hdr->size = offset+written; - if (offset == 0) { - // was an empty object, update same page (size was 0xffffffff) - res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); - SPIFFS_CHECK_RES(res); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - } else { - // was a nonempty object, update to new page - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, - new_objix_hdr_page, 0, written); - } - } else { - // this is an update to an object index page - res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); - SPIFFS_CHECK_RES(res); + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset + written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); - // update length in object index header page - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, - offset+written, new_objix_hdr_page, 0, written); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + offset + written, new_objix_hdr_page, 0, written); + } + fd->size = offset + written; + fd->offset = offset + written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size - 1) / SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset + written; + fd->size = offset + written; + } + prev_objix_spix = cur_objix_spix; } - fd->size = offset+written; - fd->offset = offset+written; - } - // create or load new object index page - if (cur_objix_spix == 0) { - // load object index header page, must always exist - SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - } else { - spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); - // on subsequent passes, create a new object index page - if (written > 0 || cur_objix_spix > len_objix_spix) { - p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = cur_objix_spix; - p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); - res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, 0, 0, 0, 1, &cur_objix_pix); - SPIFFS_CHECK_RES(res); - // quick "load" of new object index page - memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); - SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id - , cur_objix_pix, cur_objix_spix, written); + // write data + u32_t to_write = MIN(len - written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); } else { - // on first pass, we load existing object index page - spiffs_page_ix pix; - SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); - if (fd->cursor_objix_spix == cur_objix_spix) { - pix = fd->cursor_objix_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); SPIFFS_CHECK_RES(res); - } - SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - cur_objix_pix = pix; + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); } - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = offset+written; - fd->size = offset+written; - } - prev_objix_spix = cur_objix_spix; - } - // write data - u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); - if (page_offs == 0) { - // at beginning of a page, allocate and write a new page of data - p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = data_spix; - p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, &data[written], to_write, page_offs, 1, &data_page); - SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, - data_page, data_spix, page_offs, to_write, written); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset + written; + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset + written; + fd->offset = offset + written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id + , offset + written, new_objix_hdr_page, 0, written, res2); + SPIFFS_CHECK_RES(res2); } else { - // append to existing page, fill out free data in existing page - if (cur_objix_spix == 0) { - // get data page from object index header page - data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - } else { - // get data page from object index page - data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset + written; + SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); - res = spiffs_page_data_check(fs, fd, data_page, data_spix); - SPIFFS_CHECK_RES(res); + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); - SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id - , data_page, data_spix, page_offs, to_write, written); + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } } - if (res != SPIFFS_OK) break; - - // update memory representation of object index page with new data page - if (cur_objix_spix == 0) { - // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; - SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id - , data_page, data_spix); - objix_hdr->size = offset+written; - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; - SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id - , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } - - // update internals - page_offs = 0; - data_spix++; - written += to_write; - } // while all data - - fd->size = offset+written; - fd->offset = offset+written; - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - - // finalize updated object indices - s32_t res2 = SPIFFS_OK; - if (cur_objix_spix != 0) { - // wrote beyond object index header page - // write last modified object index page, unless object header index page - SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, - cur_objix_pix, cur_objix_spix, written); - - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); - - res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res2); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); - - // update size in object header index page - res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id - , offset+written, new_objix_hdr_page, 0, written, res2); - SPIFFS_CHECK_RES(res2); - } else { - // wrote within object index header page - if (offset == 0) { - // wrote to empty object - simply update size and write whole page - objix_hdr->size = offset+written; - SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id - , cur_objix_pix, cur_objix_spix, written); - - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); - - res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res2); - // callback on object index update - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); - } else { - // modifying object index header page, update size and make new copy - res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id - , new_objix_hdr_page, 0, written); - SPIFFS_CHECK_RES(res2); - } - } - - return res; + return res; } // spiffs_object_append #endif // !SPIFFS_READ_ONLY @@ -1458,204 +1457,204 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // Modify object // keep current object index (header) page in fs->work buffer s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { - spiffs *fs = fd->fs; - s32_t res = SPIFFS_OK; - u32_t written = 0; + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; - res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_CHECK_RES(res); + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); - 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_page_header p_hdr; + 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_page_header p_hdr; - spiffs_span_ix cur_objix_spix = 0; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; - spiffs_page_ix new_objix_hdr_pix; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; - spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - spiffs_page_ix data_pix; - u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); - // write all data - while (res == SPIFFS_OK && written < len) { - // calculate object index page span index - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - // handle storing and loading of object indices - if (cur_objix_spix != prev_objix_spix) { - // new object index page - // within this clause we return directly if something fails, object index mess-up - if (written > 0) { - // store previous object index (header) page, unless first pass - if (prev_objix_spix == 0) { - // store previous object index header page - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); - SPIFFS_CHECK_RES(res); - } else { - // store new version of previous object index page - spiffs_page_ix new_objix_pix; + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; - res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); - SPIFFS_CHECK_RES(res); + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); - res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + res = spiffs_page_move(fs, fd->file_nbr, (u8_t *)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset + written; + prev_objix_spix = cur_objix_spix; } - } - // load next object index page - if (cur_objix_spix == 0) { - // load object index header page, must exist - SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - } else { - // load existing object index page on first pass - spiffs_page_ix pix; - SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); - if (fd->cursor_objix_spix == cur_objix_spix) { - pix = fd->cursor_objix_pix; + // write partial data + u32_t to_write = MIN(len - written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); - SPIFFS_CHECK_RES(res); + // get data page from object index page + orig_data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; } - SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - cur_objix_pix = pix; - } - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = offset+written; - prev_objix_spix = cur_objix_spix; - } - // write partial data - u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); - spiffs_page_ix orig_data_pix; - if (cur_objix_spix == 0) { - // get data page from object index header page - orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - } else { - // get data page from object index page - orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); + } else { + // write to existing page, allocate new and copy unmodified data - p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = data_spix; - p_hdr.flags = 0xff; - if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { - // a full page, allocate and write a new page of data - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); - SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); - } else { - // write to existing page, allocate new and copy unmodified data + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); - res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); - SPIFFS_CHECK_RES(res); + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, 0, 0, 0, 0, &data_pix); - if (res != SPIFFS_OK) break; + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } - // copy unmodified data - if (page_offs > 0) { - // before modification - res = spiffs_phys_cpy(fs, fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), - page_offs); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); if (res != SPIFFS_OK) break; - } - if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { - // after modification - res = spiffs_phys_cpy(fs, fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, - SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, - SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); - if (res != SPIFFS_OK) break; - } + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); - if (res != SPIFFS_OK) break; - p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&p_hdr.flags); - if (res != SPIFFS_OK) break; + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data - SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); - } - - // delete original data page - res = spiffs_page_delete(fs, orig_data_pix); - if (res != SPIFFS_OK) break; - // update memory representation of object index page with new data page - if (cur_objix_spix == 0) { - // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; - SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; - SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } - - // update internals - page_offs = 0; - data_spix++; - written += to_write; - } // while all data - - fd->offset = offset+written; - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - - // finalize updated object indices - s32_t res2 = SPIFFS_OK; - if (cur_objix_spix != 0) { - // wrote beyond object index header page - // write last modified object index page - // move and update page - spiffs_page_ix new_objix_pix; - - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); - - res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); - fd->cursor_objix_pix = new_objix_pix; + fd->offset = offset + written; + fd->cursor_objix_pix = cur_objix_pix; fd->cursor_objix_spix = cur_objix_spix; - SPIFFS_CHECK_RES(res2); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - } else { - // wrote within object index header page - res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); - SPIFFS_CHECK_RES(res2); - } + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; - return res; + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t *)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; } // spiffs_object_modify #endif // !SPIFFS_READ_ONLY @@ -1666,26 +1665,26 @@ static s32_t spiffs_object_find_object_index_header_by_name_v( int ix_entry, const void *user_const_p, void *user_var_p) { - (void)user_var_p; - s32_t res; - spiffs_page_object_ix_header objix_hdr; - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || - (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { - return SPIFFS_VIS_COUNTINUE; - } - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - SPIFFS_CHECK_RES(res); - if (objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { - return SPIFFS_OK; + (void)user_var_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char *)user_const_p, (char *)objix_hdr.name) == 0) { + return SPIFFS_OK; + } } - } - return SPIFFS_VIS_COUNTINUE; + return SPIFFS_VIS_COUNTINUE; } // Finds object index header page by name @@ -1693,34 +1692,34 @@ 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 res; - spiffs_block_ix bix; - int entry; + s32_t res; + spiffs_block_ix bix; + int entry; - res = spiffs_obj_lu_find_entry_visitor(fs, - fs->cursor_block_ix, - fs->cursor_obj_lu_entry, - 0, - 0, - spiffs_object_find_object_index_header_by_name_v, - name, - 0, - &bix, - &entry); + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + name, + 0, + &bix, + &entry); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } - SPIFFS_CHECK_RES(res); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + SPIFFS_CHECK_RES(res); - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } - fs->cursor_block_ix = bix; - fs->cursor_obj_lu_entry = entry; + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; - return res; + return res; } #if !SPIFFS_READ_ONLY @@ -1729,238 +1728,238 @@ s32_t spiffs_object_truncate( spiffs_fd *fd, u32_t new_size, u8_t remove_full) { - s32_t res = SPIFFS_OK; - spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; - if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { - // no op - return res; - } + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { + // no op + return res; + } - // need 2 pages if not removing: object index page + possibly chopped data page - if (remove_full == 0) { - res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); - SPIFFS_CHECK_RES(res); - } - - spiffs_page_ix objix_pix = fd->objix_hdr_pix; - spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); - u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; - spiffs_span_ix cur_objix_spix = 0; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - 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_page_ix data_pix; - spiffs_page_ix new_objix_hdr_pix; - - // before truncating, check if object is to be fully removed and mark this - if (remove_full && new_size == 0) { - u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&flags); - SPIFFS_CHECK_RES(res); - } - - // delete from end of object until desired len is reached - while (cur_size > new_size) { - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - - // put object index for current data span index in work buffer - if (prev_objix_spix != cur_objix_spix) { - if (prev_objix_spix != (spiffs_span_ix)-1) { - // remove previous object index page - SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); - - res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); SPIFFS_CHECK_RES(res); + } - res = spiffs_page_delete(fs, objix_pix); + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size - 1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + 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_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove_full && new_size == 0) { + u8_t flags = ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, - SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); - if (prev_objix_spix > 0) { - // Update object index header page, unless we totally want to remove the file. - // If fully removing, we're not keeping consistency as good as when storing the header between chunks, - // would we be aborted. But when removing full files, a crammed system may otherwise - // report ERR_FULL a la windows. We cannot have that. - // Hence, take the risk - if aborted, a file check would free the lost pages and mend things - // as the file is marked as fully deleted in the beginning. - if (remove_full == 0) { - SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix) - 1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) { + SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); - } - fd->size = cur_size; + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; } - } - // load current object index (header) page - if (cur_objix_spix == 0) { - objix_pix = fd->objix_hdr_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); - SPIFFS_CHECK_RES(res); - } - SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - fd->cursor_objix_pix = objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = cur_size; + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } - prev_objix_spix = cur_objix_spix; - } + SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { + SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); + break; + } + + if (res == SPIFFS_OK) { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) { + SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); + break; + } + } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { + res = SPIFFS_OK; + } + + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices if (cur_objix_spix == 0) { - // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; - } else { - // get data page from object index page - data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; - } - - SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); - - if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { - // delete full data page - res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { - SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); - break; - } - - if (res == SPIFFS_OK) { - res = spiffs_page_delete(fs, data_pix); - if (res != SPIFFS_OK) { - SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); - break; - } - } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { - res = SPIFFS_OK; - } - - // update current size - if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { - cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); - } else { - cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); - } - fd->size = cur_size; - fd->offset = cur_size; - SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); - } else { - // delete last page, partially - spiffs_page_header p_hdr; - spiffs_page_ix new_data_pix; - u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); - - res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - if (res != SPIFFS_OK) break; - - p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = data_spix; - p_hdr.flags = 0xff; - // allocate new page and copy unmodified data - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, 0, 0, 0, 0, &new_data_pix); - if (res != SPIFFS_OK) break; - res = spiffs_phys_cpy(fs, 0, - SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), - SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); - if (res != SPIFFS_OK) break; - // delete original data page - res = spiffs_page_delete(fs, data_pix); - if (res != SPIFFS_OK) break; - p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&p_hdr.flags); - if (res != SPIFFS_OK) break; - - // update memory representation of object index page with new data page - if (cur_objix_spix == 0) { // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; - SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; - SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } - cur_size = new_size; - fd->size = new_size; - fd->offset = cur_size; - break; - } - data_spix--; - } // while all data + if (cur_size == 0) { + if (remove_full) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); - // update object indices - if (cur_objix_spix == 0) { - // update object index header page - if (cur_size == 0) { - if (remove_full) { - // remove object altogether - SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); - res = spiffs_page_index_check(fs, fd, objix_pix, 0); - SPIFFS_CHECK_RES(res); - - res = spiffs_page_delete(fs, objix_pix); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, - SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); - } else { - // make uninitialized object - SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); - memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); - } + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBGF("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } } else { - // update object index header page - SPIFFS_DBGF("truncate: update object index header page with indices and size\n"); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t *)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); } - } else { - // update both current object index page and object index header page - spiffs_page_ix new_objix_pix; + fd->size = cur_size; - res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res); - - // move and update object index page - res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); - fd->cursor_objix_pix = new_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = cur_size; - // update object index header page with new size - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); - } - fd->size = cur_size; - - return res; + return res; } // spiffs_object_truncate #endif // !SPIFFS_READ_ONLY @@ -1969,162 +1968,162 @@ s32_t spiffs_object_read( u32_t offset, u32_t len, u8_t *dst) { - s32_t res = SPIFFS_OK; - spiffs *fs = fd->fs; - spiffs_page_ix objix_pix; - spiffs_page_ix data_pix; - spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - u32_t cur_offset = offset; - spiffs_span_ix cur_objix_spix; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - 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; + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + 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; - while (cur_offset < offset + len) { + while (cur_offset < offset + len) { #if SPIFFS_IX_MAP - // check if we have a memory, index map and if so, if we're within index map's range - // and if so, if the entry is populated - if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix - && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { - data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; - } else { -#endif - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - if (prev_objix_spix != cur_objix_spix) { - // load current object index (header) page - if (cur_objix_spix == 0) { - objix_pix = fd->objix_hdr_pix; + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; } else { - SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); - if (fd->cursor_objix_spix == cur_objix_spix) { - objix_pix = fd->cursor_objix_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); - SPIFFS_CHECK_RES(res); - } - } - SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); - - fd->offset = cur_offset; - fd->cursor_objix_pix = objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - - prev_objix_spix = cur_objix_spix; - } - - if (cur_objix_spix == 0) { - // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - } else { - // get data page from object index page - data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } -#if SPIFFS_IX_MAP - } #endif - // all remaining data - u32_t len_to_read = offset + len - cur_offset; - // remaining data in page - len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); - // remaining data in file - len_to_read = MIN(len_to_read, fd->size - cur_offset); - SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, - (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); - if (len_to_read <= 0) { - res = SPIFFS_ERR_END_OF_OBJECT; - break; - } - res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - SPIFFS_CHECK_RES(res); - res = _spiffs_rd( - fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), - len_to_read, - dst); - SPIFFS_CHECK_RES(res); - dst += len_to_read; - cur_offset += len_to_read; - fd->offset = cur_offset; - data_spix++; - } + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + objix_pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); - return res; + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } +#endif + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size - cur_offset); + SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); + if (len_to_read <= 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; } #if !SPIFFS_READ_ONLY typedef struct { - spiffs_obj_id min_obj_id; - spiffs_obj_id max_obj_id; - u32_t compaction; - const u8_t *conflicting_name; + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; + const u8_t *conflicting_name; } spiffs_free_obj_id_state; static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - const void *user_const_p, void *user_var_p) { - if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { - spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); - const u8_t *conflicting_name = (const u8_t*)user_const_p; + const void *user_const_p, void *user_var_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { + spiffs_obj_id min_obj_id = *((spiffs_obj_id *)user_var_p); + const u8_t *conflicting_name = (const u8_t *)user_const_p; - // if conflicting name parameter is given, also check if this name is found in object index hdrs - if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - int res; - spiffs_page_object_ix_header objix_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - SPIFFS_CHECK_RES(res); - if (objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { - return SPIFFS_ERR_CONFLICTING_NAME; + // if conflicting name parameter is given, also check if this name is found in object index hdrs + if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + int res; + spiffs_page_object_ix_header objix_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char *)user_const_p, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + } } - } - } - id &= ~SPIFFS_OBJ_ID_IX_FLAG; - u32_t bit_ix = (id-min_obj_id) & 7; - int byte_ix = (id-min_obj_id) >> 3; - if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { - fs->work[byte_ix] |= (1<> 3; + if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1 << bit_ix); + } } - } - return SPIFFS_VIS_COUNTINUE; + return SPIFFS_VIS_COUNTINUE; } static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - const void *user_const_p, void *user_var_p) { - (void)user_var_p; - if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) { - s32_t res; - const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state*)user_const_p; - spiffs_page_object_ix_header objix_hdr; + const void *user_const_p, void *user_var_p) { + (void)user_var_p; + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + s32_t res; + const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state *)user_const_p; + spiffs_page_object_ix_header objix_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&objix_hdr); - if (res == SPIFFS_OK && objix_hdr.p_hdr.span_ix == 0 && - ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == - (SPIFFS_PH_FLAG_DELET))) { - // ok object look up entry - if (state->conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { - return SPIFFS_ERR_CONFLICTING_NAME; - } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res == SPIFFS_OK && objix_hdr.p_hdr.span_ix == 0 && + ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == + (SPIFFS_PH_FLAG_DELET))) { + // ok object look up entry + if (state->conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } - id &= ~SPIFFS_OBJ_ID_IX_FLAG; - if (id >= state->min_obj_id && id <= state->max_obj_id) { - u8_t *map = (u8_t *)fs->work; - int ix = (id - state->min_obj_id) / state->compaction; - //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); - map[ix]++; - } + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + if (id >= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } } - } - return SPIFFS_VIS_COUNTINUE; + return SPIFFS_VIS_COUNTINUE; } // Scans thru all object lookup for object index header pages. If total possible number of @@ -2132,218 +2131,218 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id // object ids is found, the object lu is again scanned for object ids within group and bitmasked. // Finally, the bitmask is searched for a free id s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { - s32_t res = SPIFFS_OK; - u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; - spiffs_free_obj_id_state state; - spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; - state.min_obj_id = 1; - state.max_obj_id = max_objects + 1; - if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { - state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; - } - state.compaction = 0; - state.conflicting_name = conflicting_name; - while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { - if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { - // possible to represent in bitmap - u32_t i, j; - SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); - - memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, - conflicting_name, &state.min_obj_id, 0, 0); - if (res == SPIFFS_VIS_END) res = SPIFFS_OK; - SPIFFS_CHECK_RES(res); - // traverse bitmask until found free obj_id - for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { - u8_t mask = fs->work[i]; - if (mask == 0xff) { - continue; - } - for (j = 0; j < 8; j++) { - if ((mask & (1<work; - u8_t min_count = 0xff; - - for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { - if (map[i] < min_count) { - min_count = map[i]; - min_i = i; - if (min_count == 0) { - break; - } - } - } - - if (min_count == state.compaction) { - // there are no free objids! - SPIFFS_DBGF("free_obj_id: compacted table is full\n"); - return SPIFFS_ERR_FULL; - } - - SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); - - if (min_count == 0) { - // no id in this range, skip compacting and use directly - *obj_id = min_i * state.compaction + state.min_obj_id; - return SPIFFS_OK; - } else { - SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); - state.min_obj_id += min_i * state.compaction; - state.max_obj_id = state.min_obj_id + state.compaction; - // decrease compaction - } - if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { - // no need for compacting, use bitmap - continue; - } - } - // in a work memory of log_page_size bytes, we may fit in log_page_size ids - // todo what if compaction is > 255 - then we cannot fit it in a byte - state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); - SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); - - memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); - if (res == SPIFFS_VIS_END) res = SPIFFS_OK; - SPIFFS_CHECK_RES(res); - state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + s32_t res = SPIFFS_OK; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id) - 1) & ~SPIFFS_OBJ_ID_IX_FLAG; } - } + state.compaction = 0; + state.conflicting_name = conflicting_name; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8) { + // possible to represent in bitmap + u32_t i, j; + SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); - return res; + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1 << j)) == 0) { + *obj_id = (i << 3) + j + state.min_obj_id; + return SPIFFS_OK; + } + } + } + return SPIFFS_ERR_FULL; + } else { + // not possible to represent all ids in range in a bitmap, compact and count + if (state.compaction != 0) { + // select element in compacted table, decrease range and recompact + u32_t i, min_i = 0; + u8_t *map = (u8_t *)fs->work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBGF("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id - state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + } + + return res; } #endif // !SPIFFS_READ_ONLY #if SPIFFS_TEMPORAL_FD_CACHE // djb2 hash static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { - (void)fs; - u32_t hash = 5381; - u8_t c; - int i = 0; - while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { - hash = (hash * 33) ^ c; - } - return hash; + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { + hash = (hash * 33) ^ c; + } + return hash; } #endif s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { #if SPIFFS_TEMPORAL_FD_CACHE - u32_t i; - u16_t min_score = 0xffff; - u32_t cand_ix = (u32_t)-1; - u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t) -1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - if (name) { - // first, decrease score of all closed descriptors - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr == 0) { - if (cur_fd->score > 1) { // score == 0 indicates never used fd - cur_fd->score--; - } - } - } - } - - // find the free fd with least score or name match - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr == 0) { - if (name && cur_fd->name_hash == name_hash) { - cand_ix = i; - break; - } - if (cur_fd->score < min_score) { - min_score = cur_fd->score; - cand_ix = i; - } - } - } - - if (cand_ix != (u32_t)-1) { - spiffs_fd *cur_fd = &fds[cand_ix]; if (name) { - if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { - // opened an fd with same name hash, assume same file - // set search point to saved obj index page and hope we have a correct match directly - // when start searching - if not, we will just keep searching until it is found - fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); - fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); - // update score - if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { - cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; - } else { - cur_fd->score = 0xffff; + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (cur_fd->score > 1) { // score == 0 indicates never used fd + cur_fd->score--; + } + } } - } else { - // no hash hit, restore this fd to initial state - cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; - cur_fd->name_hash = name_hash; - } } - cur_fd->file_nbr = cand_ix+1; - *fd = cur_fd; - return SPIFFS_OK; - } else { - return SPIFFS_ERR_OUT_OF_FILE_DESCS; - } + + // find the free fd with least score or name match + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (name && cur_fd->name_hash == name_hash) { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t) -1) { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff - SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } else { + cur_fd->score = 0xffff; + } + } else { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix + 1; + *fd = cur_fd; + return SPIFFS_OK; + } else { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } #else - (void)name; - 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->file_nbr = i+1; - *fd = cur_fd; - return SPIFFS_OK; + (void)name; + 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->file_nbr = i + 1; + *fd = cur_fd; + return SPIFFS_OK; + } } - } - return SPIFFS_ERR_OUT_OF_FILE_DESCS; + return SPIFFS_ERR_OUT_OF_FILE_DESCS; #endif } s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { - if (f <= 0 || f > (s16_t)fs->fd_count) { - return SPIFFS_ERR_BAD_DESCRIPTOR; - } - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - spiffs_fd *fd = &fds[f-1]; - if (fd->file_nbr == 0) { - return SPIFFS_ERR_FILE_CLOSED; - } - fd->file_nbr = 0; + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f - 1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; #if SPIFFS_IX_MAP - fd->ix_map = 0; + fd->ix_map = 0; #endif - return SPIFFS_OK; + return SPIFFS_OK; } s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { - if (f <= 0 || f > (s16_t)fs->fd_count) { - return SPIFFS_ERR_BAD_DESCRIPTOR; - } - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - *fd = &fds[f-1]; - if ((*fd)->file_nbr == 0) { - return SPIFFS_ERR_FILE_CLOSED; - } - return SPIFFS_OK; + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f - 1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; } #if SPIFFS_TEMPORAL_FD_CACHE @@ -2351,15 +2350,15 @@ void spiffs_fd_temporal_cache_rehash( spiffs *fs, const char *old_path, const char *new_path) { - u32_t i; - u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); - u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); - 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->score > 0 && cur_fd->name_hash == old_hash) { - cur_fd->name_hash = new_hash; + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + 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->score > 0 && cur_fd->name_hash == old_hash) { + cur_fd->name_hash = new_hash; + } } - } } #endif diff --git a/armsrc/spiffs_nucleus.h b/armsrc/spiffs_nucleus.h index 255c5aa08..c4a360384 100644 --- a/armsrc/spiffs_nucleus.h +++ b/armsrc/spiffs_nucleus.h @@ -144,15 +144,15 @@ #if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) - /* For GCC, clang and TI compilers */ +/* For GCC, clang and TI compilers */ #define SPIFFS_PACKED __attribute__((packed)) #elif defined(__ICCARM__) || defined(__CC_ARM) - /* For IAR ARM and Keil MDK-ARM compilers */ -#define SPIFFS_PACKED +/* For IAR ARM and Keil MDK-ARM compilers */ +#define SPIFFS_PACKED #else - /* Unknown compiler */ -#define SPIFFS_PACKED +/* Unknown compiler */ +#define SPIFFS_PACKED #endif @@ -338,7 +338,7 @@ 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; +//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; \ @@ -399,39 +399,39 @@ // 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 spix { - // read cache page index - spiffs_page_ix pix; - } spix; + // 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 spix { + // read cache page index + spiffs_page_ix pix; + } spix; #if SPIFFS_CACHE_WR - // type write cache - struct swrc { - // write cache - spiffs_obj_id obj_id; - // offset in cache page - u32_t offset; - // size of cache page - u16_t size; - } swrc; + // type write cache + struct swrc { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + } swrc; #endif - } ucache; + } ucache; } 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; + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; } spiffs_cache; #endif @@ -439,38 +439,38 @@ typedef struct { // 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 (cached) - u32_t fdoffset; - // fd flags - spiffs_flags flags; + // 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 (cached) + u32_t fdoffset; + // fd flags + spiffs_flags flags; #if SPIFFS_CACHE_WR - spiffs_cache_page *cache_page; + 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; + // 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; + // spiffs index map, if 0 it means unmapped + spiffs_ix_map *ix_map; #endif } spiffs_fd; @@ -481,45 +481,46 @@ typedef struct { // 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 SPIFFS_PACKED { - // object id - spiffs_obj_id obj_id; - // object span index - spiffs_span_ix span_ix; - // flags - u8_t flags; + // 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 SPIFFS_PACKED #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES - __attribute(( aligned(sizeof(spiffs_page_ix)) )) +__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]; + // 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]; + // metadata. not interpreted by SPIFFS in any way. + u8_t meta[SPIFFS_OBJ_META_LEN]; #endif -} spiffs_page_object_ix_header; +} +spiffs_page_object_ix_header; // object index page header typedef struct SPIFFS_PACKED { - spiffs_page_header p_hdr; - u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; + 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); + const void *user_const_p, void *user_var_p); #if SPIFFS_CACHE diff --git a/armsrc/string.c b/armsrc/string.c index 02392ec40..ea113680e 100644 --- a/armsrc/string.c +++ b/armsrc/string.c @@ -109,98 +109,89 @@ void itoa(int n, char s[]) { -char *strcpy(char *dst, const char *src) - { - char *save = dst; +char *strcpy(char *dst, const char *src) { + char *save = dst; - for (; (*dst = *src) != '\0'; ++src, ++dst); - return save; - } + for (; (*dst = *src) != '\0'; ++src, ++dst); + return save; +} -char *strncpy(char *dst, const char *src, size_t n) - { - if (n != 0) { - char *d = dst; - const char *s = src; +char *strncpy(char *dst, const char *src, size_t n) { + if (n != 0) { + char *d = dst; + const char *s = src; - do { - if ((*d++ = *s++) == 0) { - /* NUL pad the remaining n-1 bytes */ - while (--n) { + do { + if ((*d++ = *s++) == 0) { + /* NUL pad the remaining n-1 bytes */ + while (--n) { *d++ = 0; - } - break; - } - } while (--n); + } + break; + } + } while (--n); } - return dst; - } + return dst; +} -int strcmp(const char *s1, const char *s2) -{ +int strcmp(const char *s1, const char *s2) { while (*s1 == *s2++) { if (*s1++ == 0) { return (0); } } - return (*(unsigned char *) s1 - *(unsigned char *) --s2); + return (*(unsigned char *) s1 - * (unsigned char *) --s2); } -char* __strtok_r(char*, const char*, char**); +char *__strtok_r(char *, const char *, char **); -char* __strtok_r(char* s, const char* delim, char** last) -{ - char *spanp, *tok; - int c, sc; +char *__strtok_r(char *s, const char *delim, char **last) { + char *spanp, *tok; + int c, sc; - if(s == NULL && (s = *last) == NULL) - return (NULL); + if (s == NULL && (s = *last) == NULL) + return (NULL); -/* - * Skip (span) leading delimiters (s += strspn(s, delim), sort of). - */ + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ cont: - c = *s++; - for(spanp = (char*)delim; (sc = *spanp++) != 0;) - { - if(c == sc) - goto cont; - } + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } - if(c == 0) - { /* no non-delimiter characters */ - *last = NULL; - return (NULL); - } - tok = s - 1; + if (c == 0) { + /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; - /* - * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). - * Note that delim must have one NUL; we stop if we see that, too. - */ - for(;;) - { - c = *s++; - spanp = (char*)delim; - do - { - if((sc = *spanp++) == c) - { - if(c == 0) - s = NULL; - else - s[-1] = '\0'; - *last = s; - return (tok); - } - } while(sc != 0); - } - /* NOTREACHED */ + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = '\0'; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ } -char* strtok(char* s, const char* delim) -{ - static char* last; +char *strtok(char *s, const char *delim) { + static char *last; - return (__strtok_r(s, delim, &last)); + return (__strtok_r(s, delim, &last)); } diff --git a/armsrc/string.h b/armsrc/string.h index 9a899cfc7..c30293a2a 100644 --- a/armsrc/string.h +++ b/armsrc/string.h @@ -24,8 +24,8 @@ char *strcat(char *dest, const char *src); void strreverse(char s[]); void itoa(int n, char s[]); char *strcpy(char *dst, const char *src); -char* strncpy(char* destination, const char* source, size_t num); +char *strncpy(char *destination, const char *source, size_t num); int strcmp(const char *s1, const char *s2); -char* strtok(char* s, const char* delim); +char *strtok(char *s, const char *delim); #endif /* __STRING_H */ diff --git a/client/cmddata.c b/client/cmddata.c index 8a220a39d..7ade24586 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -865,7 +865,7 @@ static int CmdBitsamples(const char *Cmd) { int cnt = 0; uint8_t got[12288]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -1397,7 +1397,7 @@ static int CmdHexsamples(const char *Cmd) { return PM3_EINVARG; } - if (!GetFromDevice(BIG_BUF, got, requested, offset,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ESOFT; } @@ -1470,7 +1470,7 @@ int getSamples(uint32_t n, bool silent) { if (!silent) PrintAndLogEx(NORMAL, "Reading %d bytes from device memory\n", n); PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, got, n, 0,NULL,0, &response, 10000, true)) { + if (!GetFromDevice(BIG_BUF, got, n, 0, NULL, 0, &response, 10000, true)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } diff --git a/client/cmdflashmem.c b/client/cmdflashmem.c index a3a957552..cfe122c50 100644 --- a/client/cmdflashmem.c +++ b/client/cmdflashmem.c @@ -315,7 +315,7 @@ static int CmdFlashMemDump(const char *Cmd) { } PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")"bytes from flashmem", len); - if (!GetFromDevice(FLASH_MEM, dump, len, start_index,NULL,0, NULL, -1, true)) { + if (!GetFromDevice(FLASH_MEM, dump, len, start_index, NULL, 0, NULL, -1, true)) { PrintAndLogEx(FAILED, "ERROR; downloading from flashmemory"); free(dump); return PM3_EFLASH; diff --git a/client/cmdflashmemspiffs.c b/client/cmdflashmemspiffs.c index e06f492a0..066bb6f3f 100644 --- a/client/cmdflashmemspiffs.c +++ b/client/cmdflashmemspiffs.c @@ -216,37 +216,37 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmemspiffs_dump(); - /*case 'l': - len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); - cmdp += 2; - break;*/ - case 'o': - param_getstr(Cmd, cmdp + 1, destfilename, 32); - cmdp += 2; - break; - case 'p': - print = true; - cmdp += 1; - break; - case 'e': - eml = true; - cmdp += 1; - break; - case 'f': - // File handling - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); + case 'h': + return usage_flashmemspiffs_dump(); + /*case 'l': + len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); + cmdp += 2; + break;*/ + case 'o': + param_getstr(Cmd, cmdp + 1, destfilename, 32); + cmdp += 2; + break; + case 'p': + print = true; + cmdp += 1; + break; + case 'e': + eml = true; + cmdp += 1; + break; + case 'f': + // File handling + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; } } @@ -314,24 +314,24 @@ static int CmdFlashMemSpiFFSLoad(const char *Cmd) { while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmemspiffs_load(); - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); + case 'h': + return usage_flashmemspiffs_load(); + case 'f': + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + case 'o': + param_getstr(Cmd, cmdp + 1, destfilename, 32); + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; - } - cmdp += 2; - break; - case 'o': - param_getstr(Cmd, cmdp + 1, destfilename, 32); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; } } @@ -432,8 +432,10 @@ static int CmdFlashMemSpiFFSLoad(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, - "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"}, + { + "copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, + "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)" + }, {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, {"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, @@ -443,7 +445,8 @@ static command_t CommandTable[] = { {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Functionning (require wiping pages 0 and 1)"}, {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, - {NULL, NULL, NULL, NULL}}; + {NULL, NULL, NULL, NULL} +}; static int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far diff --git a/client/cmdhffelica.c b/client/cmdhffelica.c index 3e6001ee1..5a6f1d33a 100644 --- a/client/cmdhffelica.c +++ b/client/cmdhffelica.c @@ -383,7 +383,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { return 1; } - if (!GetFromDevice(BIG_BUF, trace, tracelen, 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(trace); return 0; diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 96b9a0ee5..91757bbd4 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -990,7 +990,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { } // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocksRead * 8, startindex,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocksRead * 8, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return 0; } @@ -1033,7 +1033,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { blocksRead = (sizeof(tag_data) - gotBytes) / 8; } // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocksRead * 8, startindex,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocksRead * 8, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return 0; } diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index 031f5c95a..e8089c3d8 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -781,7 +781,7 @@ int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uin PrintAndLogEx(WARNING, "Fail, only managed to read %u bytes", *outlen); // copy data from device - if (!GetFromDevice(BIG_BUF_EML, out, *outlen, 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, out, *outlen, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); return 4; } @@ -930,7 +930,7 @@ static int CmdLegicDump(const char *Cmd) { PrintAndLogEx(WARNING, "Fail, only managed to read 0x%02X bytes of 0x%02X", readlen, dumplen); // copy data from device - if (!GetFromDevice(BIG_BUF_EML, data, readlen, 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, data, readlen, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(data); return 4; @@ -1200,7 +1200,7 @@ static int CmdLegicESave(const char *Cmd) { // download emulator memory PrintAndLogEx(SUCCESS, "Reading emulator memory..."); - if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(data); return 4; diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index c227b1a83..6e2ca62f5 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2693,7 +2693,7 @@ static int CmdHF14AMfESave(const char *Cmd) { memset(dump, 0, bytes); PrintAndLogEx(INFO, "downloading from emulator memory"); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 935c178f2..df760f187 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -1931,7 +1931,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { bufferSize = sizeof(data); } - if (!GetFromDevice(BIG_BUF, data, bufferSize, startindex,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, data, bufferSize, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return 1; } diff --git a/client/cmdlfcotag.c b/client/cmdlfcotag.c index fa6ff01a8..134d55c68 100644 --- a/client/cmdlfcotag.c +++ b/client/cmdlfcotag.c @@ -91,7 +91,7 @@ static int CmdCOTAGRead(const char *Cmd) { } case 1: { - if (!GetFromDevice(BIG_BUF, DemodBuffer, COTAG_BITS, 0,NULL,0, NULL, 1000, false)) { + if (!GetFromDevice(BIG_BUF, DemodBuffer, COTAG_BITS, 0, NULL, 0, NULL, 1000, false)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index b594eb4bc..eb8de8f86 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -986,7 +986,7 @@ static bool downloadSamplesEM() { // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) uint8_t got[6000]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return false; } diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 1af8defe4..8acfc69c7 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -1804,7 +1804,7 @@ static int CmdResetRead(const char *Cmd) { } uint8_t got[BIGBUF_SIZE - 1]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } diff --git a/client/cmdtrace.c b/client/cmdtrace.c index e9a609184..3a2ea9aef 100644 --- a/client/cmdtrace.c +++ b/client/cmdtrace.c @@ -759,7 +759,7 @@ int CmdTraceList(const char *Cmd) { if (isOnline) { // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, trace, PM3_CMD_DATA_SIZE, 0,NULL,0, &response, 4000, true)) { + if (!GetFromDevice(BIG_BUF, trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &response, 4000, true)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return 1; } @@ -773,7 +773,7 @@ int CmdTraceList(const char *Cmd) { return 2; } trace = p; - if (!GetFromDevice(BIG_BUF, trace, traceLen, 0,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, trace, traceLen, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(trace); return 3; diff --git a/client/comms.c b/client/comms.c index 45771c60d..00c6a0f20 100644 --- a/client/comms.c +++ b/client/comms.c @@ -761,9 +761,9 @@ bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint3 return dl_it(dest, bytes, start_index, response, ms_timeout, show_warning, CMD_DOWNLOADED_EML_BIGBUF); } case SPIFFS: { - SendCommandMIX(CMD_SPIFFS_DOWNLOAD, start_index, bytes, 0, data, datalen); + SendCommandMIX(CMD_SPIFFS_DOWNLOAD, start_index, bytes, 0, data, datalen); return dl_it(dest, bytes, start_index, response, ms_timeout, show_warning, CMD_SPIFFS_DOWNLOADED); - } + } case FLASH_MEM: { SendCommandMIX(CMD_FLASHMEM_DOWNLOAD, start_index, bytes, 0, NULL, 0); return dl_it(dest, bytes, start_index, response, ms_timeout, show_warning, CMD_FLASHMEM_DOWNLOADED); diff --git a/client/flash.c b/client/flash.c index 316b53066..f2f0b526a 100644 --- a/client/flash.c +++ b/client/flash.c @@ -415,7 +415,7 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t version = BL_VERSION_INVALID; PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported an invalid version number")); flash_suggest_update_bootloader(); - // + // } else if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(FLASHER_VERSION)) { PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported a version older than this flasher")); flash_suggest_update_bootloader(); @@ -448,7 +448,7 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new CHIP_INFO command")); flash_suggest_update_bootloader(); } - + if (enable_bl_writes) { PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", FLASH_START, flash_end); } else { diff --git a/client/scripting.c b/client/scripting.c index 9e735b0db..e55aa507a 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -223,7 +223,7 @@ static int l_GetFromBigBuf(lua_State *L) { return returnToLuaWithError(L, "Allocating memory failed"); } - if (!GetFromDevice(BIG_BUF, data, len, startindex,NULL,0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, data, len, startindex, NULL, 0, NULL, 2500, false)) { free(data); return returnToLuaWithError(L, "command execution time out"); } @@ -263,7 +263,7 @@ static int l_GetFromFlashMem(lua_State *L) { if (!data) return returnToLuaWithError(L, "Allocating memory failed"); - if (!GetFromDevice(FLASH_MEM, data, len, startindex,NULL,0, NULL, -1, false)) { + if (!GetFromDevice(FLASH_MEM, data, len, startindex, NULL, 0, NULL, -1, false)) { free(data); return returnToLuaWithError(L, "command execution time out"); } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 99a858ff0..3de6688fd 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -238,8 +238,8 @@ typedef struct { #define CMD_FLASHMEM_INFO 0x0125 #define CMD_FLASHMEM_SET_SPIBAUDRATE 0x0126 -// RDV40, High level flashmem SPIFFS Manipulation -// ALL function will have a lazy or Safe version +// RDV40, High level flashmem SPIFFS Manipulation +// ALL function will have a lazy or Safe version // that will be handled as argument of safety level [0..2] respectiveley normal / lazy / safe // However as how design is, MOUNT and UNMOUNT only need/have lazy as safest level so a safe level will still execute a lazy version // see spiffs.c for more about the normal/lazy/safety information) From 544fb30ce68ed401792f3219d61b8a026e05e0ad Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 21:40:01 +0200 Subject: [PATCH 34/40] typos --- armsrc/iso14443a.c | 2 +- armsrc/iso15693.c | 2 +- armsrc/spiffs.c | 6 +++--- armsrc/spiffs.h | 2 +- client/cmdflashmemspiffs.c | 2 +- client/scripts/legic.lua | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 734513b54..a09978d7a 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -93,7 +93,7 @@ static uint32_t LastProxToAirDuration; // Sequence D: 11110000 modulation with subcarrier during first half // Sequence E: 00001111 modulation with subcarrier during second half // Sequence F: 00000000 no modulation with subcarrier -// Sequence COLL: 11111111 load modulation over the full bitlenght. +// Sequence COLL: 11111111 load modulation over the full bitlength. // Tricks the reader to think that multiple cards answer (at least one card with 1 and at least one card with 0). // READER TO CARD - miller // Sequence X: 00001100 drop after half a period diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 374b2be23..e74ca972d 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -695,7 +695,7 @@ static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) { // speed ... 0 low speed, 1 hi speed // **recv will return you a pointer to the received data // If you do not need the answer use NULL for *recv[] -// return: lenght of received data +// return: length of received data // logging enabled int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *outdata) { diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 0fecc556c..1439bfd20 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -208,7 +208,7 @@ rdv40_spiffs_fsinfo info_of_spiffs() { fsinfo.blockSize = SPIFFS_CFG_LOG_BLOCK_SZ; fsinfo.pageSize = LOG_PAGE_SIZE; fsinfo.maxOpenFiles = RDV40_SPIFFS_MAX_FD; - fsinfo.maxPathLenght = SPIFFS_OBJ_NAME_LEN; + fsinfo.maxPathLength = SPIFFS_OBJ_NAME_LEN; if (SPIFFS_info(&fs, &fsinfo.totalBytes, &fsinfo.usedBytes) < 0) Dbprintf("errno %i\n", SPIFFS_errno(&fs)); fsinfo.freeBytes = fsinfo.totalBytes - fsinfo.usedBytes; @@ -532,7 +532,7 @@ void rdv40_spiffs_safe_print_fsinfos() { Dbprintf("* Filesystem Logical Page Size..........%d bytes", fsinfo.pageSize); Dbprintf("--"); Dbprintf("* Filesystem Max Open Files.............%d file descriptors", fsinfo.maxOpenFiles); - Dbprintf("* Filesystem Max Path Lenght............%d chars", fsinfo.maxPathLenght); + Dbprintf("* Filesystem Max Path Length............%d chars", fsinfo.maxPathLength); Dbprintf("--"); Dbprintf("Filesystem\tSize\tUsed\tAvailable\tUse%\tMounted on"); Dbprintf("spiffs\t%dB\t%dB\t%dB\t\t%d%\t/", fsinfo.totalBytes, fsinfo.usedBytes, fsinfo.freeBytes, @@ -580,7 +580,7 @@ void rdv40_spiffs_safe_print_tree(uint8_t banner) { void test_spiffs() { Dbprintf("---------------------------"); - Dbprintf("Testing SPIFFS functionning"); + Dbprintf("Testing SPIFFS operations"); Dbprintf("---------------------------"); Dbprintf("(all test are made using lazy safetylevel)"); Dbprintf("* Mounting filesystem (lazy)......."); diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index b9308f4dd..e79783402 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -26,7 +26,7 @@ typedef struct rdv40_spiffs_fsinfo { uint32_t blockSize; uint32_t pageSize; uint32_t maxOpenFiles; - uint32_t maxPathLenght; + uint32_t maxPathLength; uint32_t totalBytes, usedBytes, freeBytes; uint32_t usedPercent, freePercent; } rdv40_spiffs_fsinfo; diff --git a/client/cmdflashmemspiffs.c b/client/cmdflashmemspiffs.c index 066bb6f3f..bc7a408bd 100644 --- a/client/cmdflashmemspiffs.c +++ b/client/cmdflashmemspiffs.c @@ -442,7 +442,7 @@ static command_t CommandTable[] = { {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Functionning (require wiping pages 0 and 1)"}, + {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"}, {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, {NULL, NULL, NULL, NULL} diff --git a/client/scripts/legic.lua b/client/scripts/legic.lua index 9637ef6c8..7e0f8d3be 100644 --- a/client/scripts/legic.lua +++ b/client/scripts/legic.lua @@ -2654,7 +2654,7 @@ function modifyMode() local stamp=getSegmentStamp(inTAG.SEG[x]) print("Stamp : "..stamp) stamp=str2bytes(stamp) - print("lenght: "..#stamp) + print("length: "..#stamp) end, --- -- calculate crc16 From 7e84a546c5cbda12a9654094b4da6a6b7b163381 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 21:45:06 +0200 Subject: [PATCH 35/40] GetFromDevice leftovers --- client/cmdlfhitag.c | 4 ++-- client/comms.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 69ba874c1..0fc4b053c 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -137,7 +137,7 @@ static int CmdLFHitagList(const char *Cmd) { // Query for the actual size of the trace PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, got, PM3_CMD_DATA_SIZE, 0, &response, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, PM3_CMD_DATA_SIZE, 0, NULL, 0, &response, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(got); return 2; @@ -152,7 +152,7 @@ static int CmdLFHitagList(const char *Cmd) { return 2; } got = p; - if (!GetFromDevice(BIG_BUF, got, traceLen, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, traceLen, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(got); return 2; diff --git a/client/comms.c b/client/comms.c index 00c6a0f20..ef00ebc44 100644 --- a/client/comms.c +++ b/client/comms.c @@ -734,6 +734,8 @@ bool WaitForResponse(uint32_t cmd, PacketResponseNG *response) { * @param dest Destination address for transfer * @param bytes number of bytes to be transferred * @param start_index offset into Proxmark3 BigBuf[] +* @param data used by SPIFFS to provide filename +* @param datalen used by SPIFFS to provide filename length * @param response struct to copy last command (CMD_ACK) into * @param ms_timeout timeout in milliseconds * @param show_warning display message after 2 seconds From 7f57879e4583cfc30546a09c1acbe66f8c3d3cbb Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 21:48:09 +0200 Subject: [PATCH 36/40] typo --- armsrc/appmain.c | 2 +- armsrc/spiffs.c | 4 ++-- armsrc/spiffs.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 7128c4a2f..c47a96528 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1607,7 +1607,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_SPIFFS_PRINT_FSINFO: { - rdv40_spiffs_safe_print_fsinfos(); + rdv40_spiffs_safe_print_fsinfo(); break; } case CMD_SPIFFS_DOWNLOAD: { diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 1439bfd20..7fd712e7f 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -523,10 +523,10 @@ int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, R ///////// MISC HIGH LEVEL FUNCTIONS //////////////////////////////////////////// -void rdv40_spiffs_safe_print_fsinfos() { +void rdv40_spiffs_safe_print_fsinfo() { rdv40_spiffs_fsinfo fsinfo; rdv40_spiffs_getfsinfo(&fsinfo, RDV40_SPIFFS_SAFETY_SAFE); - DbpString(_BLUE_("Flash Memory FileSystem Infos (SPIFFS)")); + DbpString(_BLUE_("Flash Memory FileSystem Info (SPIFFS)")); Dbprintf("-------------------------------------"); Dbprintf("* Filesystem Logical Block Size.........%d bytes", fsinfo.blockSize); Dbprintf("* Filesystem Logical Page Size..........%d bytes", fsinfo.pageSize); diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index e79783402..22d7434de 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -49,7 +49,7 @@ void rdv40_spiffs_safe_print_tree(uint8_t banner); int rdv40_spiffs_unmount(); int rdv40_spiffs_mount(); int rdv40_spiffs_is_symlink(const char *s); -void rdv40_spiffs_safe_print_fsinfos(); +void rdv40_spiffs_safe_print_fsinfo(); int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level); void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size); int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level); From ac18114909cd70a711c9c432941a82cc1b69b32b Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 22:46:58 +0200 Subject: [PATCH 37/40] Remove long unsigned int warning --- client/cmdlft55xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 7b8a77487..618ab8c7c 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -2606,7 +2606,7 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { // printf ("DLmode %d\n",downlink_mode); t55xx_config conf = {0}; - printf ("Size conf %lld\n",sizeof(conf)); + printf ("Size conf %zu\n",sizeof(conf)); if (erase) { memset (&conf,0xff, sizeof(conf)); printf ("Conf.m[0] %x\n",conf.m[0].start_gap); From ee40840a141881fa3fecd0584573b10f389015ce Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 23:12:18 +0200 Subject: [PATCH 38/40] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 622293abf..76b3db976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Add T55x7 Downlink mode support (@mwalker33) + - Add SPIFFS Flash filesystem support (@cjbrigato) - Fix support for flashing 512K units with old bootrom (@slurdge/@doegox) - Fix 'hf mf sim' - wrong access rights to write key B in trailer (@McEloff) - Add option -i to flasher to query Pm3 for its memory size (@doegox) From ac233a346a70a55832dfbe3365769b66eac6f486 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 23:43:30 +0200 Subject: [PATCH 39/40] make style --- armsrc/appmain.c | 10 +- armsrc/apps.h | 2 +- armsrc/lfops.c | 676 +++++++++++++++++++++++--------------------- client/cmdlft55xx.c | 647 +++++++++++++++++++++--------------------- client/scripting.c | 6 +- include/pm3_cmd.h | 18 +- 6 files changed, 689 insertions(+), 670 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 80d0f9316..02829c20c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -880,7 +880,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t downlink_mode; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password,payload->downlink_mode); + T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password, payload->downlink_mode); break; } case CMD_T55XX_WRITE_BLOCK: { @@ -889,15 +889,15 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_T55XX_WAKEUP: { - T55xxWakeUp(packet->oldarg[0],packet->oldarg[1]); - break; + T55xxWakeUp(packet->oldarg[0], packet->oldarg[1]); + break; } case CMD_T55XX_RESET_READ: { - T55xxResetRead(packet->data.asBytes[0]&0xff); + T55xxResetRead(packet->data.asBytes[0] & 0xff); break; } case CMD_T55XX_CHKPWDS: { - T55xx_ChkPwds(packet->data.asBytes[0]&0xff); + T55xx_ChkPwds(packet->data.asBytes[0] & 0xff); break; } case CMD_PCF7931_READ: { diff --git a/armsrc/apps.h b/armsrc/apps.h index a508b74b0..e3ea8048c 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -106,7 +106,7 @@ void T55xxResetRead(uint8_t flags); //id T55xxWriteBlock(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); void T55xxWriteBlock(uint8_t *data); // void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); -void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd,uint8_t downlink_mode); +void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd, uint8_t downlink_mode); void T55xxWakeUp(uint32_t Pwd, uint8_t flags); void T55xx_ChkPwds(uint8_t flags); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 17ac49dd2..26a494cf2 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -47,13 +47,13 @@ // 1fc = 8us = 12ticks /* ========================================================================================================== - T55x7 Timing + T55x7 Timing ========================================================================================================== // t55xx_config t_config = { 29 * 8, 17 * 8, 15 * 8, 47 * 8, 15 * 8 } ; - ATA5577 Downlink Protocol Timings. - Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz) + ATA5577 Downlink Protocol Timings. + Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz) ----------------------------------------------------------------------- Fixed-bit-length Protocol | Normal Downlink | Fast Downlink | ------------------------------+-----------------------------------+-----------------------------------+------ @@ -67,7 +67,7 @@ ------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------- - Long Leading Reference | Normal Downlink | Fast Downlink | + Long Leading Reference | Normal Downlink | Fast Downlink | ------------------------------+-----------------------------------+-----------------------------------+------ | Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | |-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| @@ -76,7 +76,7 @@ |-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| | Write | Ref | | 152 | 160 | 168 | 140 | 144 | 148 | Tc | | data | Pulse | dref | 136 clocks + 0 data bit | 132 clocks + 0 data bit | Tc | -| coding |--------+---------+-----------------------------------+-----------------------------------+------| +| coding |--------+---------+-----------------------------------+-----------------------------------+------| | | 0 data | d0 |dref – 143 |dref – 136 |dref – 128 |dref – 135 |dref – 132 |dref – 124 | Tc | | | 1 data | d1 |dref – 111 |dref – 104 |dref – 96 |dref – 119 |dref – 116 |dref – 112 | Tc | ------------------------------------------------------------------------------------------------------------- @@ -113,16 +113,22 @@ /* // Note: Moved * 8 to apply when used. Saving 28 bytes here (- the *8) and 28 bytes flash. // StartGap WriteGap Bit 0/00 Bit 1/01 Bit 10 Bit 11 ReadGap -t55xx_config T55xx_Timing = {{{ 29 , 17 , 15 , 50 , 0 , 0 , 15 }, // Default Fixed - { 31 , 20 , 18 , 50 , 0 , 0 , 15 }, // Long Leading Ref. - { 31 , 20 , 18 , 40 , 0 , 0 , 15 }, // Leading 0 - { 29 , 17 , 15 , 31 , 47 , 63 , 15 } }}; // 1 of 4 +t55xx_config T55xx_Timing = {{ + { 29 , 17 , 15 , 50 , 0 , 0 , 15 }, // Default Fixed + { 31 , 20 , 18 , 50 , 0 , 0 , 15 }, // Long Leading Ref. + { 31 , 20 , 18 , 40 , 0 , 0 , 15 }, // Leading 0 + { 29 , 17 , 15 , 31 , 47 , 63 , 15 } // 1 of 4 + } +}; */ // StartGap WriteGap Bit 0/00 Bit 1/01 Bit 10 Bit 11 ReadGap -t55xx_config T55xx_Timing = {{{ 29 * 8 , 17 * 8 , 15 * 8 , 50 * 8 , 0 , 0 , 15 * 8 }, // Default Fixed - { 31 * 8 , 20 * 8 , 18 * 8 , 50 * 8 , 0 , 0 , 15 * 8 }, // Long Leading Ref. - { 31 * 8 , 20 * 8 , 18 * 8 , 40 * 8 , 0 , 0 , 15 * 8 }, // Leading 0 - { 29 * 8 , 17 * 8 , 15 * 8 , 31 * 8 , 47 * 8, 63 * 8, 15 * 8 } }}; // 1 of 4 +t55xx_config T55xx_Timing = {{ + { 29 * 8, 17 * 8, 15 * 8, 50 * 8, 0, 0, 15 * 8 }, // Default Fixed + { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 0, 0, 15 * 8 }, // Long Leading Ref. + { 31 * 8, 20 * 8, 18 * 8, 40 * 8, 0, 0, 15 * 8 }, // Leading 0 + { 29 * 8, 17 * 8, 15 * 8, 31 * 8, 47 * 8, 63 * 8, 15 * 8 } // 1 of 4 + } +}; // Some defines for readability @@ -133,48 +139,55 @@ t55xx_config T55xx_Timing = {{{ 29 * 8 , 17 * 8 , 15 * 8 , 50 * 8 , 0 , 0 #define T55xx_LongLeadingReference 4 // Value to tell Write Bit to send long reference void printT55xxConfig(void) { - int DLMode; - + int DLMode; + DbpString(_BLUE_("LF T55XX config")); - for (DLMode = 0; DLMode < 4; DLMode++) { - switch (DLMode){ - case T55xx_DLMode_Fixed : Dbprintf("r 0 fixed bit length (default)"); break; - case T55xx_DLMode_LLR : Dbprintf("r 1 long leading reference"); break; - case T55xx_DLMode_Leading0 : Dbprintf("r 2 leading zero"); break; - case T55xx_DLMode_1of4 : Dbprintf("r 3 1 of 4 coding reference"); break; - } - Dbprintf(" [a] startgap............%d*8 (%d)", T55xx_Timing.m[DLMode].start_gap / 8, T55xx_Timing.m[DLMode].start_gap); - Dbprintf(" [b] writegap............%d*8 (%d)", T55xx_Timing.m[DLMode].write_gap / 8, T55xx_Timing.m[DLMode].write_gap); - Dbprintf(" [c] write_0.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_0 / 8, T55xx_Timing.m[DLMode].write_0 ); - Dbprintf(" [d] write_1.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_1 / 8, T55xx_Timing.m[DLMode].write_1 ); - if (DLMode == T55xx_DLMode_1of4) { - Dbprintf(" [e] write_2.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_2 / 8, T55xx_Timing.m[DLMode].write_2); - Dbprintf(" [f] write_3.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_3 / 8, T55xx_Timing.m[DLMode].write_3); - } - Dbprintf(" [g] readgap.............%d*8 (%d)", T55xx_Timing.m[DLMode].read_gap / 8, T55xx_Timing.m[DLMode].read_gap); - } + for (DLMode = 0; DLMode < 4; DLMode++) { + switch (DLMode) { + case T55xx_DLMode_Fixed : + Dbprintf("r 0 fixed bit length (default)"); + break; + case T55xx_DLMode_LLR : + Dbprintf("r 1 long leading reference"); + break; + case T55xx_DLMode_Leading0 : + Dbprintf("r 2 leading zero"); + break; + case T55xx_DLMode_1of4 : + Dbprintf("r 3 1 of 4 coding reference"); + break; + } + Dbprintf(" [a] startgap............%d*8 (%d)", T55xx_Timing.m[DLMode].start_gap / 8, T55xx_Timing.m[DLMode].start_gap); + Dbprintf(" [b] writegap............%d*8 (%d)", T55xx_Timing.m[DLMode].write_gap / 8, T55xx_Timing.m[DLMode].write_gap); + Dbprintf(" [c] write_0.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_0 / 8, T55xx_Timing.m[DLMode].write_0); + Dbprintf(" [d] write_1.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_1 / 8, T55xx_Timing.m[DLMode].write_1); + if (DLMode == T55xx_DLMode_1of4) { + Dbprintf(" [e] write_2.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_2 / 8, T55xx_Timing.m[DLMode].write_2); + Dbprintf(" [f] write_3.............%d*8 (%d)", T55xx_Timing.m[DLMode].write_3 / 8, T55xx_Timing.m[DLMode].write_3); + } + Dbprintf(" [g] readgap.............%d*8 (%d)", T55xx_Timing.m[DLMode].read_gap / 8, T55xx_Timing.m[DLMode].read_gap); + } } void setT55xxConfig(uint8_t arg0, t55xx_config *c) { - uint8_t DLMode; -// uint8_t ClearT55Settings = c->m[0].start_gap & 0xffff; // all values will be ffff if clear requested - - - for (DLMode = 0; DLMode < 4; DLMode++) { - if (c->m[DLMode].start_gap != 0) T55xx_Timing.m[DLMode].start_gap = c->m[DLMode].start_gap;// * 8; - if (c->m[DLMode].write_gap != 0) T55xx_Timing.m[DLMode].write_gap = c->m[DLMode].write_gap;// * 8; - if (c->m[DLMode].write_0 != 0) T55xx_Timing.m[DLMode].write_0 = c->m[DLMode].write_0 ;// * 8; - if (c->m[DLMode].write_1 != 0) T55xx_Timing.m[DLMode].write_1 = c->m[DLMode].write_1 ;// * 8; - if (DLMode == T55xx_DLMode_1of4) { - if (c->m[DLMode].write_2 != 0) T55xx_Timing.m[DLMode].write_2 = c->m[DLMode].write_2;// * 8; - if (c->m[DLMode].write_3 != 0) T55xx_Timing.m[DLMode].write_3 = c->m[DLMode].write_3;// * 8 ; - } - else{ - T55xx_Timing.m[DLMode].write_2 = 0x00; - T55xx_Timing.m[DLMode].write_3 = 0x00; - } - if (c->m[DLMode].read_gap != 0) T55xx_Timing.m[DLMode].read_gap = c->m[DLMode].read_gap;//* 8; - } + uint8_t DLMode; +// uint8_t ClearT55Settings = c->m[0].start_gap & 0xffff; // all values will be ffff if clear requested + + + for (DLMode = 0; DLMode < 4; DLMode++) { + if (c->m[DLMode].start_gap != 0) T55xx_Timing.m[DLMode].start_gap = c->m[DLMode].start_gap;// * 8; + if (c->m[DLMode].write_gap != 0) T55xx_Timing.m[DLMode].write_gap = c->m[DLMode].write_gap;// * 8; + if (c->m[DLMode].write_0 != 0) T55xx_Timing.m[DLMode].write_0 = c->m[DLMode].write_0 ;// * 8; + if (c->m[DLMode].write_1 != 0) T55xx_Timing.m[DLMode].write_1 = c->m[DLMode].write_1 ;// * 8; + if (DLMode == T55xx_DLMode_1of4) { + if (c->m[DLMode].write_2 != 0) T55xx_Timing.m[DLMode].write_2 = c->m[DLMode].write_2;// * 8; + if (c->m[DLMode].write_3 != 0) T55xx_Timing.m[DLMode].write_3 = c->m[DLMode].write_3;// * 8 ; + } else { + T55xx_Timing.m[DLMode].write_2 = 0x00; + T55xx_Timing.m[DLMode].write_3 = 0x00; + } + if (c->m[DLMode].read_gap != 0) T55xx_Timing.m[DLMode].read_gap = c->m[DLMode].read_gap;//* 8; + } printT55xxConfig(); @@ -188,7 +201,7 @@ void setT55xxConfig(uint8_t arg0, t55xx_config *c) { return; } - + uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN); Flash_CheckBusy(BUSY_TIMEOUT); uint16_t res = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); @@ -198,21 +211,21 @@ void setT55xxConfig(uint8_t arg0, t55xx_config *c) { return; } - // if ( ClearT55Settings) // dont copy over new timings + // if ( ClearT55Settings) // dont copy over new timings memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN); - - Flash_CheckBusy(BUSY_TIMEOUT); + + Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xD); - // if not a settings erase, write data - // if ( ClearT55Settings) { - res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); + // if not a settings erase, write data + // if ( ClearT55Settings) { + res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - if (res == T55XX_CONFIG_LEN && DBGLEVEL > 1) { + if (res == T55XX_CONFIG_LEN && DBGLEVEL > 1) { DbpString("T55XX Config save success"); - } -// } + } + // } BigBuf_free(); #endif @@ -246,8 +259,8 @@ void loadT55xxConfig(void) { return; } - if (buf[0] != 0xFF) // if not set for clear - memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN); + if (buf[0] != 0xFF) // if not set for clear + memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN); if (isok == T55XX_CONFIG_LEN) { if (DBGLEVEL > 1) DbpString("T55XX Config load success"); @@ -1470,7 +1483,7 @@ void TurnReadLF_off(uint32_t delay) { #define BitStream_Byte(X) ((X) >> 3) #define BitStream_Bit(X) ((X) & 7) #define t55_send_PwdMode (arg & 0x01) -#define t55_send_Page ((arg & 0x02) >> 1) +#define t55_send_Page ((arg & 0x02) >> 1) #define t55_send_TestMode ((arg & 0x04) >> 2) #define t55_send_RegReadMode ((arg & 0x20) >> 5) #define t55_send_ReadCmd ((arg & 0x40) >> 6) @@ -1479,18 +1492,28 @@ void TurnReadLF_off(uint32_t delay) { // Write one bit to chip void T55xxWriteBit(uint8_t bit, uint8_t downlink_idx) { - // Dbprintf ("%d",bit); - // If bit = 4 Send Long Leading Reference which is (138*8) + WRITE_0 - switch (bit){ - case 0 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0 ); break; // Send bit 0/00 - case 1 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_1 ); break; // Send bit 1/01 - case 2 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_2 ); break; // Send bits 10 (1 of 4) - case 3 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_3 ); break; // Send bits 11 (1 of 4) - case 4 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0 + (136 * 8)); break; // Send Long Leading Reference - } + // Dbprintf ("%d",bit); + // If bit = 4 Send Long Leading Reference which is (138*8) + WRITE_0 + switch (bit) { + case 0 : + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0); + break; // Send bit 0/00 + case 1 : + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_1); + break; // Send bit 1/01 + case 2 : + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_2); + break; // Send bits 10 (1 of 4) + case 3 : + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_3); + break; // Send bits 11 (1 of 4) + case 4 : + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0 + (136 * 8)); + break; // Send Long Leading Reference + } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(T55xx_Timing.m[downlink_idx].write_gap ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(T55xx_Timing.m[downlink_idx].write_gap); } // Function to abstract an Arbitrary length byte array to store bit pattern. @@ -1500,152 +1523,147 @@ void T55xxWriteBit(uint8_t bit, uint8_t downlink_idx) { // num_bits - how many bits (low x bits of data) Max 32 bits at a time // max_len - how many bytes can the bit_array hold (ensure no buffer overflow) // returns "Next" bit offset / bits stored (for next store) -uint8_t T55xx_SetBits (uint8_t *BitStream, uint8_t start_offset, uint32_t data , uint8_t num_bits, uint8_t max_len) -{ - int8_t offset; - int8_t NextOffset = start_offset; +uint8_t T55xx_SetBits(uint8_t *BitStream, uint8_t start_offset, uint32_t data, uint8_t num_bits, uint8_t max_len) { + int8_t offset; + int8_t NextOffset = start_offset; - // Check if data will fit. - if ((start_offset + num_bits) <= (max_len*8)) { - // Loop through the data and store - for (offset = (num_bits-1); offset >= 0; offset--) { + // Check if data will fit. + if ((start_offset + num_bits) <= (max_len * 8)) { + // Loop through the data and store + for (offset = (num_bits - 1); offset >= 0; offset--) { - if ((data >> offset) & 1) BitStream[BitStream_Byte(NextOffset)] |= (1 << BitStream_Bit(NextOffset)); // Set the bit to 1 - else BitStream[BitStream_Byte(NextOffset)] &= (0xff ^ (1 << BitStream_Bit(NextOffset))); // Set the bit to 0 + if ((data >> offset) & 1) BitStream[BitStream_Byte(NextOffset)] |= (1 << BitStream_Bit(NextOffset)); // Set the bit to 1 + else BitStream[BitStream_Byte(NextOffset)] &= (0xff ^ (1 << BitStream_Bit(NextOffset))); // Set the bit to 0 - NextOffset++; - } - } - else { - // Note: This should never happen unless some code changes cause it. - // So short message for coders when testing. - Dbprintf ("T55 too many bits"); - } - return NextOffset; + NextOffset++; + } + } else { + // Note: This should never happen unless some code changes cause it. + // So short message for coders when testing. + Dbprintf("T55 too many bits"); + } + return NextOffset; } -// Send one downlink command to the card -// void T55xx_SendCMD (uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t arg) { -void T55xx_SendCMD (uint32_t Data, uint32_t Pwd, uint16_t arg) { +// Send one downlink command to the card +// void T55xx_SendCMD (uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t arg) { +void T55xx_SendCMD(uint32_t Data, uint32_t Pwd, uint16_t arg) { - /* - arg bits - xxxx xxxxxxx1 0x001 PwdMode - xxxx xxxxxx1x 0x002 Page - xxxx xxxxx1xx 0x004 testMode - xxxx xxx11xxx 0x018 downlink mode - xxxx xx1xxxxx 0x020 !reg_readmode - xxxx x1xxxxxx 0x040 called for a read, so no data packet - xxxx 1xxxxxxx 0x080 reset - xxx1 xxxxxxxx 0x100 brute force - 111x xxxxxxxx 0xE00 Block - - */ + /* + arg bits + xxxx xxxxxxx1 0x001 PwdMode + xxxx xxxxxx1x 0x002 Page + xxxx xxxxx1xx 0x004 testMode + xxxx xxx11xxx 0x018 downlink mode + xxxx xx1xxxxx 0x020 !reg_readmode + xxxx x1xxxxxx 0x040 called for a read, so no data packet + xxxx 1xxxxxxx 0x080 reset + xxx1 xxxxxxxx 0x100 brute force + 111x xxxxxxxx 0xE00 Block + */ - uint8_t downlink_mode = (arg >> 3) & 0x03; - uint8_t i = 0; - uint8_t BitStream[10]; // Max Downlink Command size ~74 bits, so 10 bytes (80 bits) - uint8_t BitStreamLen = 0; - uint8_t SendBits; - uint8_t start_wait = 4; - bool brute_mem = (arg & 0x100); - uint8_t Block = (arg >> 9) & 0x07; - - if (brute_mem) start_wait = 0; - - // Build Bit Stream to send. - memset (BitStream,0x00,sizeof(BitStream)); - - BitStreamLen = 0; // Ensure 0 bit index to start. - - // Add Leading 0 and 1 of 4 reference bit - if ((downlink_mode == T55xx_DLMode_Leading0) || (downlink_mode == T55xx_DLMode_1of4)) - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 1,sizeof(BitStream)); + uint8_t downlink_mode = (arg >> 3) & 0x03; + uint8_t i = 0; + uint8_t BitStream[10]; // Max Downlink Command size ~74 bits, so 10 bytes (80 bits) + uint8_t BitStreamLen = 0; + uint8_t SendBits; + uint8_t start_wait = 4; + bool brute_mem = (arg & 0x100); + uint8_t Block = (arg >> 9) & 0x07; - // Add extra reference 0 for 1 of 4 - if (downlink_mode == T55xx_DLMode_1of4) - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 1,sizeof(BitStream)); + if (brute_mem) start_wait = 0; - // Add Opcode - if (t55_send_Reset) { - // Reset : r*) 00 - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 2,sizeof(BitStream)); - } - else { - if (t55_send_TestMode) Dbprintf("TestMODE"); - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen,t55_send_TestMode ? 0 : 1 , 1,sizeof(BitStream)); - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen,t55_send_TestMode ? 1 : t55_send_Page , 1,sizeof(BitStream)); - //if (PwdMode) { - if (t55_send_PwdMode) { - // Leading 0 and 1 of 4 00 fixed bits if passsword used - if ((downlink_mode == T55xx_DLMode_Leading0) || (downlink_mode == T55xx_DLMode_1of4)) { - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 2,sizeof(BitStream)); - } - BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, Pwd, 32,sizeof(BitStream)); - } + // Build Bit Stream to send. + memset(BitStream, 0x00, sizeof(BitStream)); - // Add Lock bit 0 - if (!t55_send_RegReadMode) BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 1,sizeof(BitStream)); + BitStreamLen = 0; // Ensure 0 bit index to start. - // Add Data if a write command - if (!t55_send_ReadCmd) BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, Data, 32,sizeof(BitStream)); + // Add Leading 0 and 1 of 4 reference bit + if ((downlink_mode == T55xx_DLMode_Leading0) || (downlink_mode == T55xx_DLMode_1of4)) + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, 0, 1, sizeof(BitStream)); - // Add Address - if (!t55_send_RegReadMode) BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, Block, 3,sizeof(BitStream)); - } + // Add extra reference 0 for 1 of 4 + if (downlink_mode == T55xx_DLMode_1of4) + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, 0, 1, sizeof(BitStream)); - // Send Bits to T55xx - // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); + // Add Opcode + if (t55_send_Reset) { + // Reset : r*) 00 + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, 0, 2, sizeof(BitStream)); + } else { + if (t55_send_TestMode) Dbprintf("TestMODE"); + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, t55_send_TestMode ? 0 : 1, 1, sizeof(BitStream)); + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, t55_send_TestMode ? 1 : t55_send_Page, 1, sizeof(BitStream)); + //if (PwdMode) { + if (t55_send_PwdMode) { + // Leading 0 and 1 of 4 00 fixed bits if passsword used + if ((downlink_mode == T55xx_DLMode_Leading0) || (downlink_mode == T55xx_DLMode_1of4)) { + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, 0, 2, sizeof(BitStream)); + } + BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, Pwd, 32, sizeof(BitStream)); + } - // make sure tag is fully powered up... - WaitMS(start_wait); + // Add Lock bit 0 + if (!t55_send_RegReadMode) BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, 0, 1, sizeof(BitStream)); - // Trigger T55x7 in mode. - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(T55xx_Timing.m[downlink_mode].start_gap * 8); + // Add Data if a write command + if (!t55_send_ReadCmd) BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, Data, 32, sizeof(BitStream)); - // If long leading 0 send long reference pulse - if (downlink_mode == T55xx_DLMode_LLR) - T55xxWriteBit (T55xx_LongLeadingReference,downlink_mode);//Timing); // Send Long Leading Start Reference + // Add Address + if (!t55_send_RegReadMode) BitStreamLen = T55xx_SetBits(BitStream, BitStreamLen, Block, 3, sizeof(BitStream)); + } - if ((downlink_mode == T55xx_DLMode_1of4) && (BitStreamLen > 0)) { // 1 of 4 need to send 2 bits at a time - for ( i = 0; i < BitStreamLen-1; i+=2 ) { - SendBits = (BitStream[BitStream_Byte(i )] >> (BitStream_Bit(i )) & 1) << 1; // Bit i - SendBits += (BitStream[BitStream_Byte(i+1)] >> (BitStream_Bit(i+1)) & 1); // Bit i+1; - T55xxWriteBit (SendBits & 3,downlink_mode);//Timing); - } - } - else { - for (i = 0; i < BitStreamLen; i++) { - SendBits = (BitStream[BitStream_Byte(i)] >> BitStream_Bit(i)); - T55xxWriteBit (SendBits & 1,downlink_mode);//Timing); - } - } + // Send Bits to T55xx + // Set up FPGA, 125kHz + LFSetupFPGAForADC(95, true); + + // make sure tag is fully powered up... + WaitMS(start_wait); + + // Trigger T55x7 in mode. + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(T55xx_Timing.m[downlink_mode].start_gap * 8); + + // If long leading 0 send long reference pulse + if (downlink_mode == T55xx_DLMode_LLR) + T55xxWriteBit(T55xx_LongLeadingReference, downlink_mode);//Timing); // Send Long Leading Start Reference + + if ((downlink_mode == T55xx_DLMode_1of4) && (BitStreamLen > 0)) { // 1 of 4 need to send 2 bits at a time + for (i = 0; i < BitStreamLen - 1; i += 2) { + SendBits = (BitStream[BitStream_Byte(i)] >> (BitStream_Bit(i)) & 1) << 1; // Bit i + SendBits += (BitStream[BitStream_Byte(i + 1)] >> (BitStream_Bit(i + 1)) & 1); // Bit i+1; + T55xxWriteBit(SendBits & 3, downlink_mode);//Timing); + } + } else { + for (i = 0; i < BitStreamLen; i++) { + SendBits = (BitStream[BitStream_Byte(i)] >> BitStream_Bit(i)); + T55xxWriteBit(SendBits & 1, downlink_mode);//Timing); + } + } } // Send T5577 reset command then read stream (see if we can identify the start of the stream) void T55xxResetRead(uint8_t flags) { - uint8_t downlink_mode = ((flags >> 3) & 3); - uint8_t arg = 0x80 | downlink_mode; - + uint8_t downlink_mode = ((flags >> 3) & 3); + uint8_t arg = 0x80 | downlink_mode; + LED_A_ON(); - + //clear buffer now so it does not interfere with timing later BigBuf_Clear_keep_EM(); - T55xx_SendCMD (0, 0, arg); + T55xx_SendCMD(0, 0, arg); TurnReadLFOn(T55xx_Timing.m[downlink_mode].read_gap); - + // Acquisition DoPartialAcquisition(0, true, BigBuf_max_traceLen(), 0); // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off reply_mix(CMD_ACK, 0, 0, 0, 0, 0); - + LED_A_OFF(); } @@ -1653,60 +1671,60 @@ void T55xxResetRead(uint8_t flags) { //void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags) { void T55xxWriteBlock(uint8_t *data) { - /* - flag bits - xxxxxxx1 0x01 PwdMode - xxxxxx1x 0x02 Page - xxxxx1xx 0x04 testMode - xxx11xxx 0x18 downlink mode - xx1xxxxx 0x20 !reg_readmode - x1xxxxxx 0x40 called for a read, so no data packet - 1xxxxxxx 0x80 reset - */ - - t55xx_write_block_t *c = (t55xx_write_block_t *)data; - // c->data, c->blockno, c->pwd, c->flags + /* + flag bits + xxxxxxx1 0x01 PwdMode + xxxxxx1x 0x02 Page + xxxxx1xx 0x04 testMode + xxx11xxx 0x18 downlink mode + xx1xxxxx 0x20 !reg_readmode + x1xxxxxx 0x40 called for a read, so no data packet + 1xxxxxxx 0x80 reset + */ - bool testMode = ((c->flags & 0x04) == 0x04); + t55xx_write_block_t *c = (t55xx_write_block_t *)data; + // c->data, c->blockno, c->pwd, c->flags - c->flags &= (0xff ^ 0x40); // Called for a write, so ensure it is clear/0 - - LED_A_ON (); - T55xx_SendCMD (c->data, c->pwd, c->flags | (c->blockno << 9)) ;//, false); + bool testMode = ((c->flags & 0x04) == 0x04); - // Perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, - // so wait a little more) + c->flags &= (0xff ^ 0x40); // Called for a write, so ensure it is clear/0 - // "there is a clock delay before programming" - // - programming takes ~5.6ms for t5577 ~18ms for E5550 or t5567 - // so we should wait 1 clock + 5.6ms then read response? - // but we need to know we are dealing with t5577 vs t5567 vs e5550 (or q5) marshmellow... - if (testMode) { - //TESTMODE TIMING TESTS: - // <566us does nothing - // 566-568 switches between wiping to 0s and doing nothing - // 5184 wipes and allows 1 block to be programmed. - // indefinite power on wipes and then programs all blocks with bitshifted data sent. - TurnReadLFOn(5184); + LED_A_ON(); + T55xx_SendCMD(c->data, c->pwd, c->flags | (c->blockno << 9)) ; //, false); - } else { - TurnReadLFOn(20 * 1000); - //could attempt to do a read to confirm write took - // as the tag should repeat back the new block - // until it is reset, but to confirm it we would - // need to know the current block 0 config mode for - // modulation clock an other details to demod the response... - // response should be (for t55x7) a 0 bit then (ST if on) - // block data written in on repeat until reset. + // Perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, + // so wait a little more) - //DoPartialAcquisition(20, true, 12000); - } - // turn field off - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // "there is a clock delay before programming" + // - programming takes ~5.6ms for t5577 ~18ms for E5550 or t5567 + // so we should wait 1 clock + 5.6ms then read response? + // but we need to know we are dealing with t5577 vs t5567 vs e5550 (or q5) marshmellow... + if (testMode) { + //TESTMODE TIMING TESTS: + // <566us does nothing + // 566-568 switches between wiping to 0s and doing nothing + // 5184 wipes and allows 1 block to be programmed. + // indefinite power on wipes and then programs all blocks with bitshifted data sent. + TurnReadLFOn(5184); - // cmd_send(CMD_ACK,0,0,0,0,0); - reply_ng(CMD_T55XX_WRITE_BLOCK, PM3_SUCCESS, NULL, 0); - LED_A_OFF (); + } else { + TurnReadLFOn(20 * 1000); + //could attempt to do a read to confirm write took + // as the tag should repeat back the new block + // until it is reset, but to confirm it we would + // need to know the current block 0 config mode for + // modulation clock an other details to demod the response... + // response should be (for t55x7) a 0 bit then (ST if on) + // block data written in on repeat until reset. + + //DoPartialAcquisition(20, true, 12000); + } + // turn field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + // cmd_send(CMD_ACK,0,0,0,0,0); + reply_ng(CMD_T55XX_WRITE_BLOCK, PM3_SUCCESS, NULL, 0); + LED_A_OFF(); } /* @@ -1719,49 +1737,49 @@ void T55xxWriteBlock(uint8_t *data) { */ /* // Read one card block in page [page] -void T55xxReadBlockExt (uint16_t flags, uint8_t block, uint32_t pwd) { - / * - flag bits - xxxx xxxxxxx1 0x0001 PwdMode - xxxx xxxxxx1x 0x0002 Page - xxxx xxxxx1xx 0x0004 testMode - xxxx xxx11xxx 0x0018 downlink mode - xxxx xx1xxxxx 0x0020 !reg_readmode - xxxx x1xxxxxx 0x0040 called for a read, so no data packet - xxxx 1xxxxxxx 0x0080 reset - xxx1 xxxxxxxx 0x0100 brute/leave field on - * / - size_t samples = 12000; - bool brute_mem = (flags & 0x0100) >> 8; - - LED_A_ON(); +void T55xxReadBlockExt(uint16_t flags, uint8_t block, uint32_t pwd) { + / * + flag bits + xxxx xxxxxxx1 0x0001 PwdMode + xxxx xxxxxx1x 0x0002 Page + xxxx xxxxx1xx 0x0004 testMode + xxxx xxx11xxx 0x0018 downlink mode + xxxx xx1xxxxx 0x0020 !reg_readmode + xxxx x1xxxxxx 0x0040 called for a read, so no data packet + xxxx 1xxxxxxx 0x0080 reset + xxx1 xxxxxxxx 0x0100 brute / leave field on + * / + size_t samples = 12000; +bool brute_mem = (flags & 0x0100) >> 8; - if (brute_mem) samples = 1024; - - // Set Read Flag to ensure SendCMD does not add "data" to the packet - flags |= 0x40; + LED_A_ON(); - // RegRead Mode true block = 0xff, so read without an address - if (block == 0xff) flags |= 0x20; - - //make sure block is at max 7 - block &= 0x7; + if (brute_mem) samples = 1024; - //clear buffer now so it does not interfere with timing later - BigBuf_Clear_keep_EM(); + // Set Read Flag to ensure SendCMD does not add "data" to the packet + flags |= 0x40; - T55xx_SendCMD (0, pwd, flags | (block << 9)); //, true); + // RegRead Mode true block = 0xff, so read without an address + if (block == 0xff) flags |= 0x20; - // Turn field on to read the response - // 137*8 seems to get to the start of data pretty well... - // but we want to go past the start and let the repeating data settle in... + //make sure block is at max 7 + block &= 0x7; - // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help - TurnReadLFOn(137*8); + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); - // Acquisition - // Now do the acquisition - DoPartialAcquisition(0, true, samples, 0); + T55xx_SendCMD(0, pwd, flags | (block << 9)); //, true); + + // Turn field on to read the response + // 137*8 seems to get to the start of data pretty well... + // but we want to go past the start and let the repeating data settle in... + + // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help + TurnReadLFOn(137 * 8); + + // Acquisition + // Now do the acquisition + DoPartialAcquisition(0, true, samples, 0); // Turn the field off if (!brute_mem) { @@ -1773,55 +1791,55 @@ void T55xxReadBlockExt (uint16_t flags, uint8_t block, uint32_t pwd) { */ // Read one card block in page [page] void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd, uint8_t downlink_mode) { - /* - flag bits - xxxxxxx1 0x0001 PwdMode - xxxxxx1x 0x0002 Page - xxxxx1xx 0x0004 testMode - xxx11xxx 0x0018 downlink mode - xx1xxxxx 0x0020 !reg_readmode - x1xxxxxx 0x0040 called for a read, so no data packet - 1xxxxxxx 0x0080 reset - 1xxxxxxxx 0x0100 brute/leave field on - */ - uint16_t flags = 0x0040; // read packet - if (pwd_mode) flags |= 0x0001; - if (page) flags |= 0x0002; - flags |= (downlink_mode & 3) << 3; - if (brute_mem) flags |= 0x0100; - -// T55xxReadBlockExt (flags,block,pwd); - size_t samples = 12000; - // bool brute_mem = (flags & 0x0100) >> 8; - - LED_A_ON(); + /* + flag bits + xxxx xxxxxxx1 0x0001 PwdMode + xxxx xxxxxx1x 0x0002 Page + xxxx xxxxx1xx 0x0004 testMode + xxxx xxx11xxx 0x0018 downlink mode + xxxx xx1xxxxx 0x0020 !reg_readmode + xxxx x1xxxxxx 0x0040 called for a read, so no data packet + xxxx 1xxxxxxx 0x0080 reset + xxx1 xxxxxxxx 0x0100 brute / leave field on + */ + uint16_t flags = 0x0040; // read packet + if (pwd_mode) flags |= 0x0001; + if (page) flags |= 0x0002; + flags |= (downlink_mode & 3) << 3; + if (brute_mem) flags |= 0x0100; - if (brute_mem) samples = 1024; - - //-- Set Read Flag to ensure SendCMD does not add "data" to the packet - //-- flags |= 0x40; +// T55xxReadBlockExt (flags,block,pwd); + size_t samples = 12000; + // bool brute_mem = (flags & 0x0100) >> 8; - // RegRead Mode true block = 0xff, so read without an address - if (block == 0xff) flags |= 0x20; - - //make sure block is at max 7 - block &= 0x7; + LED_A_ON(); - //clear buffer now so it does not interfere with timing later - BigBuf_Clear_keep_EM(); + if (brute_mem) samples = 1024; - T55xx_SendCMD (0, pwd, flags | (block << 9)); //, true); + //-- Set Read Flag to ensure SendCMD does not add "data" to the packet + //-- flags |= 0x40; - // Turn field on to read the response - // 137*8 seems to get to the start of data pretty well... - // but we want to go past the start and let the repeating data settle in... + // RegRead Mode true block = 0xff, so read without an address + if (block == 0xff) flags |= 0x20; - // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help - TurnReadLFOn(137*8); + //make sure block is at max 7 + block &= 0x7; - // Acquisition - // Now do the acquisition - DoPartialAcquisition(0, true, samples, 0); + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); + + T55xx_SendCMD(0, pwd, flags | (block << 9)); //, true); + + // Turn field on to read the response + // 137*8 seems to get to the start of data pretty well... + // but we want to go past the start and let the repeating data settle in... + + // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help + TurnReadLFOn(137 * 8); + + // Acquisition + // Now do the acquisition + DoPartialAcquisition(0, true, samples, 0); // Turn the field off if (!brute_mem) { @@ -1841,13 +1859,13 @@ void T55xx_ChkPwds(uint8_t flags) { // tends to mess up BigBuf uint8_t *buf = BigBuf_get_addr(); uint32_t b1, baseline = 0; - uint8_t downlink_mode = (flags >> 3) & 0x03; - + uint8_t downlink_mode = (flags >> 3) & 0x03; + // collect baseline for failed attempt uint8_t x = 32; while (x--) { b1 = 0; - T55xxReadBlock(0, 0, true, 1, 0,downlink_mode); + T55xxReadBlock(0, 0, true, 1, 0, downlink_mode); for (uint16_t j = 0; j < 1024; ++j) b1 += buf[j]; @@ -1892,7 +1910,7 @@ void T55xx_ChkPwds(uint8_t flags) { pwd = bytes_to_num(pwds + i * 4, 4); - T55xxReadBlock(0, true, true, 0, pwd,downlink_mode); + T55xxReadBlock(0, true, true, 0, pwd, downlink_mode); // calc mean of BigBuf 1024 samples. uint32_t sum = 0; @@ -1909,7 +1927,7 @@ void T55xx_ChkPwds(uint8_t flags) { Dbprintf("[=] Pwd %08X | ABS %u", pwd, curr); if (curr > prev) { - Dbprintf("[=] --> ABS %u Candidate %08X <--", curr, pwd); + Dbprintf("[=] --> ABS %u Candidate %08X <--", curr, pwd); candidate = pwd; prev = curr; } @@ -1925,12 +1943,12 @@ OUT: } void T55xxWakeUp(uint32_t Pwd, uint8_t flags) { - - flags |= 0x01 | 0x40 | 0x20; //Password | Read Call (no data) | reg_read no block + + flags |= 0x01 | 0x40 | 0x20; //Password | Read Call (no data) | reg_read no block LED_B_ON(); - T55xx_SendCMD (0, Pwd, flags); - + T55xx_SendCMD(0, Pwd, flags); + //-- Turn and leave field on to let the begin repeating transmission TurnReadLFOn(20 * 1000); } @@ -1939,16 +1957,16 @@ void T55xxWakeUp(uint32_t Pwd, uint8_t flags) { /*-------------- Cloning routines -----------*/ void WriteT55xx(uint32_t *blockdata, uint8_t startblock, uint8_t numblocks) { - t55xx_write_block_t cmd; - cmd.pwd = 0; + t55xx_write_block_t cmd; + cmd.pwd = 0; cmd.flags = 0; - for (uint8_t i = numblocks + startblock; i > startblock; i--) { - cmd.data = blockdata[i - 1]; - cmd.blockno = i - 1; - T55xxWriteBlock ((uint8_t *)&cmd); - } - + for (uint8_t i = numblocks + startblock; i > startblock; i--) { + cmd.data = blockdata[i - 1]; + cmd.blockno = i - 1; + T55xxWriteBlock((uint8_t *)&cmd); + } + } // Copy HID id to card and setup block 0 config diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 618ab8c7c..69cd8251b 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -49,8 +49,8 @@ static int usage_t55xx_read() { PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); PrintAndLogEx(NORMAL, " o - OPTIONAL override safety check"); PrintAndLogEx(NORMAL, " 1 - OPTIONAL read Page 1 instead of Page 0"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, " ****WARNING****"); PrintAndLogEx(NORMAL, " Use of read with password on a tag not configured for a pwd"); PrintAndLogEx(NORMAL, " can damage the tag"); @@ -70,8 +70,8 @@ static int usage_t55xx_write() { PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex characters)"); PrintAndLogEx(NORMAL, " 1 - OPTIONAL write Page 1 instead of Page 0"); PrintAndLogEx(NORMAL, " t - OPTIONAL test mode write - ****DANGER****"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx write b 3 d 11223344 - write 11223344 to block 3"); @@ -82,10 +82,10 @@ static int usage_t55xx_write() { static int usage_t55xx_trace() { PrintAndLogEx(NORMAL, "Usage: lf t55xx trace [r mode]"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); // Command did not seem to support the 1 option (yet) so have removed the help lines - // PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); + // PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx trace"); @@ -101,8 +101,8 @@ static int usage_t55xx_info() { PrintAndLogEx(NORMAL, " d - 4 bytes of data (8 hex characters)"); PrintAndLogEx(NORMAL, " if set, use these data instead of reading tag."); PrintAndLogEx(NORMAL, " q - if set, provided data are interpreted as Q5 config."); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx info"); @@ -117,8 +117,8 @@ static int usage_t55xx_dump() { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " - OPTIONAL password 4bytes (8 hex symbols)"); PrintAndLogEx(NORMAL, " o - OPTIONAL override, force pwd read despite danger to card"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx dump"); @@ -131,9 +131,9 @@ static int usage_t55xx_detect() { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); PrintAndLogEx(NORMAL, " p - downlink encoding '0' fixed bit length (default)"); - PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); - PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx detect"); @@ -148,9 +148,9 @@ static int usage_t55xx_detectP1() { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); - PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); - PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx p1detect"); @@ -165,8 +165,8 @@ static int usage_t55xx_wakup() { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); PrintAndLogEx(NORMAL, " p - password 4bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx wakeup p 11223344 - send wakeup password"); @@ -181,9 +181,9 @@ static int usage_t55xx_chk() { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); PrintAndLogEx(NORMAL, " m - use dictionary from flashmemory\n"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); - PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); - PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, " i <*.dic> - loads a default keys dictionary file <*.dic>"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); @@ -201,9 +201,9 @@ static int usage_t55xx_bruteforce() { PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); - PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); - PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, " - 4 byte hex value to start pwd search at"); PrintAndLogEx(NORMAL, " - 4 byte hex value to end pwd search at"); PrintAndLogEx(NORMAL, ""); @@ -222,9 +222,9 @@ static int usage_t55xx_recoverpw() { PrintAndLogEx(NORMAL, " default password is 51243648, used by many cloners"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); - PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); - PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, " [password] - 4 byte hex value of password written by cloner"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); @@ -258,10 +258,10 @@ static int usage_lf_deviceconfig() { PrintAndLogEx(NORMAL, " f <8..255> - Set write THREE gap (1 of 4 only)"); PrintAndLogEx(NORMAL, " g <8..255> - Set read gap"); PrintAndLogEx(NORMAL, " p - persist to flashmemory"); - PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); - PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); - PrintAndLogEx(NORMAL, " z - erase t55x7 timings (needs p and reboot to load defaults)"); - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " z - erase t55x7 timings (needs p and reboot to load defaults)"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 29 b 17 c 15 d 47 e 15 - default T55XX"); PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 55 b 14 c 21 d 30 - default EM4305"); @@ -397,7 +397,7 @@ int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, uint8_t override, uin if (usepwd) { // try reading the config block and verify that PWD bit is set before doing this! if (!override) { - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0,downlink_mode)) return PM3_ESOFT; + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0, downlink_mode)) return PM3_ESOFT; if (!tryDetectModulation()) { PrintAndLogEx(NORMAL, "Safety Check: Could not detect if PWD bit is set in config block. Exits."); @@ -408,9 +408,9 @@ int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, uint8_t override, uin page1 = false; } } else { - // Show only if first for command i.e. override = 1 (override and display) override = 2 (override and dont display) - if ((override & 2) != 2) - PrintAndLogEx(NORMAL, "Safety Check Overriden - proceeding despite risk"); + // Show only if first for command i.e. override = 1 (override and display) override = 2 (override and dont display) + if ((override & 2) != 2) + PrintAndLogEx(NORMAL, "Safety Check Overriden - proceeding despite risk"); } } @@ -430,8 +430,8 @@ static int CmdT55xxReadBlock(const char *Cmd) { bool page1 = false; bool errors = false; uint8_t cmdp = 0; - uint8_t downlink_mode = 0; - + uint8_t downlink_mode = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -453,13 +453,13 @@ static int CmdT55xxReadBlock(const char *Cmd) { page1 = true; cmdp++; break; - case 'r': - case 'R': - downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; - if (downlink_mode > 3) downlink_mode = 0; - cmdp +=2; - break; - + case 'r': + case 'R': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; + default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -556,34 +556,42 @@ static int SanityOfflineCheck(bool useGraphBuffer) { return PM3_SUCCESS; } -void T55xx_Print_DownlinkMode (uint8_t downlink_mode) -{ - char Msg[80]; - sprintf (Msg,"Downlink Mode used : "); - - switch (downlink_mode) { - case 0 : strcat (Msg,"default/fixed bit length"); break; - case 1 : strcat (Msg,"long leading reference (r 1)"); break; - case 2 : strcat (Msg,"leading zero reference (r 2)"); break; - case 3 : strcat (Msg,"1 of 4 coding reference (r 3)"); break; - default : - strcat (Msg,"default/fixed bit length"); break; - } - - PrintAndLogEx (NORMAL,Msg); +void T55xx_Print_DownlinkMode(uint8_t downlink_mode) { + char Msg[80]; + sprintf(Msg, "Downlink Mode used : "); + + switch (downlink_mode) { + case 0 : + strcat(Msg, "default/fixed bit length"); + break; + case 1 : + strcat(Msg, "long leading reference (r 1)"); + break; + case 2 : + strcat(Msg, "leading zero reference (r 2)"); + break; + case 3 : + strcat(Msg, "1 of 4 coding reference (r 3)"); + break; + default : + strcat(Msg, "default/fixed bit length"); + break; + } + + PrintAndLogEx(NORMAL, Msg); } -// +// static int CmdT55xxDetect(const char *Cmd) { bool errors = false; bool useGB = false; - bool usepwd = false; - bool try_all_dl_modes = false; - bool found = false; + bool usepwd = false; + bool try_all_dl_modes = false; + bool found = false; uint32_t password = 0; uint8_t cmdp = 0; - uint8_t downlink_mode = 0; - uint8_t dl_mode = 0; + uint8_t downlink_mode = 0; + uint8_t dl_mode = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { @@ -599,12 +607,12 @@ static int CmdT55xxDetect(const char *Cmd) { useGB = true; cmdp++; break; - case 'r': - downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; - if (downlink_mode == 4) try_all_dl_modes = true; - if (downlink_mode > 3) downlink_mode = 0; - cmdp +=2; - break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode == 4) try_all_dl_modes = true; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -617,44 +625,44 @@ static int CmdT55xxDetect(const char *Cmd) { if (SanityOfflineCheck(useGB) != PM3_SUCCESS) return PM3_ENODATA; if (!useGB) { - for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { - found = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,dl_mode); + for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { + found = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password, dl_mode); - // found = false if password is supplied but wrong d/l mode - // so keep trying other modes (if requested) - /* if (!found) { - printf ("Aquire not found"); - return PM3_ENODATA; - } - */ - if (tryDetectModulation()) - { - T55xx_Print_DownlinkMode (dl_mode); - dl_mode = 4; - found = true; - } - else found = false; + // found = false if password is supplied but wrong d/l mode + // so keep trying other modes (if requested) + /* + if (!found) { + printf ("Aquire not found"); + return PM3_ENODATA; + } + */ + if (tryDetectModulation()) { + T55xx_Print_DownlinkMode(dl_mode); + dl_mode = 4; + found = true; + } else found = false; - if (!try_all_dl_modes) dl_mode = 4; - } + if (!try_all_dl_modes) dl_mode = 4; + } } - - if (useGB) found = tryDetectModulation(); - - if (!found) - PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); + + if (useGB) found = tryDetectModulation(); + + if (!found) + PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); - /* if (!useGB) { + /* + if (!useGB) { if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,downlink_mode)) return PM3_ENODATA; } if (!tryDetectModulation()) PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); - else - T55xx_Print_DownlinkMode (downlink_mode); -*/ + else + T55xx_Print_DownlinkMode (downlink_mode); + */ return PM3_SUCCESS; } @@ -1101,14 +1109,14 @@ int printConfiguration(t55xx_conf_block_t b) { } static int CmdT55xxWakeUp(const char *Cmd) { - + uint32_t password = 0; uint8_t cmdp = 0; bool errors = false; uint8_t downlink_mode = 0; - uint8_t flags = 0; + uint8_t flags = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': return usage_t55xx_wakup(); @@ -1117,11 +1125,11 @@ static int CmdT55xxWakeUp(const char *Cmd) { cmdp += 2; errors = false; break; - case 'r': - downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; - if (downlink_mode > 3) downlink_mode = 0; - cmdp +=2; - break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -1131,7 +1139,7 @@ static int CmdT55xxWakeUp(const char *Cmd) { if (errors) return usage_t55xx_wakup(); - flags = (downlink_mode & 3) << 3; + flags = (downlink_mode & 3) << 3; clearCommandBuffer(); SendCommandMIX(CMD_T55XX_WAKEUP, password, flags, 0, NULL, 0); PrintAndLogEx(SUCCESS, "Wake up command sent. Try read now"); @@ -1149,8 +1157,8 @@ static int CmdT55xxWriteBlock(const char *Cmd) { bool testMode = false; bool errors = false; uint8_t cmdp = 0; - uint32_t downlink_mode = 0; - + uint32_t downlink_mode = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -1182,11 +1190,11 @@ static int CmdT55xxWriteBlock(const char *Cmd) { page1 = true; cmdp++; break; - case 'r': - downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; - if (downlink_mode > 3) downlink_mode = 0; - cmdp +=2; - break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -1200,8 +1208,8 @@ static int CmdT55xxWriteBlock(const char *Cmd) { flags = (usepwd) ? 0x1 : 0; flags |= (page1) ? 0x2 : 0; flags |= (testMode) ? 0x4 : 0; - flags |= (downlink_mode << 3); - + flags |= (downlink_mode << 3); + char pwdStr[16] = {0}; snprintf(pwdStr, sizeof(pwdStr), "pwd: 0x%08X", password); @@ -1234,26 +1242,26 @@ static int CmdT55xxWriteBlock(const char *Cmd) { } static int CmdT55xxReadTrace(const char *Cmd) { - uint8_t cmd_len = 0; - uint8_t downlink_mode = 0; - + uint8_t cmd_len = 0; + uint8_t downlink_mode = 0; + char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'r') { - downlink_mode = param_getchar(Cmd, 1) - '0'; - if (downlink_mode > 3) downlink_mode = 0; - cmd_len = 3; - } + if (cmdp == 'r') { + downlink_mode = param_getchar(Cmd, 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmd_len = 3; + } if ((strlen(Cmd) != cmd_len) || (cmdp == 'h')) return usage_t55xx_trace(); - + if (strlen(Cmd) == cmd_len) { // sanity check. if (SanityOfflineCheck(false) != PM3_SUCCESS) return PM3_ENODATA; bool pwdmode = false; uint32_t password = 0; -// REGULAR_READ_MODE_BLOCK - yeilds correct Page 1 Block 2 data i.e. + 32 bit offset. +// REGULAR_READ_MODE_BLOCK - yeilds correct Page 1 Block 2 data i.e. + 32 bit offset. // if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, pwdmode, password,downlink_mode)) - if (!AquireData(T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password,downlink_mode)) + if (!AquireData(T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password, downlink_mode)) return PM3_ENODATA; } @@ -1497,9 +1505,9 @@ static int CmdT55xxInfo(const char *Cmd) { */ bool frombuff = false, gotdata = false, dataasq5 = false; uint8_t cmdp = 0; - uint8_t downlink_mode = 0; + uint8_t downlink_mode = 0; uint32_t block0 = 0; - + while (param_getchar(Cmd, cmdp) != 0x00) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -1517,11 +1525,11 @@ static int CmdT55xxInfo(const char *Cmd) { dataasq5 = true; cmdp += 2; break; - case 'r': - downlink_mode = param_getchar(Cmd, cmdp+1)- '0'; - if (downlink_mode > 3) downlink_mode = 0; - cmdp += 2; - break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); return usage_t55xx_info(); @@ -1540,7 +1548,7 @@ static int CmdT55xxInfo(const char *Cmd) { bool pwdmode = false; uint32_t password = 0; - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password,downlink_mode)) + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password, downlink_mode)) return PM3_ENODATA; } if (!gotdata) { @@ -1637,19 +1645,19 @@ static int CmdT55xxDump(const char *Cmd) { uint32_t password = 0; uint8_t override = false; - uint8_t cmd_opt_idx = 0; - uint8_t downlink_mode = 0; - uint8_t pwd_offset = 0; + uint8_t cmd_opt_idx = 0; + uint8_t downlink_mode = 0; + uint8_t pwd_offset = 0; char cmdp = tolower(param_getchar(Cmd, 0)); - + if (cmdp == 'h') return usage_t55xx_dump(); - if (cmdp == 'r') { - cmd_opt_idx++; - downlink_mode = param_getchar(Cmd, cmd_opt_idx++) - '0'; - if (downlink_mode > 3) downlink_mode = 0; - pwd_offset = 3; - } + if (cmdp == 'r') { + cmd_opt_idx++; + downlink_mode = param_getchar(Cmd, cmd_opt_idx++) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + pwd_offset = 3; + } bool usepwd = (strlen(Cmd) > pwd_offset); if (usepwd) { password = param_get32ex(Cmd, cmd_opt_idx++, 0, 16); @@ -1659,14 +1667,14 @@ static int CmdT55xxDump(const char *Cmd) { printT5xxHeader(0); for (uint8_t i = 0; i < 8; ++i) { - T55xxReadBlock(i, 0, usepwd, override, password,downlink_mode); - // idea for better user experience and display. - // only show override warning on the first block read - if (override) override |= 2; // flag not to show safty for 2nd and on. - } + T55xxReadBlock(i, 0, usepwd, override, password, downlink_mode); + // idea for better user experience and display. + // only show override warning on the first block read + if (override) override |= 2; // flag not to show safty for 2nd and on. + } printT5xxHeader(1); for (uint8_t i = 0; i < 4; i++) - T55xxReadBlock(i, 1, usepwd, override, password,downlink_mode); + T55xxReadBlock(i, 1, usepwd, override, password, downlink_mode); return PM3_SUCCESS; } @@ -1683,14 +1691,14 @@ bool AquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password, ui uint8_t blockno; uint8_t page; bool pwdmode; - uint8_t downlink_mode; + uint8_t downlink_mode; } PACKED; struct p payload; payload.password = password; payload.blockno = block; payload.page = page & 0x1; payload.pwdmode = pwdmode; - payload.downlink_mode = downlink_mode; + payload.downlink_mode = downlink_mode; clearCommandBuffer(); SendCommandNG(CMD_T55XX_READ_BLOCK, (uint8_t *)&payload, sizeof(payload)); @@ -1951,17 +1959,17 @@ static void t55x7_create_config_block(int tagtype) { static int CmdResetRead(const char *Cmd) { - uint8_t downlink_mode = 0; - uint8_t flags = 0; - - - if (strlen (Cmd) == 3) - downlink_mode = param_getchar(Cmd, 1) - '0'; - - if (downlink_mode > 3) downlink_mode = 0; - - printf ("DL : %d\n",downlink_mode); - flags = downlink_mode << 3; + uint8_t downlink_mode = 0; + uint8_t flags = 0; + + + if (strlen(Cmd) == 3) + downlink_mode = param_getchar(Cmd, 1) - '0'; + + if (downlink_mode > 3) downlink_mode = 0; + + printf("DL : %d\n", downlink_mode); + flags = downlink_mode << 3; clearCommandBuffer(); SendCommandNG(CMD_T55XX_RESET_READ, &flags, sizeof(flags)); if (!WaitForResponseTimeout(CMD_ACK, NULL, 2500)) { @@ -2019,44 +2027,44 @@ static bool IsCancelled(void) { // load a default pwd file. static int CmdT55xxChkPwds(const char *Cmd) { - char filename[FILE_PATH_SIZE] = {0}; - bool found = false; - uint8_t timeout = 0; - uint8_t *keyBlock = NULL; - bool from_flash = false; - bool try_all_dl_modes = false; - uint8_t downlink_mode = 0; - int len; - char cmdp; - bool use_pwd_file = false; - int dl_mode; // to try each downlink mode for each password - - - cmdp = tolower(param_getchar(Cmd,0)); - - if (cmdp == 'h') return usage_t55xx_chk(); - if (cmdp == 'm') { - from_flash = true; - Cmd +=2; - cmdp = tolower(param_getchar(Cmd,0)); - } - if (cmdp == 'r') { - Cmd += 2; - downlink_mode = param_getchar(Cmd,0 ) - '0'; // get 2nd option, as this is fixed order. - if (downlink_mode == 4) try_all_dl_modes = true; - if (downlink_mode > 3) downlink_mode = 0; - Cmd += 2; - cmdp = param_getchar(Cmd,0); - } - if (cmdp == 'i') { - Cmd+=2; - len = strlen(Cmd); - if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd, len); - use_pwd_file = true; - } - - + char filename[FILE_PATH_SIZE] = {0}; + bool found = false; + uint8_t timeout = 0; + uint8_t *keyBlock = NULL; + bool from_flash = false; + bool try_all_dl_modes = false; + uint8_t downlink_mode = 0; + int len; + char cmdp; + bool use_pwd_file = false; + int dl_mode; // to try each downlink mode for each password + + + cmdp = tolower(param_getchar(Cmd, 0)); + + if (cmdp == 'h') return usage_t55xx_chk(); + if (cmdp == 'm') { + from_flash = true; + Cmd += 2; + cmdp = tolower(param_getchar(Cmd, 0)); + } + if (cmdp == 'r') { + Cmd += 2; + downlink_mode = param_getchar(Cmd, 0) - '0'; // get 2nd option, as this is fixed order. + if (downlink_mode == 4) try_all_dl_modes = true; + if (downlink_mode > 3) downlink_mode = 0; + Cmd += 2; + cmdp = param_getchar(Cmd, 0); + } + if (cmdp == 'i') { + Cmd += 2; + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + use_pwd_file = true; + } + + /* // block 7, page1 = false, usepwd = false, override = false, pwd = 00000000 @@ -2069,8 +2077,8 @@ static int CmdT55xxChkPwds(const char *Cmd) { */ uint64_t t1 = msclock(); - uint8_t flags = downlink_mode << 3; - + uint8_t flags = downlink_mode << 3; + if (from_flash) { clearCommandBuffer(); SendCommandNG(CMD_T55XX_CHKPWDS, &flags, sizeof(flags)); @@ -2089,11 +2097,11 @@ static int CmdT55xxChkPwds(const char *Cmd) { if (resp.oldarg[0]) { PrintAndLogEx(SUCCESS, "\nFound a candidate [ " _YELLOW_("%08X") " ]. Trying to validate", resp.oldarg[1]); - if (AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, resp.oldarg[1],downlink_mode)) { + if (AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, resp.oldarg[1], downlink_mode)) { found = tryDetectModulation(); if (found) { PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", resp.oldarg[1]); - T55xx_Print_DownlinkMode (downlink_mode); + T55xx_Print_DownlinkMode(downlink_mode); } else { PrintAndLogEx(WARNING, "Check pwd failed"); @@ -2143,25 +2151,25 @@ static int CmdT55xxChkPwds(const char *Cmd) { curr_password = bytes_to_num(keyBlock + 4 * c, 4); PrintAndLogEx(INFO, "Testing %08X", curr_password); - for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++){ - - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password,dl_mode)) { - continue; - } + for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++) { - found = tryDetectModulation(); - if (found) { - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr_password); - T55xx_Print_DownlinkMode (dl_mode); - dl_mode = 4; // Exit other downlink mode checks - c = keycount; // Exit loop - } - - if (!try_all_dl_modes) // Exit loop if not trying all downlink modes - dl_mode = 4; - } - } - if (!found) PrintAndLogEx(WARNING, "Check pwd failed"); + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password, dl_mode)) { + continue; + } + + found = tryDetectModulation(); + if (found) { + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr_password); + T55xx_Print_DownlinkMode(dl_mode); + dl_mode = 4; // Exit other downlink mode checks + c = keycount; // Exit loop + } + + if (!try_all_dl_modes) // Exit loop if not trying all downlink modes + dl_mode = 4; + } + } + if (!found) PrintAndLogEx(WARNING, "Check pwd failed"); } free(keyBlock); @@ -2178,19 +2186,19 @@ static int CmdT55xxBruteForce(const char *Cmd) { uint32_t start_password = 0x00000000; //start password uint32_t end_password = 0xFFFFFFFF; //end password uint32_t curr = 0; - uint8_t downlink_mode = 0; - uint8_t cmd_opt_idx = 0; + uint8_t downlink_mode = 0; + uint8_t cmd_opt_idx = 0; uint8_t found = 0; // > 0 if found xx1 xx downlink needed, 1 found char cmdp = tolower(param_getchar(Cmd, cmd_opt_idx)); if (cmdp == 'h') return usage_t55xx_bruteforce(); - if (cmdp == 'r') { // downlink mode supplied - cmd_opt_idx++; // skip over 'r' - downlink_mode = param_getchar (Cmd,cmd_opt_idx++) - '0'; - if (downlink_mode > 4) downlink_mode = 0; - } - + if (cmdp == 'r') { // downlink mode supplied + cmd_opt_idx++; // skip over 'r' + downlink_mode = param_getchar(Cmd, cmd_opt_idx++) - '0'; + if (downlink_mode > 4) downlink_mode = 0; + } + uint64_t t1 = msclock(); @@ -2214,7 +2222,7 @@ static int CmdT55xxBruteForce(const char *Cmd) { return PM3_EOPABORTED; } - found = tryOnePassword(curr,downlink_mode); + found = tryOnePassword(curr, downlink_mode); if (curr == end_password) break; @@ -2225,10 +2233,9 @@ static int CmdT55xxBruteForce(const char *Cmd) { PrintAndLogEx(NORMAL, ""); if (found) { - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") "]", curr-1); - T55xx_Print_DownlinkMode ((found >> 1) & 3); - } - else + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") "]", curr - 1); + T55xx_Print_DownlinkMode((found >> 1) & 3); + } else PrintAndLogEx(WARNING, "Bruteforce failed, last tried: [ " _YELLOW_("%08X") " ]", curr); t1 = msclock() - t1; @@ -2237,31 +2244,30 @@ static int CmdT55xxBruteForce(const char *Cmd) { } uint8_t tryOnePassword(uint32_t password, uint8_t downlink_mode) { - - bool try_all_dl_modes = false; - uint8_t dl_mode = 0; - - PrintAndLogEx(INFO, "Trying password %08X", password); - if (downlink_mode == 4) try_all_dl_modes = true; - - downlink_mode = (downlink_mode & 3); // ensure 0-3 - - // check if dl mode 4 and loop if needed - for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++){ + bool try_all_dl_modes = false; + uint8_t dl_mode = 0; - AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, password,dl_mode); + PrintAndLogEx(INFO, "Trying password %08X", password); - // if (getSignalProperties()->isnoise == false) { - // } else { - if (tryDetectModulation()) - { - return 1 + (dl_mode << 1); - } - // } - if (!try_all_dl_modes) dl_mode = 4; - } - return 0; + if (downlink_mode == 4) try_all_dl_modes = true; + + downlink_mode = (downlink_mode & 3); // ensure 0-3 + + // check if dl mode 4 and loop if needed + for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { + + AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, password, dl_mode); + + // if (getSignalProperties()->isnoise == false) { + // } else { + if (tryDetectModulation()) { + return 1 + (dl_mode << 1); + } + // } + if (!try_all_dl_modes) dl_mode = 4; + } + return 0; } static int CmdT55xxRecoverPW(const char *Cmd) { @@ -2270,25 +2276,25 @@ static int CmdT55xxRecoverPW(const char *Cmd) { uint32_t curr_password = 0x0; uint32_t prev_password = 0xffffffff; uint32_t mask = 0x0; - uint8_t downlink_mode = 0; + uint8_t downlink_mode = 0; uint8_t found = 0; - uint8_t cmd_opt_idx = 0; - + uint8_t cmd_opt_idx = 0; + char cmdp = tolower(param_getchar(Cmd, cmd_opt_idx)); if (cmdp == 'h') return usage_t55xx_recoverpw(); - if (cmdp == 'r') { // downlink mode supplied - cmd_opt_idx++; // skip over 'r' - downlink_mode = param_getchar (Cmd,cmd_opt_idx++) - '0'; - if (downlink_mode > 4) downlink_mode = 0; - } + if (cmdp == 'r') { // downlink mode supplied + cmd_opt_idx++; // skip over 'r' + downlink_mode = param_getchar(Cmd, cmd_opt_idx++) - '0'; + if (downlink_mode > 4) downlink_mode = 0; + } orig_password = param_get32ex(Cmd, cmd_opt_idx++, 0x51243648, 16); //password used by handheld cloners // first try fliping each bit in the expected password while (bit < 32) { curr_password = orig_password ^ (1u << bit); - found = tryOnePassword(curr_password,downlink_mode); + found = tryOnePassword(curr_password, downlink_mode); if (found > 0) // xx1 for found xx = dl mode used goto out; @@ -2313,7 +2319,7 @@ static int CmdT55xxRecoverPW(const char *Cmd) { continue; } - found = tryOnePassword(curr_password,downlink_mode); + found = tryOnePassword(curr_password, downlink_mode); if (found > 0) goto out; @@ -2335,7 +2341,7 @@ static int CmdT55xxRecoverPW(const char *Cmd) { bit++; continue; } - found = tryOnePassword(curr_password,downlink_mode); + found = tryOnePassword(curr_password, downlink_mode); if (found > 0) goto out; @@ -2352,9 +2358,8 @@ out: if (found > 0) { PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") "]", curr_password); - T55xx_Print_DownlinkMode ((found >> 1) & 3); - } - else + T55xx_Print_DownlinkMode((found >> 1) & 3); + } else PrintAndLogEx(WARNING, "Recover pwd failed"); return PM3_SUCCESS; @@ -2371,7 +2376,7 @@ bool tryDetectP1(bool getData) { bool st = true; if (getData) { - if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, false, 0,0)) + if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, false, 0, 0)) return false; } @@ -2477,12 +2482,12 @@ static int CmdT55xxDetectPage1(const char *Cmd) { bool errors = false; bool useGB = false; bool usepwd = false; - bool try_all_dl_modes = false; - uint8_t found = 0; + bool try_all_dl_modes = false; + uint8_t found = 0; uint32_t password = 0; uint8_t cmdp = 0; - uint8_t downlink_mode = 0; - uint8_t dl_mode = 0; + uint8_t downlink_mode = 0; + uint8_t dl_mode = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { @@ -2498,12 +2503,12 @@ static int CmdT55xxDetectPage1(const char *Cmd) { useGB = true; cmdp++; break; - case 'r': - downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; - if (downlink_mode == 4) try_all_dl_modes = true; - if (downlink_mode > 3) downlink_mode = 0; - cmdp +=2; - break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode == 4) try_all_dl_modes = true; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -2513,29 +2518,26 @@ static int CmdT55xxDetectPage1(const char *Cmd) { if (errors) return usage_t55xx_detectP1(); if (!useGB) { - for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { - found = AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, usepwd, password,dl_mode); + for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { + found = AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, usepwd, password, dl_mode); //return PM3_ENODATA; - if (tryDetectP1(false)) //tryDetectModulation()) - { - found = dl_mode; - dl_mode = 4; - } - else found = false; + if (tryDetectP1(false)) { //tryDetectModulation()) + found = dl_mode; + dl_mode = 4; + } else found = false; - if (!try_all_dl_modes) dl_mode = 4; - } - - } + if (!try_all_dl_modes) dl_mode = 4; + } + + } if (useGB) found = tryDetectP1(false); - if (found) { - PrintAndLogEx(SUCCESS, "T55xx chip found!"); - T55xx_Print_DownlinkMode (found); - } - else - PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); + if (found) { + PrintAndLogEx(SUCCESS, "T55xx chip found!"); + T55xx_Print_DownlinkMode(found); + } else + PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); return PM3_SUCCESS; @@ -2546,9 +2548,9 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { uint8_t write0 = 0, write1 = 0, write2 = 0, write3 = 0; bool errors = false, shall_persist = false; uint8_t cmdp = 0; - uint8_t downlink_mode = 0; - bool erase = false; - + uint8_t downlink_mode = 0; + bool erase = false; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -2583,7 +2585,7 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { break; case 'r': downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; - if (downlink_mode > 3) downlink_mode = 0; + if (downlink_mode > 3) downlink_mode = 0; cmdp += 2; break; case 'p': @@ -2604,23 +2606,22 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { //Validations if (errors || cmdp == 0) return usage_lf_deviceconfig(); -// printf ("DLmode %d\n",downlink_mode); +// printf ("DLmode %d\n",downlink_mode); t55xx_config conf = {0}; - printf ("Size conf %zu\n",sizeof(conf)); - if (erase) { - memset (&conf,0xff, sizeof(conf)); - printf ("Conf.m[0] %x\n",conf.m[0].start_gap); - } - else { - - conf.m[downlink_mode].start_gap = startgap * 8; - conf.m[downlink_mode].write_gap = writegap * 8; - conf.m[downlink_mode].write_0 = write0 * 8; - conf.m[downlink_mode].write_1 = write1 * 8; - conf.m[downlink_mode].write_2 = write2 * 8; - conf.m[downlink_mode].write_3 = write3 * 8; - conf.m[downlink_mode].read_gap = readgap * 8; - } + printf("Size conf %zu\n", sizeof(conf)); + if (erase) { + memset(&conf, 0xff, sizeof(conf)); + printf("Conf.m[0] %x\n", conf.m[0].start_gap); + } else { + + conf.m[downlink_mode].start_gap = startgap * 8; + conf.m[downlink_mode].write_gap = writegap * 8; + conf.m[downlink_mode].write_0 = write0 * 8; + conf.m[downlink_mode].write_1 = write1 * 8; + conf.m[downlink_mode].write_2 = write2 * 8; + conf.m[downlink_mode].write_3 = write3 * 8; + conf.m[downlink_mode].read_gap = readgap * 8; + } clearCommandBuffer(); SendCommandOLD(CMD_SET_LF_T55XX_CONFIG, shall_persist, 0, 0, &conf, sizeof(t55xx_config)); return PM3_SUCCESS; diff --git a/client/scripting.c b/client/scripting.c index 703feffa4..4a2ff28d6 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -903,7 +903,7 @@ static int l_T55xx_readblock(lua_State *L) { // try reading the config block and verify that PWD bit is set before doing this! if (!override) { - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0,0)) { + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0, 0)) { return returnToLuaWithError(L, "Failed to read config block"); } @@ -920,7 +920,7 @@ static int l_T55xx_readblock(lua_State *L) { } } - if (!AquireData(usepage1, block, usepwd, password,0)) { + if (!AquireData(usepage1, block, usepwd, password, 0)) { return returnToLuaWithError(L, "Failed to aquire data from card"); } @@ -977,7 +977,7 @@ static int l_T55xx_detect(lua_State *L) { if (!useGB) { - isok = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,0); + isok = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password, 0); if (isok == false) { return returnToLuaWithError(L, "Failed to aquire LF signal data"); } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index ded330e1e..63054d3a1 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -129,20 +129,20 @@ typedef struct { } t55xx_config; */ -// Extended to support 1 of 4 timing +// Extended to support 1 of 4 timing typedef struct { - uint16_t start_gap ; - uint16_t write_gap ; - uint16_t write_0 ; - uint16_t write_1 ; - uint16_t write_2 ; - uint16_t write_3 ; - uint16_t read_gap ; + uint16_t start_gap ; + uint16_t write_gap ; + uint16_t write_0 ; + uint16_t write_1 ; + uint16_t write_2 ; + uint16_t write_3 ; + uint16_t read_gap ; } t55xx_config_t; // This setup will allow for the 4 downlink modes "m" as well as other items if needed. // Given the one struct we can then read/write to flash/client in one go. typedef struct { - t55xx_config_t m[4]; // mode + t55xx_config_t m[4]; // mode } t55xx_config; /*typedef struct { From ab043a68b826adff61129fa4b26ea3df5fe88830 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 23 Jul 2019 23:44:41 +0200 Subject: [PATCH 40/40] remove space in filename --- .../loclass/{loclass information.txt => loclass_information.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/loclass/{loclass information.txt => loclass_information.txt} (100%) diff --git a/client/loclass/loclass information.txt b/client/loclass/loclass_information.txt similarity index 100% rename from client/loclass/loclass information.txt rename to client/loclass/loclass_information.txt