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_SZ (1024 * 192)
22 #define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024)
23 #define SPIFFS_CFG_PHYS_ADDR (0)
24 #define SPIFFS_CFG_LOG_PAGE_SZ (256)
25 #define SPIFFS_CFG_LOG_BLOCK_SZ (4 * 1024)
26 #define LOG_PAGE_SIZE 256
27 #define RDV40_SPIFFS_WORKBUF_SZ (LOG_PAGE_SIZE * 2)
28 // Experimental : 4 full pages(LOG_PAGE_SIZE + file descript size) of cache for
29 // Reading and writing if reading cache is stable, writing cache may need more
30 // testing regarding power loss, page consistency checks, Garbage collector
31 // Flushing handling... in doubt, use maximal safetylevel as, in most of the
32 // case, will ensure a flush by rollbacking to previous Unmounted state
33 #define RDV40_SPIFFS_CACHE_SZ ((LOG_PAGE_SIZE + 32) * 4)
34 #define SPIFFS_FD_SIZE (32)
35 #define RDV40_SPIFFS_MAX_FD (3)
36 #define RDV40_SPIFFS_FDBUF_SZ (SPIFFS_FD_SIZE * RDV40_SPIFFS_MAX_FD)
38 #define RDV40_LLERASE_BLOCKSIZE (64*1024)
40 #define RDV40_SPIFFS_LAZY_HEADER \
42 if ((level == RDV40_SPIFFS_SAFETY_LAZY) || (level == RDV40_SPIFFS_SAFETY_SAFE)) { \
43 changed = rdv40_spiffs_lazy_mount(); \
46 #define RDV40_SPIFFS_SAFE_FOOTER \
47 if (level == RDV40_SPIFFS_SAFETY_SAFE) { \
48 changed = rdv40_spiffs_lazy_mount_rollback(changed); \
52 #define RDV40_SPIFFS_SAFE_FUNCTION(RDV40_SPIFFS_LLFUNCT) \
53 RDV40_SPIFFS_LAZY_HEADER \
54 RDV40_SPIFFS_LLFUNCT \
55 RDV40_SPIFFS_SAFE_FOOTER
61 ///// FLASH LEVEL R/W/E operations for feeding SPIFFS Driver/////////////////
62 static s32_t
rdv40_spiffs_llread(u32_t addr
, u32_t size
, u8_t
*dst
) {
64 if (!Flash_ReadData(addr
, dst
, size
)) {
70 static s32_t
rdv40_spiffs_llwrite(u32_t addr
, u32_t size
, u8_t
*src
) {
72 if (FlashInit() == false) {
75 Flash_Write(addr
, src
, size
);
79 static s32_t
rdv40_spiffs_llerase(u32_t addr
, u32_t size
) {
80 if (FlashInit() == false) {
84 if (g_dbglevel
>= DBG_DEBUG
) Dbprintf("LLERASEDBG : Orig addr : %d\n", addr
);
86 uint8_t block
, sector
= 0;
87 block
= addr
/ RDV40_LLERASE_BLOCKSIZE
;
89 addr
= addr
- (block
* RDV40_LLERASE_BLOCKSIZE
);
92 if (g_dbglevel
>= DBG_DEBUG
) Dbprintf("LLERASEDBG : Result addr : %d\n", addr
);
94 sector
= addr
/ SPIFFS_CFG_LOG_BLOCK_SZ
;
95 Flash_CheckBusy(BUSY_TIMEOUT
);
98 if (g_dbglevel
>= DBG_DEBUG
) Dbprintf("LLERASEDBG : block : %d, sector : %d \n", block
, sector
);
100 uint8_t erased
= Flash_Erase4k(block
, sector
);
101 Flash_CheckBusy(BUSY_TIMEOUT
);
104 // iceman: SPIFFS_OK expands to 0, erased is bool from Flash_Erase4k, which returns TRUE if ok.
105 // so this return logic looks wrong.
106 return (SPIFFS_OK
== erased
);
109 ////////////////////////////////////////////////////////////////////////////////
111 ////// SPIFFS LOW LEVEL OPERATIONS /////////////////////////////////////////////
112 static u8_t spiffs_work_buf
[RDV40_SPIFFS_WORKBUF_SZ
] __attribute__((aligned
));
113 static u8_t spiffs_fds
[RDV40_SPIFFS_FDBUF_SZ
] __attribute__((aligned
));
114 static u8_t spiffs_cache_buf
[RDV40_SPIFFS_CACHE_SZ
] __attribute__((aligned
));
118 static enum spiffs_mount_status
{
119 RDV40_SPIFFS_UNMOUNTED
,
120 RDV40_SPIFFS_MOUNTED
,
122 } RDV40_SPIFFS_MOUNT_STATUS
;
124 static int rdv40_spiffs_mounted(void) {
127 switch (RDV40_SPIFFS_MOUNT_STATUS
) {
128 case RDV40_SPIFFS_MOUNTED
:
131 case RDV40_SPIFFS_UNMOUNTED
:
132 case RDV40_SPIFFS_UNKNOWN
:
140 int rdv40_spiffs_mount(void) {
141 if (rdv40_spiffs_mounted()) {
142 Dbprintf("ERR: SPIFFS already mounted !");
143 return SPIFFS_ERR_MOUNTED
;
147 cfg
.hal_read_f
= rdv40_spiffs_llread
;
148 cfg
.hal_write_f
= rdv40_spiffs_llwrite
;
149 cfg
.hal_erase_f
= rdv40_spiffs_llerase
;
152 // int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds,
153 // sizeof(spiffs_fds), 0, 0, 0); cached version, experimental
154 int ret
= SPIFFS_mount(
161 sizeof(spiffs_cache_buf
),
165 if (ret
== SPIFFS_OK
) {
166 RDV40_SPIFFS_MOUNT_STATUS
= RDV40_SPIFFS_MOUNTED
;
171 int rdv40_spiffs_unmount(void) {
172 if (!rdv40_spiffs_mounted()) {
173 Dbprintf("ERR: SPIFFS not mounted !");
174 return SPIFFS_ERR_NOT_MOUNTED
;
177 SPIFFS_clearerr(&fs
);
180 int ret
= SPIFFS_errno(&fs
);
181 if (ret
== SPIFFS_OK
) {
182 RDV40_SPIFFS_MOUNT_STATUS
= RDV40_SPIFFS_UNMOUNTED
;
187 int rdv40_spiffs_check(void) {
188 rdv40_spiffs_lazy_mount();
190 SPIFFS_gc_quick(&fs
, 0);
191 rdv40_spiffs_lazy_unmount();
192 rdv40_spiffs_lazy_mount();
193 return SPIFFS_gc(&fs
, 8192) == SPIFFS_OK
;
195 ////////////////////////////////////////////////////////////////////////////////
197 ///// Base RDV40_SPIFFS_SAFETY_NORMAL operations////////////////////////////////
199 void write_to_spiffs(const char *filename
, const uint8_t *src
, uint32_t size
) {
200 spiffs_file fd
= SPIFFS_open(&fs
, filename
, SPIFFS_CREAT
| SPIFFS_TRUNC
| SPIFFS_RDWR
, 0);
201 // Note: SPIFFS_write() doesn't declare third parameter as const (but should)
202 if (SPIFFS_write(&fs
, fd
, (void *)src
, size
) < 0) {
203 Dbprintf("wr errno %i\n", SPIFFS_errno(&fs
));
205 SPIFFS_close(&fs
, fd
);
208 void append_to_spiffs(const char *filename
, const uint8_t *src
, uint32_t size
) {
209 spiffs_file fd
= SPIFFS_open(&fs
, filename
, SPIFFS_APPEND
| SPIFFS_RDWR
, 0);
210 // Note: SPIFFS_write() doesn't declare third parameter as const (but should)
211 if (SPIFFS_write(&fs
, fd
, (void *)src
, size
) < 0) {
212 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
214 SPIFFS_close(&fs
, fd
);
217 void read_from_spiffs(const char *filename
, uint8_t *dst
, uint32_t size
) {
218 spiffs_file fd
= SPIFFS_open(&fs
, filename
, SPIFFS_RDWR
, 0);
219 if (SPIFFS_read(&fs
, fd
, dst
, size
) < 0) {
220 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
222 SPIFFS_close(&fs
, fd
);
225 static void rename_in_spiffs(const char *old_filename
, const char *new_filename
) {
226 if (SPIFFS_rename(&fs
, old_filename
, new_filename
) < 0) {
227 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
231 static void remove_from_spiffs(const char *filename
) {
232 if (SPIFFS_remove(&fs
, filename
) < 0) {
233 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
237 uint32_t size_in_spiffs(const char *filename
) {
239 if (SPIFFS_stat(&fs
, filename
, &s
) < 0) {
240 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
246 static rdv40_spiffs_fsinfo
info_of_spiffs(void) {
247 rdv40_spiffs_fsinfo fsinfo
;
248 fsinfo
.blockSize
= SPIFFS_CFG_LOG_BLOCK_SZ
;
249 fsinfo
.pageSize
= LOG_PAGE_SIZE
;
250 fsinfo
.maxOpenFiles
= RDV40_SPIFFS_MAX_FD
;
251 fsinfo
.maxPathLength
= SPIFFS_OBJ_NAME_LEN
;
253 if (SPIFFS_info(&fs
, &fsinfo
.totalBytes
, &fsinfo
.usedBytes
) < 0) {
254 Dbprintf("errno %i\n", SPIFFS_errno(&fs
));
257 fsinfo
.freeBytes
= fsinfo
.totalBytes
- fsinfo
.usedBytes
;
258 // Rounding without float may be improved
259 fsinfo
.usedPercent
= ((100 * fsinfo
.usedBytes
) + (fsinfo
.totalBytes
/ 2)) / fsinfo
.totalBytes
;
260 fsinfo
.freePercent
= (100 - fsinfo
.usedPercent
);
264 int exists_in_spiffs(const char *filename
) {
266 int rc
= SPIFFS_stat(&fs
, filename
, &stat
);
267 return (rc
== SPIFFS_OK
);
270 static RDV40SpiFFSFileType
filetype_in_spiffs(const char *filename
) {
271 RDV40SpiFFSFileType filetype
= RDV40_SPIFFS_FILETYPE_UNKNOWN
;
272 char symlinked
[SPIFFS_OBJ_NAME_LEN
];
273 sprintf(symlinked
, "%s.lnk", filename
);
275 if (exists_in_spiffs(filename
)) {
276 filetype
= RDV40_SPIFFS_FILETYPE_REAL
;
279 if (exists_in_spiffs(symlinked
)) {
280 if (filetype
!= RDV40_SPIFFS_FILETYPE_UNKNOWN
) {
281 filetype
= RDV40_SPIFFS_FILETYPE_BOTH
;
283 filetype
= RDV40_SPIFFS_FILETYPE_SYMLINK
;
287 if (g_dbglevel
>= DBG_DEBUG
) {
289 case RDV40_SPIFFS_FILETYPE_REAL
:
290 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_REAL"));
292 case RDV40_SPIFFS_FILETYPE_SYMLINK
:
293 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_SYMLINK"));
295 case RDV40_SPIFFS_FILETYPE_BOTH
:
296 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_BOTH"));
298 case RDV40_SPIFFS_FILETYPE_UNKNOWN
:
299 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_UNKNOWN"));
306 static int is_valid_filename(const char *filename) {
307 if (filename == NULL) {
310 uint32_t len = strlen(filename);
311 return len > 0 && len < SPIFFS_OBJ_NAME_LEN;
314 static void copy_in_spiffs(const char *src
, const char *dst
) {
315 uint32_t size
= size_in_spiffs(src
);
316 uint8_t *mem
= BigBuf_malloc(size
);
317 read_from_spiffs(src
, (uint8_t *)mem
, size
);
318 write_to_spiffs(dst
, (uint8_t *)mem
, size
);
321 ////////////////////////////////////////////////////////////////////////////////
323 ////// Abstract Operations for base Safetyness /////////////////////////////////
325 // mount if not already
326 // As an "hint" to the behavior one should adopt after his or her laziness
327 // it will return 0 if the call was a noop, either because it did not need to
328 // change OR because it wasn't ABLE to change :)
329 // 1 if the mount status actually changed
330 // so you know what to do IN CASE you wished to set things "back to previous
332 int rdv40_spiffs_lazy_mount(void) {
334 if (!rdv40_spiffs_mounted()) {
335 changed
= rdv40_spiffs_mount();
336 /* if changed = 0 = SPIFFS_OK then all went well then the change
337 * actually occurred :)*/
343 // unmount if not already
344 int rdv40_spiffs_lazy_unmount(void) {
346 if (rdv40_spiffs_mounted()) {
347 changed
= rdv40_spiffs_unmount();
353 // Before further Reading, it is required to have in mind that UNMOUTING is
354 // important in some ways Because it is the ONLY operation which ensure that
355 // -> all Caches and writings are flushed to the FS
356 // -> all FD are properly closed
357 // -> Every best effort has been done to ensure consistency and integrity of the
358 // state reputated to be the actual state of the Filesystem.
361 // This will "toggle" mount status
362 // on "changement" conditional
363 // so it is for the former lazy_ mounting function to actually rollback or not
364 // depending on the result of the previous This is super lazy implementation as
365 // it is either a toggle to previous state or again a noop as everything was a
366 // nonevent If you have a function which NEEDS mounting but you want to exit
367 // this function in the very state mounting was before your intervention all
368 // things can now be transparent like
370 void my_lazy_spiffs_act(){
371 uint8_t changed = rdv40_spiffs_lazy_mount();
372 [..] Do what you have to do with spiffs
373 rdv40_spiffs_lazy_rollback(changed)
376 // The exact same goes for needed unmouting with eventual rollback, you just
377 // have to use lazy_unmount insted of lazy mount This way, you can ensure
378 // consistency in operation even with complex chain of mounting and unmounting
379 // Lets's say you are in a function which needs to mount if not already, and in
380 // the middle of itself calls function which indeed will need to unmount if not
381 // already. Well you better use safe or wrapped function which are made to
382 // rollback to previous state, so you can continue right after to do your
385 // As an extreme example: let's imagine that we have a function which is made
386 // to FORMAT the whole SPIFFS if a SPECIFIC content is written in the 4 first
387 // byte of that file. Also in such a case it should quickly create a bunch of
388 // skkeleton to get itself back to a known and wanted state. This behavior has
389 // to be done at every "manual" (not a lazy or internal event) mounting, just
390 // like an action upon boot.
392 void my_spiffs_boot(){
394 // this lazy_mount since needed and can also report back the change on
395 state implied by eventual mount, if needed rdv40_spiffs_lazy_read((const char
396 *)".SHOULDRESET",(uint8_t *)resetret,4); if( resetret == "YESS" ) { uint8_t
397 changed = rdv40_spiffs_lazy_format(void); // this will imply change only if we where
398 already mounted beforehand, was the case after our reading without further
399 rollback rdv40_spiffs_lazy_mount_rollback(changed); // so if we were mounted
400 just get back to this state. If not, just don't.
406 // Again : This will "toggle" spiffs mount status only if a "change" occurred
407 // (and should be fed by the result of a spiffs_lazy* function) If everything
408 // went well, it will return SPIFFS_OK if everything went well, and a report
409 // back the chain a SPI_ERRNO if not.
410 int rdv40_spiffs_lazy_mount_rollback(int changed
) {
415 if (rdv40_spiffs_mounted()) {
416 return rdv40_spiffs_unmount();
418 return rdv40_spiffs_mount();
420 ///////////////////////////////////////////////////////////////////////////////
422 // High level functions with SafetyLevel
423 // Beware that different safety level makes different return behavior
425 // RDV40_SPIFFS_SAFETY_NORMAL : will operate withtout further change on mount
426 // status RDV40_SPIFFS_SAFETY_LAZY : will ensure mount status already being in
427 // correct state before ops,
428 // will return !false if mount state had to change
429 // RDV40_SPIFFS_SAFETY_SAFE : will do same as RDV40_SPIFFS_SAFETY_LAZY
430 // will also safely rollback to previous state IF
431 // mount state had to change will return SPIFFS_OK /
432 // 0 / false if everything went well
434 // TODO : this functions are common enough to be unified with a switchcase
435 // statement or some function taking function parameters
436 // TODO : forbid writing to a filename which already exists as lnk !
437 // TODO : forbid writing to a filename.lnk which already exists without lnk !
438 // Note: Writing in SPIFFS_WRITE_CHUNK_SIZE (8192) byte chucks helps to ensure "free space" has been erased by GC (Garbage collection)
439 int rdv40_spiffs_write(const char *filename
, const uint8_t *src
, uint32_t size
, RDV40SpiFFSSafetyLevel level
) {
440 RDV40_SPIFFS_SAFE_FUNCTION(
442 if (size
<= SPIFFS_WRITE_CHUNK_SIZE
) {
444 write_to_spiffs(filename
, src
, size
);
447 // write first SPIFFS_WRITE_CHUNK_SIZE bytes
448 // need to write the first chuck of data, then append
449 write_to_spiffs(filename
, src
, SPIFFS_WRITE_CHUNK_SIZE
);
451 // append remaing SPIFFS_WRITE_CHUNK_SIZE byte chuncks
452 for (idx
= 1; idx
< (size
/ SPIFFS_WRITE_CHUNK_SIZE
); idx
++) {
453 append_to_spiffs(filename
, &src
[SPIFFS_WRITE_CHUNK_SIZE
* idx
], SPIFFS_WRITE_CHUNK_SIZE
);
455 // append remaing bytes
456 if (((int64_t)size
- (SPIFFS_WRITE_CHUNK_SIZE
* idx
)) > 0) {
457 append_to_spiffs(filename
, &src
[SPIFFS_WRITE_CHUNK_SIZE
* idx
], size
- (SPIFFS_WRITE_CHUNK_SIZE
* idx
));
462 int rdv40_spiffs_append(const char *filename
, const uint8_t *src
, uint32_t size
, RDV40SpiFFSSafetyLevel level
) {
463 RDV40_SPIFFS_SAFE_FUNCTION(
465 // Append any SPIFFS_WRITE_CHUNK_SIZE byte chunks
466 for (idx
= 0; idx
< (size
/ SPIFFS_WRITE_CHUNK_SIZE
); idx
++) {
467 append_to_spiffs(filename
, &src
[SPIFFS_WRITE_CHUNK_SIZE
* idx
], SPIFFS_WRITE_CHUNK_SIZE
);
469 // Append remain bytes
470 if (((int64_t)size
- (SPIFFS_WRITE_CHUNK_SIZE
* idx
)) > 0) {
471 append_to_spiffs(filename
, &src
[SPIFFS_WRITE_CHUNK_SIZE
* idx
], size
- (SPIFFS_WRITE_CHUNK_SIZE
* idx
));
476 // todo integrate reading symlinks transparently
477 int rdv40_spiffs_read(const char *filename
, uint8_t *dst
, uint32_t size
, RDV40SpiFFSSafetyLevel level
) {
478 RDV40_SPIFFS_SAFE_FUNCTION(
479 read_from_spiffs(filename
, dst
, size
);
483 // TODO : forbid writing to a filename which already exists as lnk !
484 // TODO : forbid writing to a filename.lnk which already exists without lnk !
485 int rdv40_spiffs_rename(const char *old_filename
, const char *new_filename
, RDV40SpiFFSSafetyLevel level
) {
486 RDV40_SPIFFS_SAFE_FUNCTION( //
487 rename_in_spiffs(old_filename
, new_filename
); //
490 int rdv40_spiffs_remove(const char *filename
, RDV40SpiFFSSafetyLevel level
) {
491 RDV40_SPIFFS_SAFE_FUNCTION( //
492 remove_from_spiffs(filename
); //
496 int rdv40_spiffs_copy(const char *src_filename
, const char *dst_filename
, RDV40SpiFFSSafetyLevel level
) {
497 RDV40_SPIFFS_SAFE_FUNCTION( //
498 copy_in_spiffs(src_filename
, dst_filename
); //
502 int rdv40_spiffs_stat(const char *filename
, uint32_t *size_in_bytes
, RDV40SpiFFSSafetyLevel level
) {
503 RDV40_SPIFFS_SAFE_FUNCTION( //
504 *size_in_bytes
= size_in_spiffs(filename
); //
508 static int rdv40_spiffs_getfsinfo(rdv40_spiffs_fsinfo
*fsinfo
, RDV40SpiFFSSafetyLevel level
) {
509 RDV40_SPIFFS_SAFE_FUNCTION( //
510 *fsinfo
= info_of_spiffs(); //
514 // test for symlink from filename
515 int rdv40_spiffs_is_symlink(const char *s
) {
519 size_t size
= strlen(s
);
521 if (size
>= 4 && s
[size
- 4] == '.' && s
[size
- 3] == 'l' && s
[size
- 2] == 'n' && s
[size
- 1] == 'k') {
529 // since FILENAME can't be longer than 32Bytes as of hard configuration, we're
530 // safe with Such maximum. So the "size" variable is actually the known/intended
531 // size of DESTINATION file, may it be known (may we provide a "stat from
533 // ATTENTION : you must NOT provide the whole filename (so please do not include the .lnk extension)
534 // TODO : integrate in read_function
535 int rdv40_spiffs_read_as_symlink(const char *filename
, uint8_t *dst
, uint32_t size
, RDV40SpiFFSSafetyLevel level
) {
537 RDV40_SPIFFS_SAFE_FUNCTION(
538 char linkdest
[SPIFFS_OBJ_NAME_LEN
];
539 char linkfilename
[SPIFFS_OBJ_NAME_LEN
];
540 sprintf(linkfilename
, "%s.lnk", filename
);
542 if (g_dbglevel
>= DBG_DEBUG
)
543 Dbprintf("Link real filename is " _YELLOW_("%s"), linkfilename
);
545 read_from_spiffs((char *)linkfilename
, (uint8_t *)linkdest
, SPIFFS_OBJ_NAME_LEN
);
547 if (g_dbglevel
>= DBG_DEBUG
)
548 Dbprintf("Symlink destination is " _YELLOW_("%s"), linkdest
);
550 read_from_spiffs((char *)linkdest
, (uint8_t *)dst
, size
);
554 // BEWARE ! This function is DESTRUCTIVE as it will UPDATE an existing symlink
555 // Since it creates a .lnk extension file it may be minor to mistake the order of arguments
556 // Still please use this function with care.
557 // Also, remind that it will NOT check if destination filename actually exists
558 // As a mnenotechnic, think about the "ln" unix command, which order is the same as "cp" unix command
559 // in regard of arguments orders.
561 // rdv40_spiffs_make_symlink((uint8_t *)"hello", (uint8_t *)"world", RDV40_SPIFFS_SAFETY_SAFE)
562 // will generate a file named "world.lnk" with the path to file "hello" written in
563 // which you can then read back with :
564 // rdv40_spiffs_read_as_symlink((uint8_t *)"world",(uint8_t *) buffer, orig_file_size, RDV40_SPIFFS_SAFETY_SAFE);
565 // TODO : FORBID creating a symlink with a basename (before.lnk) which already exists as a file !
566 int rdv40_spiffs_make_symlink(const char *linkdest
, const char *filename
, RDV40SpiFFSSafetyLevel level
) {
567 RDV40_SPIFFS_SAFE_FUNCTION(
568 char linkfilename
[SPIFFS_OBJ_NAME_LEN
];
569 sprintf(linkfilename
, "%s.lnk", filename
);
570 write_to_spiffs(linkfilename
, (const uint8_t *)linkdest
, SPIFFS_OBJ_NAME_LEN
);
574 // filename and filename.lnk will both the existence-checked
575 // if filename exists, read will be used, if filename.lnk exists, read_as_symlink will be used
576 // Both existence is not handled right now and should not happen or create a default fallback behavior
577 // Still, this case won't happen when the write(s) functions will check for both symlink and real file
578 // preexistence, avoiding a link being created if filename exists, or avoiding a file being created if
579 // symlink exists with same name
580 int rdv40_spiffs_read_as_filetype(const char *filename
, uint8_t *dst
, uint32_t size
, RDV40SpiFFSSafetyLevel level
) {
581 RDV40_SPIFFS_SAFE_FUNCTION(
582 RDV40SpiFFSFileType filetype
= filetype_in_spiffs((char *)filename
);
584 case RDV40_SPIFFS_FILETYPE_REAL
:
585 rdv40_spiffs_read(filename
, dst
, size
, level
);
587 case RDV40_SPIFFS_FILETYPE_SYMLINK
:
588 rdv40_spiffs_read_as_symlink(filename
, dst
, size
, level
);
590 case RDV40_SPIFFS_FILETYPE_BOTH
:
591 case RDV40_SPIFFS_FILETYPE_UNKNOWN
:
598 // TODO regarding reads/write and symlinks :
599 // Provide a higher level readFile function which
600 // - don't need a size to be provided, getting it from STAT call and using bigbuff malloc
601 // - send back the whole read file as return Result
602 // Maybe a good think to implement a VFS api here.
604 ////////////////////////////////////////////////////////////////////////////////
606 ///////// MISC HIGH LEVEL FUNCTIONS ////////////////////////////////////////////
607 #define SPIFFS_BANNER DbpString(_CYAN_("Flash Memory FileSystem tree (SPIFFS)"));
609 void rdv40_spiffs_safe_print_fsinfo(void) {
610 rdv40_spiffs_fsinfo fsinfo
;
611 rdv40_spiffs_getfsinfo(&fsinfo
, RDV40_SPIFFS_SAFETY_SAFE
);
613 Dbprintf(" Logical block size... " _YELLOW_("%d")" bytes", fsinfo
.blockSize
);
614 Dbprintf(" Logical page size.... " _YELLOW_("%d")" bytes", fsinfo
.pageSize
);
615 Dbprintf(" Max open files....... " _YELLOW_("%d")" file descriptors", fsinfo
.maxOpenFiles
);
616 Dbprintf(" Max path length...... " _YELLOW_("%d")" chars", fsinfo
.maxPathLength
);
618 Dbprintf(" Filesystem size used available use% mounted");
619 DbpString("------------------------------------------------------------------");
620 Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /"
629 // this function is safe and WILL rollback since it is only a PRINTING function,
630 // not a function intended to give any sort of struct to manipulate the FS
632 // TODO : Fake the Directory availability by splitting strings , buffering,
633 // maintaining prefix list sorting, unique_checking, THEN outputting precomputed
634 // tree Other solution would be to add directory support to SPIFFS, but that we
635 // don't want, as prefix are way easier and lighter in every aspect.
636 void rdv40_spiffs_safe_print_tree(void) {
637 int changed
= rdv40_spiffs_lazy_mount();
639 struct spiffs_dirent e
;
640 struct spiffs_dirent
*pe
= &e
;
642 char *resolvedlink
= (char *)BigBuf_calloc(11 + SPIFFS_OBJ_NAME_LEN
);
643 char *linkdest
= (char *)BigBuf_calloc(SPIFFS_OBJ_NAME_LEN
);
644 bool printed
= false;
646 SPIFFS_opendir(&fs
, "/", &d
);
647 while ((pe
= SPIFFS_readdir(&d
, pe
))) {
649 memset(resolvedlink
, 0, 11 + SPIFFS_OBJ_NAME_LEN
);
651 if (rdv40_spiffs_is_symlink((const char *)pe
->name
)) {
653 read_from_spiffs((char *)pe
->name
, (uint8_t *)linkdest
, SPIFFS_OBJ_NAME_LEN
);
654 sprintf(resolvedlink
, "(.lnk) --> %s", linkdest
);
655 // Kind of stripping the .lnk extension
656 strtok((char *)pe
->name
, ".");
659 Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe
->obj_id
, pe
->size
, pe
->name
, resolvedlink
);
662 if (printed
== false) {
663 DbpString("<empty>");
666 rdv40_spiffs_lazy_mount_rollback(changed
);
670 void rdv40_spiffs_safe_wipe(void) {
672 int changed
= rdv40_spiffs_lazy_mount();
675 struct spiffs_dirent e
;
676 struct spiffs_dirent
*pe
= &e
;
677 SPIFFS_opendir(&fs
, "/", &d
);
679 while ((pe
= SPIFFS_readdir(&d
, pe
))) {
681 if (rdv40_spiffs_is_symlink((const char *)pe
->name
)) {
683 char linkdest
[SPIFFS_OBJ_NAME_LEN
];
684 read_from_spiffs((char *)pe
->name
, (uint8_t *)linkdest
, SPIFFS_OBJ_NAME_LEN
);
686 remove_from_spiffs(linkdest
);
687 Dbprintf(".lnk removed %s", pe
->name
);
689 remove_from_spiffs((char *)pe
->name
);
690 Dbprintf("removed %s", linkdest
);
693 remove_from_spiffs((char *)pe
->name
);
694 Dbprintf("removed %s", pe
->name
);
699 rdv40_spiffs_lazy_mount_rollback(changed
);
703 void test_spiffs(void) {
704 Dbprintf("----------------------------------------------");
705 Dbprintf("Testing SPIFFS operations");
706 Dbprintf("----------------------------------------------");
707 Dbprintf("-- all test are made using lazy safetylevel");
709 Dbprintf(" Mounting filesystem (lazy).......");
710 int changed
= rdv40_spiffs_lazy_mount();
712 Dbprintf(" Printing tree..............");
713 rdv40_spiffs_safe_print_tree();
715 Dbprintf(" Writing 'I love Proxmark3 RDV4' in a testspiffs.txt");
717 // Since We lazy_mounted manually before hand, the write safety level will
719 rdv40_spiffs_write((char *)"testspiffs.txt", (uint8_t *)"I love Proxmark3 RDV4", 21, RDV40_SPIFFS_SAFETY_SAFE
);
721 Dbprintf(" Printing tree again.......");
722 rdv40_spiffs_safe_print_tree();
724 Dbprintf(" Making a symlink to testspiffs.txt");
725 rdv40_spiffs_make_symlink((char *)"testspiffs.txt", (char *)"linktotestspiffs.txt", RDV40_SPIFFS_SAFETY_SAFE
);
727 Dbprintf(" Printing tree again.......");
728 rdv40_spiffs_safe_print_tree();
730 // TODO READBACK, rename,print tree read back, remove, print tree;
731 Dbprintf(" Rollbacking The mount status IF things have changed");
732 rdv40_spiffs_lazy_mount_rollback(changed
);
734 Dbprintf(_GREEN_("All done"));
738 ///////////////////////////////////////////////////////////////////////////////