mfkey32_moebius: no need to wait for 20 keys to see there are more than 1 key...
[RRG-proxmark3.git] / armsrc / spiffs.c
blob7604f6db720f9e7750dd8c0fb96430bd4cfb8f8a
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_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 \
41 int changed = 0; \
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); \
49 } \
50 return changed;
52 #define RDV40_SPIFFS_SAFE_FUNCTION(RDV40_SPIFFS_LLFUNCT) \
53 RDV40_SPIFFS_LAZY_HEADER \
54 RDV40_SPIFFS_LLFUNCT \
55 RDV40_SPIFFS_SAFE_FOOTER
57 #include "spiffs.h"
58 #include "BigBuf.h"
59 #include "dbprint.h"
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)) {
65 return 128;
67 return SPIFFS_OK;
70 static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) {
72 if (FlashInit() == false) {
73 return 129;
75 Flash_Write(addr, src, size);
76 return SPIFFS_OK;
79 static s32_t rdv40_spiffs_llerase(u32_t addr, u32_t size) {
80 if (FlashInit() == false) {
81 return 130;
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;
88 if (block) {
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);
96 Flash_WriteEnable();
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);
102 FlashStop();
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));
116 static spiffs fs;
118 static enum spiffs_mount_status {
119 RDV40_SPIFFS_UNMOUNTED,
120 RDV40_SPIFFS_MOUNTED,
121 RDV40_SPIFFS_UNKNOWN
122 } RDV40_SPIFFS_MOUNT_STATUS;
124 static int rdv40_spiffs_mounted(void) {
125 int ret = 0;
127 switch (RDV40_SPIFFS_MOUNT_STATUS) {
128 case RDV40_SPIFFS_MOUNTED:
129 ret = 1;
130 break;
131 case RDV40_SPIFFS_UNMOUNTED:
132 case RDV40_SPIFFS_UNKNOWN:
133 default:
134 ret = 0;
137 return ret;
140 int rdv40_spiffs_mount(void) {
141 if (rdv40_spiffs_mounted()) {
142 Dbprintf("ERR: SPIFFS already mounted !");
143 return SPIFFS_ERR_MOUNTED;
146 spiffs_config cfg;
147 cfg.hal_read_f = rdv40_spiffs_llread;
148 cfg.hal_write_f = rdv40_spiffs_llwrite;
149 cfg.hal_erase_f = rdv40_spiffs_llerase;
151 // uncached version
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(
155 &fs,
156 &cfg,
157 spiffs_work_buf,
158 spiffs_fds,
159 sizeof(spiffs_fds),
160 spiffs_cache_buf,
161 sizeof(spiffs_cache_buf),
165 if (ret == SPIFFS_OK) {
166 RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_MOUNTED;
168 return ret;
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);
178 SPIFFS_unmount(&fs);
180 int ret = SPIFFS_errno(&fs);
181 if (ret == SPIFFS_OK) {
182 RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_UNMOUNTED;
184 return ret;
187 int rdv40_spiffs_check(void) {
188 rdv40_spiffs_lazy_mount();
189 SPIFFS_check(&fs);
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) {
238 spiffs_stat s;
239 if (SPIFFS_stat(&fs, filename, &s) < 0) {
240 Dbprintf("errno %i\n", SPIFFS_errno(&fs));
241 return 0;
243 return s.size;
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);
261 return fsinfo;
264 int exists_in_spiffs(const char *filename) {
265 spiffs_stat stat;
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;
282 } else {
283 filetype = RDV40_SPIFFS_FILETYPE_SYMLINK;
287 if (g_dbglevel >= DBG_DEBUG) {
288 switch (filetype) {
289 case RDV40_SPIFFS_FILETYPE_REAL:
290 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_REAL"));
291 break;
292 case RDV40_SPIFFS_FILETYPE_SYMLINK:
293 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_SYMLINK"));
294 break;
295 case RDV40_SPIFFS_FILETYPE_BOTH:
296 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_BOTH"));
297 break;
298 case RDV40_SPIFFS_FILETYPE_UNKNOWN:
299 Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_UNKNOWN"));
300 break;
303 return filetype;
306 static int is_valid_filename(const char *filename) {
307 if (filename == NULL) {
308 return false;
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
331 // state"
332 int rdv40_spiffs_lazy_mount(void) {
333 int changed = 0;
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 :)*/
338 changed = !changed;
340 return changed;
343 // unmount if not already
344 int rdv40_spiffs_lazy_unmount(void) {
345 int changed = 0;
346 if (rdv40_spiffs_mounted()) {
347 changed = rdv40_spiffs_unmount();
348 changed = !changed;
350 return changed;
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.
359 //---
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
383 // things.
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(){
393 uint8_t resetret[4];
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.
401 [...]
403 [...]
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) {
411 if (!changed) {
412 return SPIFFS_OK;
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(
441 uint32_t idx;
442 if (size <= SPIFFS_WRITE_CHUNK_SIZE) {
443 // write small file
444 write_to_spiffs(filename, src, size);
445 size = 0;
446 } else { //
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(
464 uint32_t idx;
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) {
516 int ret = 0;
518 if (s != NULL) {
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') {
522 ret = 1;
526 return ret;
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
532 // symlink ?")
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.
560 // Eg :
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);
583 switch (filetype) {
584 case RDV40_SPIFFS_FILETYPE_REAL:
585 rdv40_spiffs_read(filename, dst, size, level);
586 break;
587 case RDV40_SPIFFS_FILETYPE_SYMLINK:
588 rdv40_spiffs_read_as_symlink(filename, dst, size, level);
589 break;
590 case RDV40_SPIFFS_FILETYPE_BOTH:
591 case RDV40_SPIFFS_FILETYPE_UNKNOWN:
592 default:
593 break;
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);
617 DbpString("");
618 Dbprintf(" Filesystem size used available use% mounted");
619 DbpString("------------------------------------------------------------------");
620 Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /"
621 , fsinfo.totalBytes
622 , fsinfo.usedBytes
623 , fsinfo.freeBytes
624 , fsinfo.usedPercent
626 DbpString("");
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
631 // objects
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();
638 spiffs_DIR d;
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);
660 printed = true;
662 if (printed == false) {
663 DbpString("<empty>");
665 SPIFFS_closedir(&d);
666 rdv40_spiffs_lazy_mount_rollback(changed);
667 BigBuf_free();
670 void rdv40_spiffs_safe_wipe(void) {
672 int changed = rdv40_spiffs_lazy_mount();
674 spiffs_DIR d;
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);
692 } else {
693 remove_from_spiffs((char *)pe->name);
694 Dbprintf("removed %s", pe->name);
698 SPIFFS_closedir(&d);
699 rdv40_spiffs_lazy_mount_rollback(changed);
702 // Selftest function
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
718 // just imply noops
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"));
735 return;
738 ///////////////////////////////////////////////////////////////////////////////