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.
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 \
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); \
51 #define RDV40_SPIFFS_SAFE_FUNCTION(RDV40_SPIFFS_LLFUNCT) \
52 RDV40_SPIFFS_LAZY_HEADER \
53 RDV40_SPIFFS_LLFUNCT \
54 RDV40_SPIFFS_SAFE_FOOTER
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
)) {
69 static s32_t
rdv40_spiffs_llwrite(u32_t addr
, u32_t size
, u8_t
*src
) {
71 if (FlashInit() == false) {
74 Flash_Write(addr
, src
, size
);
78 static s32_t
rdv40_spiffs_llerase(u32_t addr
, u32_t size
) {
79 if (FlashInit() == false) {
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
;
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
);
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
);
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
));
117 static enum spiffs_mount_status
{
118 RDV40_SPIFFS_UNMOUNTED
,
119 RDV40_SPIFFS_MOUNTED
,
121 } RDV40_SPIFFS_MOUNT_STATUS
;
123 static int rdv40_spiffs_mounted(void) {
126 switch (RDV40_SPIFFS_MOUNT_STATUS
) {
127 case RDV40_SPIFFS_MOUNTED
:
130 case RDV40_SPIFFS_UNMOUNTED
:
131 case RDV40_SPIFFS_UNKNOWN
:
139 int rdv40_spiffs_mount(void) {
140 if (rdv40_spiffs_mounted()) {
141 Dbprintf("ERR: SPIFFS already mounted !");
142 return SPIFFS_ERR_MOUNTED
;
146 cfg
.hal_read_f
= rdv40_spiffs_llread
;
147 cfg
.hal_write_f
= rdv40_spiffs_llwrite
;
148 cfg
.hal_erase_f
= rdv40_spiffs_llerase
;
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(
160 sizeof(spiffs_cache_buf
),
164 if (ret
== SPIFFS_OK
) {
165 RDV40_SPIFFS_MOUNT_STATUS
= RDV40_SPIFFS_MOUNTED
;
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
);
179 int ret
= SPIFFS_errno(&fs
);
180 if (ret
== SPIFFS_OK
) {
181 RDV40_SPIFFS_MOUNT_STATUS
= RDV40_SPIFFS_UNMOUNTED
;
186 int rdv40_spiffs_check(void) {
187 rdv40_spiffs_lazy_mount();
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
) {
238 if (SPIFFS_stat(&fs
, filename
, &s
) < 0) {
239 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
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
);
263 int exists_in_spiffs(const char *filename
) {
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
;
282 filetype
= RDV40_SPIFFS_FILETYPE_SYMLINK
;
286 if (g_dbglevel
>= DBG_DEBUG
) {
288 case RDV40_SPIFFS_FILETYPE_REAL
:
289 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_REAL"));
291 case RDV40_SPIFFS_FILETYPE_SYMLINK
:
292 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_SYMLINK"));
294 case RDV40_SPIFFS_FILETYPE_BOTH
:
295 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_BOTH"));
297 case RDV40_SPIFFS_FILETYPE_UNKNOWN
:
298 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_UNKNOWN"));
305 static int is_valid_filename(const char *filename) {
306 if (filename == NULL) {
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
331 int rdv40_spiffs_lazy_mount(void) {
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 :)*/
342 // unmount if not already
343 int rdv40_spiffs_lazy_unmount(void) {
345 if (rdv40_spiffs_mounted()) {
346 changed
= rdv40_spiffs_unmount();
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.
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
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(){
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.
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
) {
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(
441 if (size
<= SPIFFS_WRITE_CHUNK_SIZE
) {
443 write_to_spiffs(filename
, src
, size
);
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(
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
) {
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') {
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
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.
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
);
583 case RDV40_SPIFFS_FILETYPE_REAL
:
584 rdv40_spiffs_read(filename
, dst
, size
, level
);
586 case RDV40_SPIFFS_FILETYPE_SYMLINK
:
587 rdv40_spiffs_read_as_symlink(filename
, dst
, size
, level
);
589 case RDV40_SPIFFS_FILETYPE_BOTH
:
590 case RDV40_SPIFFS_FILETYPE_UNKNOWN
:
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
);
617 Dbprintf(" Filesystem size used available use% mounted");
618 DbpString("------------------------------------------------------------------");
619 Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /"
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
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();
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
);
661 if (printed
== false) {
662 DbpString("<empty>");
665 rdv40_spiffs_lazy_mount_rollback(changed
);
669 void rdv40_spiffs_safe_wipe(void) {
671 int changed
= rdv40_spiffs_lazy_mount();
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
);
692 remove_from_spiffs((char *)pe
->name
);
693 Dbprintf("removed %s", pe
->name
);
698 rdv40_spiffs_lazy_mount_rollback(changed
);
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
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"));
737 ///////////////////////////////////////////////////////////////////////////////