Makefile: display firmware size
[RRG-proxmark3.git] / armsrc / spiffs.c
blob9c5f96042f8fe26046781562bde7ed17312ca8a9
1 //-----------------------------------------------------------------------------
2 // Borrowed initially from https://github.com/pellepl/spiffs
3 // Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com)
4 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // See LICENSE.txt for the text of the license.
17 //-----------------------------------------------------------------------------
18 // SPIFFS api for RDV40 Integration
19 //-----------------------------------------------------------------------------
21 #define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024)
22 #define SPIFFS_CFG_PHYS_ADDR (0)
23 #define SPIFFS_CFG_LOG_PAGE_SZ (256)
24 #define SPIFFS_CFG_LOG_BLOCK_SZ (4 * 1024)
25 #define LOG_PAGE_SIZE 256
26 #define RDV40_SPIFFS_WORKBUF_SZ (LOG_PAGE_SIZE * 2)
27 // Experimental : 4 full pages(LOG_PAGE_SIZE + file descript size) of cache for
28 // Reading and writing if reading cache is stable, writing cache may need more
29 // testing regarding power loss, page consistency checks, Garbage collector
30 // Flushing handling... in doubt, use maximal safetylevel as, in most of the
31 // case, will ensure a flush by rollbacking to previous Unmounted state
32 #define RDV40_SPIFFS_CACHE_SZ ((LOG_PAGE_SIZE + 32) * 4)
33 #define SPIFFS_FD_SIZE (32)
34 #define RDV40_SPIFFS_MAX_FD (3)
35 #define RDV40_SPIFFS_FDBUF_SZ (SPIFFS_FD_SIZE * RDV40_SPIFFS_MAX_FD)
37 #define RDV40_LLERASE_BLOCKSIZE (64*1024)
39 #define RDV40_SPIFFS_LAZY_HEADER \
40 int changed = 0; \
41 if ((level == RDV40_SPIFFS_SAFETY_LAZY) || (level == RDV40_SPIFFS_SAFETY_SAFE)) { \
42 changed = rdv40_spiffs_lazy_mount(); \
45 #define RDV40_SPIFFS_SAFE_FOOTER \
46 if (level == RDV40_SPIFFS_SAFETY_SAFE) { \
47 changed = rdv40_spiffs_lazy_mount_rollback(changed); \
48 } \
49 return changed;
51 #define RDV40_SPIFFS_SAFE_FUNCTION(RDV40_SPIFFS_LLFUNCT) \
52 RDV40_SPIFFS_LAZY_HEADER \
53 RDV40_SPIFFS_LLFUNCT \
54 RDV40_SPIFFS_SAFE_FOOTER
56 #include "spiffs.h"
57 #include "BigBuf.h"
58 #include "dbprint.h"
60 ///// FLASH LEVEL R/W/E operations for feeding SPIFFS Driver/////////////////
61 static s32_t rdv40_spiffs_llread(u32_t addr, u32_t size, u8_t *dst) {
63 if (!Flash_ReadData(addr, dst, size)) {
64 return 128;
66 return SPIFFS_OK;
69 static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) {
71 if (FlashInit() == false) {
72 return 129;
74 Flash_Write(addr, src, size);
75 return SPIFFS_OK;
78 static s32_t rdv40_spiffs_llerase(u32_t addr, u32_t size) {
79 if (FlashInit() == false) {
80 return 130;
83 if (g_dbglevel >= DBG_DEBUG) Dbprintf("LLERASEDBG : Orig addr : %d\n", addr);
85 uint8_t block, sector = 0;
86 block = addr / RDV40_LLERASE_BLOCKSIZE;
87 if (block) {
88 addr = addr - (block * RDV40_LLERASE_BLOCKSIZE);
91 if (g_dbglevel >= DBG_DEBUG) Dbprintf("LLERASEDBG : Result addr : %d\n", addr);
93 sector = addr / SPIFFS_CFG_LOG_BLOCK_SZ;
94 Flash_CheckBusy(BUSY_TIMEOUT);
95 Flash_WriteEnable();
97 if (g_dbglevel >= DBG_DEBUG) Dbprintf("LLERASEDBG : block : %d, sector : %d \n", block, sector);
99 uint8_t erased = Flash_Erase4k(block, sector);
100 Flash_CheckBusy(BUSY_TIMEOUT);
101 FlashStop();
103 // iceman: SPIFFS_OK expands to 0, erased is bool from Flash_Erase4k, which returns TRUE if ok.
104 // so this return logic looks wrong.
105 return (SPIFFS_OK == erased);
108 ////////////////////////////////////////////////////////////////////////////////
110 ////// SPIFFS LOW LEVEL OPERATIONS /////////////////////////////////////////////
111 static u8_t spiffs_work_buf[RDV40_SPIFFS_WORKBUF_SZ] __attribute__((aligned));
112 static u8_t spiffs_fds[RDV40_SPIFFS_FDBUF_SZ] __attribute__((aligned));
113 static u8_t spiffs_cache_buf[RDV40_SPIFFS_CACHE_SZ] __attribute__((aligned));
115 static spiffs fs;
117 static enum spiffs_mount_status {
118 RDV40_SPIFFS_UNMOUNTED,
119 RDV40_SPIFFS_MOUNTED,
120 RDV40_SPIFFS_UNKNOWN
121 } RDV40_SPIFFS_MOUNT_STATUS;
123 static int rdv40_spiffs_mounted(void) {
124 int ret = 0;
126 switch (RDV40_SPIFFS_MOUNT_STATUS) {
127 case RDV40_SPIFFS_MOUNTED:
128 ret = 1;
129 break;
130 case RDV40_SPIFFS_UNMOUNTED:
131 case RDV40_SPIFFS_UNKNOWN:
132 default:
133 ret = 0;
136 return ret;
139 int rdv40_spiffs_mount(void) {
140 if (rdv40_spiffs_mounted()) {
141 Dbprintf("ERR: SPIFFS already mounted !");
142 return SPIFFS_ERR_MOUNTED;
145 spiffs_config cfg;
146 cfg.hal_read_f = rdv40_spiffs_llread;
147 cfg.hal_write_f = rdv40_spiffs_llwrite;
148 cfg.hal_erase_f = rdv40_spiffs_llerase;
150 // uncached version
151 // int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds,
152 // sizeof(spiffs_fds), 0, 0, 0); cached version, experimental
153 int ret = SPIFFS_mount(
154 &fs,
155 &cfg,
156 spiffs_work_buf,
157 spiffs_fds,
158 sizeof(spiffs_fds),
159 spiffs_cache_buf,
160 sizeof(spiffs_cache_buf),
164 if (ret == SPIFFS_OK) {
165 RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_MOUNTED;
167 return ret;
170 int rdv40_spiffs_unmount(void) {
171 if (!rdv40_spiffs_mounted()) {
172 Dbprintf("ERR: SPIFFS not mounted !");
173 return SPIFFS_ERR_NOT_MOUNTED;
176 SPIFFS_clearerr(&fs);
177 SPIFFS_unmount(&fs);
179 int ret = SPIFFS_errno(&fs);
180 if (ret == SPIFFS_OK) {
181 RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_UNMOUNTED;
183 return ret;
186 int rdv40_spiffs_check(void) {
187 rdv40_spiffs_lazy_mount();
188 SPIFFS_check(&fs);
189 SPIFFS_gc_quick(&fs, 0);
190 rdv40_spiffs_lazy_unmount();
191 rdv40_spiffs_lazy_mount();
192 return SPIFFS_gc(&fs, 8192) == SPIFFS_OK;
194 ////////////////////////////////////////////////////////////////////////////////
196 ///// Base RDV40_SPIFFS_SAFETY_NORMAL operations////////////////////////////////
198 void write_to_spiffs(const char *filename, const uint8_t *src, uint32_t size) {
199 spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
200 // Note: SPIFFS_write() doesn't declare third parameter as const (but should)
201 if (SPIFFS_write(&fs, fd, (void *)src, size) < 0) {
202 Dbprintf("wr errno %i\n", SPIFFS_errno(&fs));
204 SPIFFS_close(&fs, fd);
207 void append_to_spiffs(const char *filename, const uint8_t *src, uint32_t size) {
208 spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_APPEND | SPIFFS_RDWR, 0);
209 // Note: SPIFFS_write() doesn't declare third parameter as const (but should)
210 if (SPIFFS_write(&fs, fd, (void *)src, size) < 0) {
211 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
213 SPIFFS_close(&fs, fd);
216 void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size) {
217 spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_RDWR, 0);
218 if (SPIFFS_read(&fs, fd, dst, size) < 0) {
219 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
221 SPIFFS_close(&fs, fd);
224 static void rename_in_spiffs(const char *old_filename, const char *new_filename) {
225 if (SPIFFS_rename(&fs, old_filename, new_filename) < 0) {
226 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
230 static void remove_from_spiffs(const char *filename) {
231 if (SPIFFS_remove(&fs, filename) < 0) {
232 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
236 uint32_t size_in_spiffs(const char *filename) {
237 spiffs_stat s;
238 if (SPIFFS_stat(&fs, filename, &s) < 0) {
239 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
240 return 0;
242 return s.size;
245 static rdv40_spiffs_fsinfo info_of_spiffs(void) {
246 rdv40_spiffs_fsinfo fsinfo;
247 fsinfo.blockSize = SPIFFS_CFG_LOG_BLOCK_SZ;
248 fsinfo.pageSize = LOG_PAGE_SIZE;
249 fsinfo.maxOpenFiles = RDV40_SPIFFS_MAX_FD;
250 fsinfo.maxPathLength = SPIFFS_OBJ_NAME_LEN;
252 if (SPIFFS_info(&fs, &fsinfo.totalBytes, &fsinfo.usedBytes) < 0) {
253 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
256 fsinfo.freeBytes = fsinfo.totalBytes - fsinfo.usedBytes;
257 // Rounding without float may be improved
258 fsinfo.usedPercent = ((100 * fsinfo.usedBytes) + (fsinfo.totalBytes / 2)) / fsinfo.totalBytes;
259 fsinfo.freePercent = (100 - fsinfo.usedPercent);
260 return fsinfo;
263 int exists_in_spiffs(const char *filename) {
264 spiffs_stat stat;
265 int rc = SPIFFS_stat(&fs, filename, &stat);
266 return (rc == SPIFFS_OK);
269 static RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) {
270 RDV40SpiFFSFileType filetype = RDV40_SPIFFS_FILETYPE_UNKNOWN;
271 char symlinked[SPIFFS_OBJ_NAME_LEN];
272 sprintf(symlinked, "%s.lnk", filename);
274 if (exists_in_spiffs(filename)) {
275 filetype = RDV40_SPIFFS_FILETYPE_REAL;
278 if (exists_in_spiffs(symlinked)) {
279 if (filetype != RDV40_SPIFFS_FILETYPE_UNKNOWN) {
280 filetype = RDV40_SPIFFS_FILETYPE_BOTH;
281 } else {
282 filetype = RDV40_SPIFFS_FILETYPE_SYMLINK;
286 if (g_dbglevel >= DBG_DEBUG) {
287 switch (filetype) {
288 case RDV40_SPIFFS_FILETYPE_REAL:
289 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_REAL"));
290 break;
291 case RDV40_SPIFFS_FILETYPE_SYMLINK:
292 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_SYMLINK"));
293 break;
294 case RDV40_SPIFFS_FILETYPE_BOTH:
295 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_BOTH"));
296 break;
297 case RDV40_SPIFFS_FILETYPE_UNKNOWN:
298 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_UNKNOWN"));
299 break;
302 return filetype;
305 static int is_valid_filename(const char *filename) {
306 if (filename == NULL) {
307 return false;
309 uint32_t len = strlen(filename);
310 return len > 0 && len < SPIFFS_OBJ_NAME_LEN;
313 static void copy_in_spiffs(const char *src, const char *dst) {
314 uint32_t size = size_in_spiffs(src);
315 uint8_t *mem = BigBuf_malloc(size);
316 read_from_spiffs(src, (uint8_t *)mem, size);
317 write_to_spiffs(dst, (uint8_t *)mem, size);
320 ////////////////////////////////////////////////////////////////////////////////
322 ////// Abstract Operations for base Safetyness /////////////////////////////////
324 // mount if not already
325 // As an "hint" to the behavior one should adopt after his or her laziness
326 // it will return 0 if the call was a noop, either because it did not need to
327 // change OR because it wasn't ABLE to change :)
328 // 1 if the mount status actually changed
329 // so you know what to do IN CASE you wished to set things "back to previous
330 // state"
331 int rdv40_spiffs_lazy_mount(void) {
332 int changed = 0;
333 if (!rdv40_spiffs_mounted()) {
334 changed = rdv40_spiffs_mount();
335 /* if changed = 0 = SPIFFS_OK then all went well then the change
336 * actually occurred :)*/
337 changed = !changed;
339 return changed;
342 // unmount if not already
343 int rdv40_spiffs_lazy_unmount(void) {
344 int changed = 0;
345 if (rdv40_spiffs_mounted()) {
346 changed = rdv40_spiffs_unmount();
347 changed = !changed;
349 return changed;
352 // Before further Reading, it is required to have in mind that UNMOUTING is
353 // important in some ways Because it is the ONLY operation which ensure that
354 // -> all Caches and writings are flushed to the FS
355 // -> all FD are properly closed
356 // -> Every best effort has been done to ensure consistency and integrity of the
357 // state reputated to be the actual state of the Filesystem.
358 //---
360 // This will "toggle" mount status
361 // on "changement" conditional
362 // so it is for the former lazy_ mounting function to actually rollback or not
363 // depending on the result of the previous This is super lazy implementation as
364 // it is either a toggle to previous state or again a noop as everything was a
365 // nonevent If you have a function which NEEDS mounting but you want to exit
366 // this function in the very state mounting was before your intervention all
367 // things can now be transparent like
369 void my_lazy_spiffs_act(){
370 uint8_t changed = rdv40_spiffs_lazy_mount();
371 [..] Do what you have to do with spiffs
372 rdv40_spiffs_lazy_rollback(changed)
375 // The exact same goes for needed unmouting with eventual rollback, you just
376 // have to use lazy_unmount insted of lazy mount This way, you can ensure
377 // consistency in operation even with complex chain of mounting and unmounting
378 // Lets's say you are in a function which needs to mount if not already, and in
379 // the middle of itself calls function which indeed will need to unmount if not
380 // already. Well you better use safe or wrapped function which are made to
381 // rollback to previous state, so you can continue right after to do your
382 // things.
384 // As an extreme example: let's imagine that we have a function which is made
385 // to FORMAT the whole SPIFFS if a SPECIFIC content is written in the 4 first
386 // byte of that file. Also in such a case it should quickly create a bunch of
387 // skkeleton to get itself back to a known and wanted state. This behavior has
388 // to be done at every "manual" (not a lazy or internal event) mounting, just
389 // like an action upon boot.
391 void my_spiffs_boot(){
392 uint8_t resetret[4];
393 // this lazy_mount since needed and can also report back the change on
394 state implied by eventual mount, if needed rdv40_spiffs_lazy_read((const char
395 *)".SHOULDRESET",(uint8_t *)resetret,4); if( resetret == "YESS" ) { uint8_t
396 changed = rdv40_spiffs_lazy_format(void); // this will imply change only if we where
397 already mounted beforehand, was the case after our reading without further
398 rollback rdv40_spiffs_lazy_mount_rollback(changed); // so if we were mounted
399 just get back to this state. If not, just don't.
400 [...]
402 [...]
405 // Again : This will "toggle" spiffs mount status only if a "change" occurred
406 // (and should be fed by the result of a spiffs_lazy* function) If everything
407 // went well, it will return SPIFFS_OK if everything went well, and a report
408 // back the chain a SPI_ERRNO if not.
409 int rdv40_spiffs_lazy_mount_rollback(int changed) {
410 if (!changed) {
411 return SPIFFS_OK;
414 if (rdv40_spiffs_mounted()) {
415 return rdv40_spiffs_unmount();
417 return rdv40_spiffs_mount();
419 ///////////////////////////////////////////////////////////////////////////////
421 // High level functions with SafetyLevel
422 // Beware that different safety level makes different return behavior
424 // RDV40_SPIFFS_SAFETY_NORMAL : will operate withtout further change on mount
425 // status RDV40_SPIFFS_SAFETY_LAZY : will ensure mount status already being in
426 // correct state before ops,
427 // will return !false if mount state had to change
428 // RDV40_SPIFFS_SAFETY_SAFE : will do same as RDV40_SPIFFS_SAFETY_LAZY
429 // will also safely rollback to previous state IF
430 // mount state had to change will return SPIFFS_OK /
431 // 0 / false if everything went well
433 // TODO : this functions are common enough to be unified with a switchcase
434 // statement or some function taking function parameters
435 // TODO : forbid writing to a filename which already exists as lnk !
436 // TODO : forbid writing to a filename.lnk which already exists without lnk !
437 // Note: Writing in SPIFFS_WRITE_CHUNK_SIZE (8192) byte chucks helps to ensure "free space" has been erased by GC (Garbage collection)
438 int rdv40_spiffs_write(const char *filename, const uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) {
439 RDV40_SPIFFS_SAFE_FUNCTION(
440 uint32_t idx;
441 if (size <= SPIFFS_WRITE_CHUNK_SIZE) {
442 // write small file
443 write_to_spiffs(filename, src, size);
444 size = 0;
445 } else { //
446 // write first SPIFFS_WRITE_CHUNK_SIZE bytes
447 // need to write the first chuck of data, then append
448 write_to_spiffs(filename, src, SPIFFS_WRITE_CHUNK_SIZE);
450 // append remaing SPIFFS_WRITE_CHUNK_SIZE byte chuncks
451 for (idx = 1; idx < (size / SPIFFS_WRITE_CHUNK_SIZE); idx++) {
452 append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], SPIFFS_WRITE_CHUNK_SIZE);
454 // append remaing bytes
455 if (((int64_t)size - (SPIFFS_WRITE_CHUNK_SIZE * idx)) > 0) {
456 append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], size - (SPIFFS_WRITE_CHUNK_SIZE * idx));
461 int rdv40_spiffs_append(const char *filename, const uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) {
462 RDV40_SPIFFS_SAFE_FUNCTION(
463 uint32_t idx;
464 // Append any SPIFFS_WRITE_CHUNK_SIZE byte chunks
465 for (idx = 0; idx < (size / SPIFFS_WRITE_CHUNK_SIZE); idx++) {
466 append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], SPIFFS_WRITE_CHUNK_SIZE);
468 // Append remain bytes
469 if (((int64_t)size - (SPIFFS_WRITE_CHUNK_SIZE * idx)) > 0) {
470 append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], size - (SPIFFS_WRITE_CHUNK_SIZE * idx));
475 // todo integrate reading symlinks transparently
476 int rdv40_spiffs_read(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) {
477 RDV40_SPIFFS_SAFE_FUNCTION(
478 read_from_spiffs(filename, dst, size);
482 // TODO : forbid writing to a filename which already exists as lnk !
483 // TODO : forbid writing to a filename.lnk which already exists without lnk !
484 int rdv40_spiffs_rename(const char *old_filename, const char *new_filename, RDV40SpiFFSSafetyLevel level) {
485 RDV40_SPIFFS_SAFE_FUNCTION( //
486 rename_in_spiffs(old_filename, new_filename); //
489 int rdv40_spiffs_remove(const char *filename, RDV40SpiFFSSafetyLevel level) {
490 RDV40_SPIFFS_SAFE_FUNCTION( //
491 remove_from_spiffs(filename); //
495 int rdv40_spiffs_copy(const char *src_filename, const char *dst_filename, RDV40SpiFFSSafetyLevel level) {
496 RDV40_SPIFFS_SAFE_FUNCTION( //
497 copy_in_spiffs(src_filename, dst_filename); //
501 int rdv40_spiffs_stat(const char *filename, uint32_t *size_in_bytes, RDV40SpiFFSSafetyLevel level) {
502 RDV40_SPIFFS_SAFE_FUNCTION( //
503 *size_in_bytes = size_in_spiffs(filename); //
507 static int rdv40_spiffs_getfsinfo(rdv40_spiffs_fsinfo *fsinfo, RDV40SpiFFSSafetyLevel level) {
508 RDV40_SPIFFS_SAFE_FUNCTION( //
509 *fsinfo = info_of_spiffs(); //
513 // test for symlink from filename
514 int rdv40_spiffs_is_symlink(const char *s) {
515 int ret = 0;
517 if (s != NULL) {
518 size_t size = strlen(s);
520 if (size >= 4 && s[size - 4] == '.' && s[size - 3] == 'l' && s[size - 2] == 'n' && s[size - 1] == 'k') {
521 ret = 1;
525 return ret;
528 // since FILENAME can't be longer than 32Bytes as of hard configuration, we're
529 // safe with Such maximum. So the "size" variable is actually the known/intended
530 // size of DESTINATION file, may it be known (may we provide a "stat from
531 // symlink ?")
532 // ATTENTION : you must NOT provide the whole filename (so please do not include the .lnk extension)
533 // TODO : integrate in read_function
534 int rdv40_spiffs_read_as_symlink(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) {
536 RDV40_SPIFFS_SAFE_FUNCTION(
537 char linkdest[SPIFFS_OBJ_NAME_LEN];
538 char linkfilename[SPIFFS_OBJ_NAME_LEN];
539 sprintf(linkfilename, "%s.lnk", filename);
541 if (g_dbglevel >= DBG_DEBUG)
542 Dbprintf("Link real filename is " _YELLOW_("%s"), linkfilename);
544 read_from_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
546 if (g_dbglevel >= DBG_DEBUG)
547 Dbprintf("Symlink destination is " _YELLOW_("%s"), linkdest);
549 read_from_spiffs((char *)linkdest, (uint8_t *)dst, size);
553 // BEWARE ! This function is DESTRUCTIVE as it will UPDATE an existing symlink
554 // Since it creates a .lnk extension file it may be minor to mistake the order of arguments
555 // Still please use this function with care.
556 // Also, remind that it will NOT check if destination filename actually exists
557 // As a mnenotechnic, think about the "ln" unix command, which order is the same as "cp" unix command
558 // in regard of arguments orders.
559 // Eg :
560 // rdv40_spiffs_make_symlink((uint8_t *)"hello", (uint8_t *)"world", RDV40_SPIFFS_SAFETY_SAFE)
561 // will generate a file named "world.lnk" with the path to file "hello" written in
562 // which you can then read back with :
563 // rdv40_spiffs_read_as_symlink((uint8_t *)"world",(uint8_t *) buffer, orig_file_size, RDV40_SPIFFS_SAFETY_SAFE);
564 // TODO : FORBID creating a symlink with a basename (before.lnk) which already exists as a file !
565 int rdv40_spiffs_make_symlink(const char *linkdest, const char *filename, RDV40SpiFFSSafetyLevel level) {
566 RDV40_SPIFFS_SAFE_FUNCTION(
567 char linkfilename[SPIFFS_OBJ_NAME_LEN];
568 sprintf(linkfilename, "%s.lnk", filename);
569 write_to_spiffs(linkfilename, (const uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
573 // filename and filename.lnk will both the existence-checked
574 // if filename exists, read will be used, if filename.lnk exists, read_as_symlink will be used
575 // Both existence is not handled right now and should not happen or create a default fallback behavior
576 // Still, this case won't happen when the write(s) functions will check for both symlink and real file
577 // preexistence, avoiding a link being created if filename exists, or avoiding a file being created if
578 // symlink exists with same name
579 int rdv40_spiffs_read_as_filetype(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) {
580 RDV40_SPIFFS_SAFE_FUNCTION(
581 RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename);
582 switch (filetype) {
583 case RDV40_SPIFFS_FILETYPE_REAL:
584 rdv40_spiffs_read(filename, dst, size, level);
585 break;
586 case RDV40_SPIFFS_FILETYPE_SYMLINK:
587 rdv40_spiffs_read_as_symlink(filename, dst, size, level);
588 break;
589 case RDV40_SPIFFS_FILETYPE_BOTH:
590 case RDV40_SPIFFS_FILETYPE_UNKNOWN:
591 default:
592 break;
597 // TODO regarding reads/write and symlinks :
598 // Provide a higher level readFile function which
599 // - don't need a size to be provided, getting it from STAT call and using bigbuff malloc
600 // - send back the whole read file as return Result
601 // Maybe a good think to implement a VFS api here.
603 ////////////////////////////////////////////////////////////////////////////////
605 ///////// MISC HIGH LEVEL FUNCTIONS ////////////////////////////////////////////
606 #define SPIFFS_BANNER DbpString(_CYAN_("Flash Memory FileSystem tree (SPIFFS)"));
608 void rdv40_spiffs_safe_print_fsinfo(void) {
609 rdv40_spiffs_fsinfo fsinfo;
610 rdv40_spiffs_getfsinfo(&fsinfo, RDV40_SPIFFS_SAFETY_SAFE);
612 Dbprintf(" Logical block size... " _YELLOW_("%d")" bytes", fsinfo.blockSize);
613 Dbprintf(" Logical page size.... " _YELLOW_("%d")" bytes", fsinfo.pageSize);
614 Dbprintf(" Max open files....... " _YELLOW_("%d")" file descriptors", fsinfo.maxOpenFiles);
615 Dbprintf(" Max path length...... " _YELLOW_("%d")" chars", fsinfo.maxPathLength);
616 DbpString("");
617 Dbprintf(" Filesystem size used available use% mounted");
618 DbpString("------------------------------------------------------------------");
619 Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /"
620 , fsinfo.totalBytes
621 , fsinfo.usedBytes
622 , fsinfo.freeBytes
623 , fsinfo.usedPercent
625 DbpString("");
628 // this function is safe and WILL rollback since it is only a PRINTING function,
629 // not a function intended to give any sort of struct to manipulate the FS
630 // objects
631 // TODO : Fake the Directory availability by splitting strings , buffering,
632 // maintaining prefix list sorting, unique_checking, THEN outputting precomputed
633 // tree Other solution would be to add directory support to SPIFFS, but that we
634 // don't want, as prefix are way easier and lighter in every aspect.
635 void rdv40_spiffs_safe_print_tree(void) {
636 int changed = rdv40_spiffs_lazy_mount();
637 spiffs_DIR d;
638 struct spiffs_dirent e;
639 struct spiffs_dirent *pe = &e;
641 char *resolvedlink = (char *)BigBuf_calloc(11 + SPIFFS_OBJ_NAME_LEN);
642 char *linkdest = (char *)BigBuf_calloc(SPIFFS_OBJ_NAME_LEN);
643 bool printed = false;
645 SPIFFS_opendir(&fs, "/", &d);
646 while ((pe = SPIFFS_readdir(&d, pe))) {
648 memset(resolvedlink, 0, 11 + SPIFFS_OBJ_NAME_LEN);
650 if (rdv40_spiffs_is_symlink((const char *)pe->name)) {
652 read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
653 sprintf(resolvedlink, "(.lnk) --> %s", linkdest);
654 // Kind of stripping the .lnk extension
655 strtok((char *)pe->name, ".");
658 Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink);
659 printed = true;
661 if (printed == false) {
662 DbpString("<empty>");
664 SPIFFS_closedir(&d);
665 rdv40_spiffs_lazy_mount_rollback(changed);
666 BigBuf_free();
669 void rdv40_spiffs_safe_wipe(void) {
671 int changed = rdv40_spiffs_lazy_mount();
673 spiffs_DIR d;
674 struct spiffs_dirent e;
675 struct spiffs_dirent *pe = &e;
676 SPIFFS_opendir(&fs, "/", &d);
678 while ((pe = SPIFFS_readdir(&d, pe))) {
680 if (rdv40_spiffs_is_symlink((const char *)pe->name)) {
682 char linkdest[SPIFFS_OBJ_NAME_LEN];
683 read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
685 remove_from_spiffs(linkdest);
686 Dbprintf(".lnk removed %s", pe->name);
688 remove_from_spiffs((char *)pe->name);
689 Dbprintf("removed %s", linkdest);
691 } else {
692 remove_from_spiffs((char *)pe->name);
693 Dbprintf("removed %s", pe->name);
697 SPIFFS_closedir(&d);
698 rdv40_spiffs_lazy_mount_rollback(changed);
701 // Selftest function
702 void test_spiffs(void) {
703 Dbprintf("----------------------------------------------");
704 Dbprintf("Testing SPIFFS operations");
705 Dbprintf("----------------------------------------------");
706 Dbprintf("-- all test are made using lazy safetylevel");
708 Dbprintf(" Mounting filesystem (lazy).......");
709 int changed = rdv40_spiffs_lazy_mount();
711 Dbprintf(" Printing tree..............");
712 rdv40_spiffs_safe_print_tree();
714 Dbprintf(" Writing 'I love Proxmark3 RDV4' in a testspiffs.txt");
716 // Since We lazy_mounted manually before hand, the write safety level will
717 // just imply noops
718 rdv40_spiffs_write((char *)"testspiffs.txt", (uint8_t *)"I love Proxmark3 RDV4", 21, RDV40_SPIFFS_SAFETY_SAFE);
720 Dbprintf(" Printing tree again.......");
721 rdv40_spiffs_safe_print_tree();
723 Dbprintf(" Making a symlink to testspiffs.txt");
724 rdv40_spiffs_make_symlink((char *)"testspiffs.txt", (char *)"linktotestspiffs.txt", RDV40_SPIFFS_SAFETY_SAFE);
726 Dbprintf(" Printing tree again.......");
727 rdv40_spiffs_safe_print_tree();
729 // TODO READBACK, rename,print tree read back, remove, print tree;
730 Dbprintf(" Rollbacking The mount status IF things have changed");
731 rdv40_spiffs_lazy_mount_rollback(changed);
733 Dbprintf(_GREEN_("All done"));
734 return;
737 ///////////////////////////////////////////////////////////////////////////////