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 //-----------------------------------------------------------------------------
19 #include "spiffs_nucleus.h"
22 static s32_t
spiffs_page_data_check(spiffs
*fs
, spiffs_fd
*fd
, spiffs_page_ix pix
, spiffs_span_ix spix
) {
23 s32_t res
= SPIFFS_OK
;
24 if (pix
== (spiffs_page_ix
) - 1) {
25 // referring to page 0xffff...., bad object index
26 return SPIFFS_ERR_INDEX_REF_FREE
;
28 if (pix
% SPIFFS_PAGES_PER_BLOCK(fs
) < SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
29 // referring to an object lookup page, bad object index
30 return SPIFFS_ERR_INDEX_REF_LU
;
32 if (pix
> SPIFFS_MAX_PAGES(fs
)) {
33 // referring to a bad page
34 return SPIFFS_ERR_INDEX_REF_INVALID
;
37 spiffs_page_header ph
;
39 fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_READ
,
41 SPIFFS_PAGE_TO_PADDR(fs
, pix
),
42 sizeof(spiffs_page_header
),
44 SPIFFS_CHECK_RES(res
);
45 SPIFFS_VALIDATE_DATA(ph
, fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
, spix
);
51 static s32_t
spiffs_page_index_check(spiffs
*fs
, spiffs_fd
*fd
, spiffs_page_ix pix
, spiffs_span_ix spix
) {
52 s32_t res
= SPIFFS_OK
;
53 if (pix
== (spiffs_page_ix
) - 1) {
54 // referring to page 0xffff...., bad object index
55 return SPIFFS_ERR_INDEX_FREE
;
57 if (pix
% SPIFFS_PAGES_PER_BLOCK(fs
) < SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
58 // referring to an object lookup page, bad object index
59 return SPIFFS_ERR_INDEX_LU
;
61 if (pix
> SPIFFS_MAX_PAGES(fs
)) {
62 // referring to a bad page
63 return SPIFFS_ERR_INDEX_INVALID
;
66 spiffs_page_header ph
;
68 fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
70 SPIFFS_PAGE_TO_PADDR(fs
, pix
),
71 sizeof(spiffs_page_header
),
73 SPIFFS_CHECK_RES(res
);
74 SPIFFS_VALIDATE_OBJIX(ph
, fd
->obj_id
, spix
);
78 #endif // !SPIFFS_READ_ONLY
87 return SPIFFS_HAL_READ(fs
, addr
, len
, dst
);
95 return SPIFFS_HAL_WRITE(fs
, addr
, len
, src
);
100 #if !SPIFFS_READ_ONLY
101 s32_t
spiffs_phys_cpy(
109 u8_t b
[SPIFFS_COPY_BUFFER_STACK
];
111 u32_t chunk_size
= MIN(SPIFFS_COPY_BUFFER_STACK
, len
);
112 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_MOVS
, fh
, src
, chunk_size
, b
);
113 SPIFFS_CHECK_RES(res
);
114 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_MOVD
, fh
, dst
, chunk_size
, b
);
115 SPIFFS_CHECK_RES(res
);
122 #endif // !SPIFFS_READ_ONLY
124 // Find object lookup entry containing given id with visitor.
125 // Iterate over object lookup pages in each block until a given object id entry is found.
126 // When found, the visitor function is called with block index, entry index and user data.
127 // If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be
128 // ended and visitor's return code is returned to caller.
129 // If no visitor is given (0) the search returns on first entry with matching object id.
130 // If no match is found in all look up, SPIFFS_VIS_END is returned.
131 // @param fs the file system
132 // @param starting_block the starting block to start search in
133 // @param starting_lu_entry the look up index entry to start search in
134 // @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH,
135 // SPIFFS_VIS_NO_WRAP
136 // @param obj_id argument object id
137 // @param v visitor callback function
138 // @param user_const_p any const pointer, passed to the callback visitor function
139 // @param user_var_p any pointer, passed to the callback visitor function
140 // @param block_ix reported block index where match was found
141 // @param lu_entry reported look up index where match was found
142 s32_t
spiffs_obj_lu_find_entry_visitor(
144 spiffs_block_ix starting_block
,
145 int starting_lu_entry
,
147 spiffs_obj_id obj_id
,
149 const void *user_const_p
,
151 spiffs_block_ix
*block_ix
,
153 s32_t res
= SPIFFS_OK
;
154 s32_t entry_count
= fs
->block_count
* SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs
);
155 spiffs_block_ix cur_block
= starting_block
;
156 u32_t cur_block_addr
= starting_block
* SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
158 spiffs_obj_id
*obj_lu_buf
= (spiffs_obj_id
*)fs
->lu_work
;
159 int cur_entry
= starting_lu_entry
;
160 int entries_per_page
= (SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(spiffs_obj_id
));
163 if (cur_entry
> (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs
) - 1) {
166 cur_block_addr
= cur_block
* SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
167 if (cur_block
>= fs
->block_count
) {
168 if (flags
& SPIFFS_VIS_NO_WRAP
) {
169 return SPIFFS_VIS_END
;
179 while (res
== SPIFFS_OK
&& entry_count
> 0) {
180 int obj_lookup_page
= cur_entry
/ entries_per_page
;
181 // check each object lookup page
182 while (res
== SPIFFS_OK
&& obj_lookup_page
< (int)SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
183 int entry_offset
= obj_lookup_page
* entries_per_page
;
184 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
185 0, cur_block_addr
+ SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
187 while (res
== SPIFFS_OK
&&
188 cur_entry
- entry_offset
< entries_per_page
&& // for non-last obj lookup pages
189 cur_entry
< (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs
)) { // for last obj lookup page
190 if ((flags
& SPIFFS_VIS_CHECK_ID
) == 0 || obj_lu_buf
[cur_entry
- entry_offset
] == obj_id
) {
191 if (block_ix
) *block_ix
= cur_block
;
192 if (lu_entry
) *lu_entry
= cur_entry
;
196 (flags
& SPIFFS_VIS_CHECK_PH
) ? obj_id
: obj_lu_buf
[cur_entry
- entry_offset
],
201 if (res
== SPIFFS_VIS_COUNTINUE
|| res
== SPIFFS_VIS_COUNTINUE_RELOAD
) {
202 if (res
== SPIFFS_VIS_COUNTINUE_RELOAD
) {
203 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
204 0, cur_block_addr
+ SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
205 SPIFFS_CHECK_RES(res
);
222 } // per object lookup page
225 cur_block_addr
+= SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
226 if (cur_block
>= fs
->block_count
) {
227 if (flags
& SPIFFS_VIS_NO_WRAP
) {
228 return SPIFFS_VIS_END
;
237 SPIFFS_CHECK_RES(res
);
239 return SPIFFS_VIS_END
;
242 #if !SPIFFS_READ_ONLY
243 s32_t
spiffs_erase_block(
245 spiffs_block_ix bix
) {
247 u32_t addr
= SPIFFS_BLOCK_TO_PADDR(fs
, bix
);
248 s32_t size
= SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
250 // here we ignore res, just try erasing the block
252 SPIFFS_DBG("erase "_SPIPRIad
":"_SPIPRIi
"\n", addr
, SPIFFS_CFG_PHYS_ERASE_SZ(fs
));
253 SPIFFS_HAL_ERASE(fs
, addr
, SPIFFS_CFG_PHYS_ERASE_SZ(fs
));
255 addr
+= SPIFFS_CFG_PHYS_ERASE_SZ(fs
);
256 size
-= SPIFFS_CFG_PHYS_ERASE_SZ(fs
);
260 // register erase count for this block
261 res
= _spiffs_wr(fs
, SPIFFS_OP_C_WRTHRU
| SPIFFS_OP_T_OBJ_LU2
, 0,
262 SPIFFS_ERASE_COUNT_PADDR(fs
, bix
),
263 sizeof(spiffs_obj_id
), (u8_t
*)&fs
->max_erase_count
);
264 SPIFFS_CHECK_RES(res
);
267 // finally, write magic
268 spiffs_obj_id magic
= SPIFFS_MAGIC(fs
, bix
);
269 res
= _spiffs_wr(fs
, SPIFFS_OP_C_WRTHRU
| SPIFFS_OP_T_OBJ_LU2
, 0,
270 SPIFFS_MAGIC_PADDR(fs
, bix
),
271 sizeof(spiffs_obj_id
), (u8_t
*)&magic
);
272 SPIFFS_CHECK_RES(res
);
275 fs
->max_erase_count
++;
276 if (fs
->max_erase_count
== SPIFFS_OBJ_ID_IX_FLAG
) {
277 fs
->max_erase_count
= 0;
282 #endif // !SPIFFS_READ_ONLY
284 #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
286 spiffs_config
*cfg
) {
289 spiffs dummy_fs
; // create a dummy fs struct just to be able to use macros
290 _SPIFFS_MEMCPY(&dummy_fs
.cfg
, cfg
, sizeof(spiffs_config
));
291 dummy_fs
.block_count
= 0;
293 // Read three magics, as one block may be in an aborted erase state.
294 // At least two of these must contain magic and be in decreasing order.
295 spiffs_obj_id magic
[3];
296 spiffs_obj_id bix_count
[3];
299 for (bix
= 0; bix
< 3; bix
++) {
300 paddr
= SPIFFS_MAGIC_PADDR(&dummy_fs
, bix
);
301 #if SPIFFS_HAL_CALLBACK_EXTRA
302 // not any proper fs to report here, so callback with null
303 // (cross fingers that no-one gets angry)
304 res
= cfg
->hal_read_f((void *)0, paddr
, sizeof(spiffs_obj_id
), (u8_t
*)&magic
[bix
]);
306 res
= cfg
->hal_read_f(paddr
, sizeof(spiffs_obj_id
), (u8_t
*)&magic
[bix
]);
308 bix_count
[bix
] = magic
[bix
] ^ SPIFFS_MAGIC(&dummy_fs
, 0);
309 SPIFFS_CHECK_RES(res
);
312 // check that we have sane number of blocks
313 if (bix_count
[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS
;
314 // check that the order is correct, take aborted erases in calculation
315 // first block aborted erase
316 if (magic
[0] == (spiffs_obj_id
)(-1) && bix_count
[1] - bix_count
[2] == 1) {
317 return (bix_count
[1] + 1) * cfg
->log_block_size
;
319 // second block aborted erase
320 if (magic
[1] == (spiffs_obj_id
)(-1) && bix_count
[0] - bix_count
[2] == 2) {
321 return bix_count
[0] * cfg
->log_block_size
;
323 // third block aborted erase
324 if (magic
[2] == (spiffs_obj_id
)(-1) && bix_count
[0] - bix_count
[1] == 1) {
325 return bix_count
[0] * cfg
->log_block_size
;
327 // no block has aborted erase
328 if (bix_count
[0] - bix_count
[1] == 1 && bix_count
[1] - bix_count
[2] == 1) {
329 return bix_count
[0] * cfg
->log_block_size
;
332 return SPIFFS_ERR_PROBE_NOT_A_FS
;
334 #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
337 static s32_t
spiffs_obj_lu_scan_v(
339 spiffs_obj_id obj_id
,
342 const void *user_const_p
,
347 if (obj_id
== SPIFFS_OBJ_ID_FREE
) {
350 // todo optimize further, return SPIFFS_NEXT_BLOCK
352 } else if (obj_id
== SPIFFS_OBJ_ID_DELETED
) {
353 fs
->stats_p_deleted
++;
355 fs
->stats_p_allocated
++;
358 return SPIFFS_VIS_COUNTINUE
;
362 // Scans thru all obj lu and counts free, deleted and used pages
363 // Find the maximum block erase count
364 // Checks magic if enabled
365 s32_t
spiffs_obj_lu_scan(
371 spiffs_block_ix unerased_bix
= (spiffs_block_ix
) - 1;
374 // find out erase count
375 // if enabled, check magic
377 spiffs_obj_id erase_count_final
;
378 spiffs_obj_id erase_count_min
= SPIFFS_OBJ_ID_FREE
;
379 spiffs_obj_id erase_count_max
= 0;
380 while (bix
< fs
->block_count
) {
384 SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
385 0, SPIFFS_MAGIC_PADDR(fs
, bix
),
386 sizeof(spiffs_obj_id
), (u8_t
*)&magic
);
388 SPIFFS_CHECK_RES(res
);
389 if (magic
!= SPIFFS_MAGIC(fs
, bix
)) {
390 if (unerased_bix
== (spiffs_block_ix
) - 1) {
391 // allow one unerased block as it might be powered down during an erase
394 // more than one unerased block, bail out
395 SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS
);
399 spiffs_obj_id erase_count
;
401 SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
402 0, SPIFFS_ERASE_COUNT_PADDR(fs
, bix
),
403 sizeof(spiffs_obj_id
), (u8_t
*)&erase_count
);
404 SPIFFS_CHECK_RES(res
);
405 if (erase_count
!= SPIFFS_OBJ_ID_FREE
) {
406 erase_count_min
= MIN(erase_count_min
, erase_count
);
407 erase_count_max
= MAX(erase_count_max
, erase_count
);
412 if (erase_count_min
== 0 && erase_count_max
== SPIFFS_OBJ_ID_FREE
) {
413 // clean system, set counter to zero
414 erase_count_final
= 0;
415 } else if (erase_count_max
- erase_count_min
> (SPIFFS_OBJ_ID_FREE
) / 2) {
417 erase_count_final
= erase_count_min
+ 1;
419 erase_count_final
= erase_count_max
+ 1;
422 fs
->max_erase_count
= erase_count_final
;
425 if (unerased_bix
!= (spiffs_block_ix
) - 1) {
426 // found one unerased block, remedy
427 SPIFFS_DBG("mount: erase block "_SPIPRIbl
"\n", bix
);
429 res
= SPIFFS_ERR_RO_ABORTED_OPERATION
;
431 res
= spiffs_erase_block(fs
, unerased_bix
);
432 #endif // SPIFFS_READ_ONLY
433 SPIFFS_CHECK_RES(res
);
440 fs
->stats_p_allocated
= 0;
441 fs
->stats_p_deleted
= 0;
443 res
= spiffs_obj_lu_find_entry_visitor(fs
,
448 spiffs_obj_lu_scan_v
,
454 if (res
== SPIFFS_VIS_END
) {
458 SPIFFS_CHECK_RES(res
);
463 #if !SPIFFS_READ_ONLY
464 // Find free object lookup entry
465 // Iterate over object lookup pages in each block until a free object id entry is found
466 s32_t
spiffs_obj_lu_find_free(
468 spiffs_block_ix starting_block
,
469 int starting_lu_entry
,
470 spiffs_block_ix
*block_ix
,
473 if (!fs
->cleaning
&& fs
->free_blocks
< 2) {
474 res
= spiffs_gc_quick(fs
, 0);
475 if (res
== SPIFFS_ERR_NO_DELETED_BLOCKS
) {
478 SPIFFS_CHECK_RES(res
);
479 if (fs
->free_blocks
< 2) {
480 return SPIFFS_ERR_FULL
;
483 res
= spiffs_obj_lu_find_id(fs
, starting_block
, starting_lu_entry
,
484 SPIFFS_OBJ_ID_FREE
, block_ix
, lu_entry
);
485 if (res
== SPIFFS_OK
) {
486 fs
->free_cursor_block_ix
= *block_ix
;
487 fs
->free_cursor_obj_lu_entry
= (*lu_entry
) + 1;
488 if (*lu_entry
== 0) {
492 if (res
== SPIFFS_ERR_FULL
) {
493 SPIFFS_DBGF("fs full\n");
498 #endif // !SPIFFS_READ_ONLY
500 // Find object lookup entry containing given id
501 // Iterate over object lookup pages in each block until a given object id entry is found
502 s32_t
spiffs_obj_lu_find_id(
504 spiffs_block_ix starting_block
,
505 int starting_lu_entry
,
506 spiffs_obj_id obj_id
,
507 spiffs_block_ix
*block_ix
,
509 s32_t res
= spiffs_obj_lu_find_entry_visitor(
510 fs
, starting_block
, starting_lu_entry
, SPIFFS_VIS_CHECK_ID
, obj_id
, 0, 0, 0, block_ix
, lu_entry
);
511 if (res
== SPIFFS_VIS_END
) {
512 res
= SPIFFS_ERR_NOT_FOUND
;
518 static s32_t
spiffs_obj_lu_find_id_and_span_v(
520 spiffs_obj_id obj_id
,
523 const void *user_const_p
,
526 spiffs_page_header ph
;
527 spiffs_page_ix pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, ix_entry
);
528 res
= _spiffs_rd(fs
, 0, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
529 SPIFFS_PAGE_TO_PADDR(fs
, pix
), sizeof(spiffs_page_header
), (u8_t
*)&ph
);
530 SPIFFS_CHECK_RES(res
);
531 if (ph
.obj_id
== obj_id
&&
532 ph
.span_ix
== *((spiffs_span_ix
*)user_var_p
) &&
533 (ph
.flags
& (SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_USED
)) == SPIFFS_PH_FLAG_DELET
&&
534 !((obj_id
& SPIFFS_OBJ_ID_IX_FLAG
) && (ph
.flags
& SPIFFS_PH_FLAG_IXDELE
) == 0 && ph
.span_ix
== 0) &&
535 (user_const_p
== 0 || *((const spiffs_page_ix
*)user_const_p
) != pix
)) {
538 return SPIFFS_VIS_COUNTINUE
;
542 // Find object lookup entry containing given id and span index
543 // Iterate over object lookup pages in each block until a given object id entry is found
544 s32_t
spiffs_obj_lu_find_id_and_span(
546 spiffs_obj_id obj_id
,
548 spiffs_page_ix exclusion_pix
,
549 spiffs_page_ix
*pix
) {
554 res
= spiffs_obj_lu_find_entry_visitor(fs
,
556 fs
->cursor_obj_lu_entry
,
559 spiffs_obj_lu_find_id_and_span_v
,
560 exclusion_pix
? &exclusion_pix
: 0,
565 if (res
== SPIFFS_VIS_END
) {
566 res
= SPIFFS_ERR_NOT_FOUND
;
569 SPIFFS_CHECK_RES(res
);
572 *pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
);
575 fs
->cursor_block_ix
= bix
;
576 fs
->cursor_obj_lu_entry
= entry
;
581 // Find object lookup entry containing given id and span index in page headers only
582 // Iterate over object lookup pages in each block until a given object id entry is found
583 s32_t
spiffs_obj_lu_find_id_and_span_by_phdr(
585 spiffs_obj_id obj_id
,
587 spiffs_page_ix exclusion_pix
,
588 spiffs_page_ix
*pix
) {
593 res
= spiffs_obj_lu_find_entry_visitor(fs
,
595 fs
->cursor_obj_lu_entry
,
598 spiffs_obj_lu_find_id_and_span_v
,
599 exclusion_pix
? &exclusion_pix
: 0,
604 if (res
== SPIFFS_VIS_END
) {
605 res
= SPIFFS_ERR_NOT_FOUND
;
608 SPIFFS_CHECK_RES(res
);
611 *pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
);
614 fs
->cursor_block_ix
= bix
;
615 fs
->cursor_obj_lu_entry
= entry
;
622 // update index map of given fd with given object index data
623 static void spiffs_update_ix_map(spiffs
*fs
,
624 spiffs_fd
*fd
, spiffs_span_ix objix_spix
, spiffs_page_object_ix
*objix
) {
628 spiffs_ix_map
*map
= fd
->ix_map
;
629 spiffs_span_ix map_objix_start_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, map
->start_spix
);
630 spiffs_span_ix map_objix_end_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, map
->end_spix
);
632 // check if updated ix is within map range
633 if (objix_spix
< map_objix_start_spix
|| objix_spix
> map_objix_end_spix
) {
637 // update memory mapped page index buffer to new pages
639 // get range of updated object index map data span indices
640 spiffs_span_ix objix_data_spix_start
=
641 SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs
, objix_spix
);
642 spiffs_span_ix objix_data_spix_end
= objix_data_spix_start
+
643 (objix_spix
== 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs
) : SPIFFS_OBJ_IX_LEN(fs
));
645 // calc union of object index range and index map range array
646 spiffs_span_ix map_spix
= MAX(map
->start_spix
, objix_data_spix_start
);
647 spiffs_span_ix map_spix_end
= MIN(map
->end_spix
+ 1, objix_data_spix_end
);
649 while (map_spix
< map_spix_end
) {
650 spiffs_page_ix objix_data_pix
;
651 if (objix_spix
== 0) {
652 // get data page from object index header page
653 objix_data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix_header
)))[map_spix
];
655 // get data page from object index page
656 objix_data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, map_spix
)];
659 if (objix_data_pix
== (spiffs_page_ix
) - 1) {
660 // reached end of object, abort
664 map
->map_buf
[map_spix
- map
->start_spix
] = objix_data_pix
;
665 SPIFFS_DBG("map "_SPIPRIid
":"_SPIPRIsp
" ("_SPIPRIsp
"--"_SPIPRIsp
") objix.spix:"_SPIPRIsp
" to pix "_SPIPRIpg
"\n",
666 fd
->obj_id
, map_spix
- map
->start_spix
,
667 map
->start_spix
, map
->end_spix
,
668 objix
->p_hdr
.span_ix
,
677 u32_t remaining_objix_pages_to_visit
;
678 spiffs_span_ix map_objix_start_spix
;
679 spiffs_span_ix map_objix_end_spix
;
680 } spiffs_ix_map_populate_state
;
682 static s32_t
spiffs_populate_ix_map_v(
684 spiffs_obj_id obj_id
,
687 const void *user_const_p
,
691 spiffs_ix_map_populate_state
*state
= (spiffs_ix_map_populate_state
*)user_var_p
;
692 spiffs_page_ix pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, ix_entry
);
694 // load header to check it
695 spiffs_page_object_ix
*objix
= (spiffs_page_object_ix
*)fs
->work
;
696 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
697 0, SPIFFS_PAGE_TO_PADDR(fs
, pix
), sizeof(spiffs_page_object_ix
), (u8_t
*)objix
);
698 SPIFFS_CHECK_RES(res
);
699 SPIFFS_VALIDATE_OBJIX(objix
->p_hdr
, obj_id
, objix
->p_hdr
.span_ix
);
701 // check if hdr is ok, and if objix range overlap with ix map range
702 if ((objix
->p_hdr
.flags
& (SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_IXDELE
)) ==
703 (SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_IXDELE
) &&
704 objix
->p_hdr
.span_ix
>= state
->map_objix_start_spix
&&
705 objix
->p_hdr
.span_ix
<= state
->map_objix_end_spix
) {
706 // ok, load rest of object index
707 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
708 0, SPIFFS_PAGE_TO_PADDR(fs
, pix
) + sizeof(spiffs_page_object_ix
),
709 SPIFFS_CFG_LOG_PAGE_SZ(fs
) - sizeof(spiffs_page_object_ix
),
710 (u8_t
*)objix
+ sizeof(spiffs_page_object_ix
));
711 SPIFFS_CHECK_RES(res
);
713 spiffs_update_ix_map(fs
, state
->fd
, objix
->p_hdr
.span_ix
, objix
);
715 state
->remaining_objix_pages_to_visit
--;
716 SPIFFS_DBG("map "_SPIPRIid
" ("_SPIPRIsp
"--"_SPIPRIsp
") remaining objix pages "_SPIPRIi
"\n",
718 state
->fd
->ix_map
->start_spix
, state
->fd
->ix_map
->end_spix
,
719 state
->remaining_objix_pages_to_visit
);
722 if (res
== SPIFFS_OK
) {
723 res
= state
->remaining_objix_pages_to_visit
? SPIFFS_VIS_COUNTINUE
: SPIFFS_VIS_END
;
728 // populates index map, from vector entry start to vector entry end, inclusive
729 s32_t
spiffs_populate_ix_map(spiffs
*fs
, spiffs_fd
*fd
, u32_t vec_entry_start
, u32_t vec_entry_end
) {
731 spiffs_ix_map
*map
= fd
->ix_map
;
732 spiffs_ix_map_populate_state state
;
733 vec_entry_start
= MIN((u32_t
)(map
->end_spix
- map
->start_spix
), vec_entry_start
);
734 vec_entry_end
= MAX((u32_t
)(map
->end_spix
- map
->start_spix
), vec_entry_end
);
735 if (vec_entry_start
> vec_entry_end
) {
736 return SPIFFS_ERR_IX_MAP_BAD_RANGE
;
738 state
.map_objix_start_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, map
->start_spix
+ vec_entry_start
);
739 state
.map_objix_end_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, map
->start_spix
+ vec_entry_end
);
740 state
.remaining_objix_pages_to_visit
=
741 state
.map_objix_end_spix
- state
.map_objix_start_spix
+ 1;
744 res
= spiffs_obj_lu_find_entry_visitor(
746 SPIFFS_BLOCK_FOR_PAGE(fs
, fd
->objix_hdr_pix
),
747 SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs
, fd
->objix_hdr_pix
),
749 fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
,
750 spiffs_populate_ix_map_v
,
756 if (res
== SPIFFS_VIS_END
) {
766 #if !SPIFFS_READ_ONLY
767 // Allocates a free defined page with given obj_id
768 // Occupies object lookup entry and page
769 // data may be NULL; where only page header is stored, len and page_offs is ignored
770 s32_t
spiffs_page_allocate_data(
772 spiffs_obj_id obj_id
,
773 spiffs_page_header
*ph
,
778 spiffs_page_ix
*pix
) {
779 s32_t res
= SPIFFS_OK
;
784 res
= spiffs_obj_lu_find_free(fs
, fs
->free_cursor_block_ix
, fs
->free_cursor_obj_lu_entry
, &bix
, &entry
);
785 SPIFFS_CHECK_RES(res
);
787 // occupy page in object lookup
788 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_UPDT
,
789 0, SPIFFS_BLOCK_TO_PADDR(fs
, bix
) + entry
* sizeof(spiffs_obj_id
), sizeof(spiffs_obj_id
), (u8_t
*)&obj_id
);
790 SPIFFS_CHECK_RES(res
);
792 fs
->stats_p_allocated
++;
795 ph
->flags
&= ~SPIFFS_PH_FLAG_USED
;
796 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
797 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs
, bix
, entry
), sizeof(spiffs_page_header
), (u8_t
*)ph
);
798 SPIFFS_CHECK_RES(res
);
802 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
803 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs
, bix
, entry
) + sizeof(spiffs_page_header
) + page_offs
, len
, data
);
804 SPIFFS_CHECK_RES(res
);
807 // finalize header if necessary
808 if (finalize
&& (ph
->flags
& SPIFFS_PH_FLAG_FINAL
)) {
809 ph
->flags
&= ~SPIFFS_PH_FLAG_FINAL
;
810 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
811 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs
, bix
, entry
) + offsetof(spiffs_page_header
, flags
),
814 SPIFFS_CHECK_RES(res
);
817 // return written page
819 *pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
);
824 #endif // !SPIFFS_READ_ONLY
826 #if !SPIFFS_READ_ONLY
827 // Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page.
828 // If page data is null, provided header is used for metainfo and page data is physically copied.
829 s32_t
spiffs_page_move(
833 spiffs_obj_id obj_id
,
834 spiffs_page_header
*page_hdr
,
835 spiffs_page_ix src_pix
,
836 spiffs_page_ix
*dst_pix
) {
839 spiffs_page_header
*p_hdr
;
842 spiffs_page_ix free_pix
;
845 res
= spiffs_obj_lu_find_free(fs
, fs
->free_cursor_block_ix
, fs
->free_cursor_obj_lu_entry
, &bix
, &entry
);
846 SPIFFS_CHECK_RES(res
);
847 free_pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
);
849 if (dst_pix
) *dst_pix
= free_pix
;
851 p_hdr
= page_data
? (spiffs_page_header
*)page_data
: page_hdr
;
854 was_final
= (p_hdr
->flags
& SPIFFS_PH_FLAG_FINAL
) == 0;
855 // write unfinalized page
856 p_hdr
->flags
|= SPIFFS_PH_FLAG_FINAL
;
857 p_hdr
->flags
&= ~SPIFFS_PH_FLAG_USED
;
858 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
859 0, SPIFFS_PAGE_TO_PADDR(fs
, free_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), page_data
);
862 res
= spiffs_phys_cpy(fs
, fh
, SPIFFS_PAGE_TO_PADDR(fs
, free_pix
), SPIFFS_PAGE_TO_PADDR(fs
, src_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
));
864 SPIFFS_CHECK_RES(res
);
866 // mark entry in destination object lookup
867 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_UPDT
,
868 0, SPIFFS_BLOCK_TO_PADDR(fs
, SPIFFS_BLOCK_FOR_PAGE(fs
, free_pix
)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs
, free_pix
) * sizeof(spiffs_page_ix
),
869 sizeof(spiffs_obj_id
),
871 SPIFFS_CHECK_RES(res
);
873 fs
->stats_p_allocated
++;
876 // mark finalized in destination page
877 p_hdr
->flags
&= ~(SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_USED
);
878 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
880 SPIFFS_PAGE_TO_PADDR(fs
, free_pix
) + offsetof(spiffs_page_header
, flags
),
882 (u8_t
*)&p_hdr
->flags
);
883 SPIFFS_CHECK_RES(res
);
885 // mark source deleted
886 res
= spiffs_page_delete(fs
, src_pix
);
889 #endif // !SPIFFS_READ_ONLY
891 #if !SPIFFS_READ_ONLY
892 // Deletes a page and removes it from object lookup.
893 s32_t
spiffs_page_delete(
895 spiffs_page_ix pix
) {
897 // mark deleted entry in source object lookup
898 spiffs_obj_id d_obj_id
= SPIFFS_OBJ_ID_DELETED
;
899 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_DELE
,
901 SPIFFS_BLOCK_TO_PADDR(fs
, SPIFFS_BLOCK_FOR_PAGE(fs
, pix
)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs
, pix
) * sizeof(spiffs_page_ix
),
902 sizeof(spiffs_obj_id
),
904 SPIFFS_CHECK_RES(res
);
906 fs
->stats_p_deleted
++;
907 fs
->stats_p_allocated
--;
909 // mark deleted in source page
911 #if SPIFFS_NO_BLIND_WRITES
912 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_READ
,
913 0, SPIFFS_PAGE_TO_PADDR(fs
, pix
) + offsetof(spiffs_page_header
, flags
),
914 sizeof(flags
), &flags
);
915 SPIFFS_CHECK_RES(res
);
917 flags
&= ~(SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_USED
);
918 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_DELE
,
920 SPIFFS_PAGE_TO_PADDR(fs
, pix
) + offsetof(spiffs_page_header
, flags
),
921 sizeof(flags
), &flags
);
925 #endif // !SPIFFS_READ_ONLY
927 #if !SPIFFS_READ_ONLY
928 // Create an object index header page with empty index and undefined length
929 s32_t
spiffs_object_create(
931 spiffs_obj_id obj_id
,
934 spiffs_obj_type type
,
935 spiffs_page_ix
*objix_hdr_pix
) {
936 s32_t res
= SPIFFS_OK
;
938 spiffs_page_object_ix_header oix_hdr
;
941 res
= spiffs_gc_check(fs
, SPIFFS_DATA_PAGE_SIZE(fs
));
942 SPIFFS_CHECK_RES(res
);
944 obj_id
|= SPIFFS_OBJ_ID_IX_FLAG
;
947 res
= spiffs_obj_lu_find_free(fs
, fs
->free_cursor_block_ix
, fs
->free_cursor_obj_lu_entry
, &bix
, &entry
);
948 SPIFFS_CHECK_RES(res
);
949 SPIFFS_DBG("create: found free page @ "_SPIPRIpg
" bix:"_SPIPRIbl
" entry:"_SPIPRIsp
"\n", (spiffs_page_ix
)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
), bix
, entry
);
951 // occupy page in object lookup
952 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_UPDT
,
953 0, SPIFFS_BLOCK_TO_PADDR(fs
, bix
) + entry
* sizeof(spiffs_obj_id
), sizeof(spiffs_obj_id
), (u8_t
*)&obj_id
);
954 SPIFFS_CHECK_RES(res
);
956 fs
->stats_p_allocated
++;
958 // write empty object index page
959 oix_hdr
.p_hdr
.obj_id
= obj_id
;
960 oix_hdr
.p_hdr
.span_ix
= 0;
961 oix_hdr
.p_hdr
.flags
= 0xff & ~(SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_INDEX
| SPIFFS_PH_FLAG_USED
);
963 oix_hdr
.size
= SPIFFS_UNDEFINED_LEN
; // keep ones so we can update later without wasting this page
964 strncpy((char *)oix_hdr
.name
, (const char *)name
, SPIFFS_OBJ_NAME_LEN
- 1);
965 #if SPIFFS_OBJ_META_LEN
967 _SPIFFS_MEMCPY(oix_hdr
.meta
, meta
, SPIFFS_OBJ_META_LEN
);
969 memset(oix_hdr
.meta
, 0xff, SPIFFS_OBJ_META_LEN
);
976 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
977 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs
, bix
, entry
), sizeof(spiffs_page_object_ix_header
), (u8_t
*)&oix_hdr
);
979 SPIFFS_CHECK_RES(res
);
980 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)&oix_hdr
,
981 SPIFFS_EV_IX_NEW
, obj_id
, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
), SPIFFS_UNDEFINED_LEN
);
984 *objix_hdr_pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
);
989 #endif // !SPIFFS_READ_ONLY
991 #if !SPIFFS_READ_ONLY
992 // update object index header with any combination of name/size/index
993 // new_objix_hdr_data may be null, if so the object index header page is loaded
994 // name may be null, if so name is not changed
995 // size may be null, if so size is not changed
996 s32_t
spiffs_object_update_index_hdr(
999 spiffs_obj_id obj_id
,
1000 spiffs_page_ix objix_hdr_pix
,
1001 u8_t
*new_objix_hdr_data
,
1005 spiffs_page_ix
*new_pix
) {
1006 s32_t res
= SPIFFS_OK
;
1007 spiffs_page_object_ix_header
*objix_hdr
;
1008 spiffs_page_ix new_objix_hdr_pix
;
1010 obj_id
|= SPIFFS_OBJ_ID_IX_FLAG
;
1012 if (new_objix_hdr_data
) {
1013 // object index header page already given to us, no need to load it
1014 objix_hdr
= (spiffs_page_object_ix_header
*)new_objix_hdr_data
;
1016 // read object index header page
1017 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1018 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, objix_hdr_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1019 SPIFFS_CHECK_RES(res
);
1020 objix_hdr
= (spiffs_page_object_ix_header
*)fs
->work
;
1023 SPIFFS_VALIDATE_OBJIX(objix_hdr
->p_hdr
, obj_id
, 0);
1027 strncpy((char *)objix_hdr
->name
, (const char *)name
, SPIFFS_OBJ_NAME_LEN
- 1);
1029 #if SPIFFS_OBJ_META_LEN
1031 _SPIFFS_MEMCPY(objix_hdr
->meta
, meta
, SPIFFS_OBJ_META_LEN
);
1037 objix_hdr
->size
= size
;
1040 // move and update page
1041 res
= spiffs_page_move(fs
, fd
== 0 ? 0 : fd
->file_nbr
, (u8_t
*)objix_hdr
, obj_id
, 0, objix_hdr_pix
, &new_objix_hdr_pix
);
1043 if (res
== SPIFFS_OK
) {
1045 *new_pix
= new_objix_hdr_pix
;
1047 // callback on object index update
1048 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)objix_hdr
,
1049 new_objix_hdr_data
? SPIFFS_EV_IX_UPD
: SPIFFS_EV_IX_UPD_HDR
,
1050 obj_id
, objix_hdr
->p_hdr
.span_ix
, new_objix_hdr_pix
, objix_hdr
->size
);
1051 if (fd
) fd
->objix_hdr_pix
= new_objix_hdr_pix
; // if this is not in the registered cluster
1056 #endif // !SPIFFS_READ_ONLY
1058 void spiffs_cb_object_event(
1060 spiffs_page_object_ix
*objix
,
1062 spiffs_obj_id obj_id_raw
,
1063 spiffs_span_ix spix
,
1064 spiffs_page_ix new_pix
,
1066 #if SPIFFS_IX_MAP == 0
1069 // update index caches in all file descriptors
1070 spiffs_obj_id obj_id
= obj_id_raw
& ~SPIFFS_OBJ_ID_IX_FLAG
;
1072 spiffs_fd
*fds
= (spiffs_fd
*)fs
->fd_space
;
1073 SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid
" spix:"_SPIPRIsp
" npix:"_SPIPRIpg
" nsz:"_SPIPRIi
"\n", (const char *[]) {"UPD", "NEW", "DEL", "MOV", "HUP", "???"}[MIN(ev
, 5)],
1074 obj_id_raw
, spix
, new_pix
, new_size
);
1075 for (i
= 0; i
< fs
->fd_count
; i
++) {
1076 spiffs_fd
*cur_fd
= &fds
[i
];
1077 if ((cur_fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
) != obj_id
) continue; // fd not related to updated file
1078 #if !SPIFFS_TEMPORAL_FD_CACHE
1079 if (cur_fd
->file_nbr
== 0) continue; // fd closed
1081 if (spix
== 0) { // object index header update
1082 if (ev
!= SPIFFS_EV_IX_DEL
) {
1083 #if SPIFFS_TEMPORAL_FD_CACHE
1084 if (cur_fd
->score
== 0) continue; // never used fd
1086 SPIFFS_DBG(" callback: setting fd "_SPIPRIfd
":"_SPIPRIid
"(fdoffs:"_SPIPRIi
" offs:"_SPIPRIi
") objix_hdr_pix to "_SPIPRIpg
", size:"_SPIPRIi
"\n",
1087 SPIFFS_FH_OFFS(fs
, cur_fd
->file_nbr
), cur_fd
->obj_id
, cur_fd
->fdoffset
, cur_fd
->offset
, new_pix
, new_size
);
1088 cur_fd
->objix_hdr_pix
= new_pix
;
1089 if (new_size
!= 0) {
1090 // update size and offsets for fds to this file
1091 cur_fd
->size
= new_size
;
1092 u32_t act_new_size
= new_size
== SPIFFS_UNDEFINED_LEN
? 0 : new_size
;
1094 if (act_new_size
> 0 && cur_fd
->cache_page
) {
1095 act_new_size
= MAX(act_new_size
, cur_fd
->cache_page
->ucache
.swrc
.offset
+ cur_fd
->cache_page
->ucache
.swrc
.size
);
1098 if (cur_fd
->offset
> act_new_size
) {
1099 cur_fd
->offset
= act_new_size
;
1101 if (cur_fd
->fdoffset
> act_new_size
) {
1102 cur_fd
->fdoffset
= act_new_size
;
1105 if (cur_fd
->cache_page
&& cur_fd
->cache_page
->ucache
.swrc
.offset
> act_new_size
+ 1) {
1106 SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi
", no writeback\n", cur_fd
->cache_page
->ix
);
1107 spiffs_cache_fd_release(fs
, cur_fd
->cache_page
);
1114 if (cur_fd
->file_nbr
&& cur_fd
->cache_page
) {
1115 SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi
", no writeback\n", cur_fd
->cache_page
->ix
);
1116 spiffs_cache_fd_release(fs
, cur_fd
->cache_page
);
1119 SPIFFS_DBG(" callback: release fd "_SPIPRIfd
":"_SPIPRIid
" span:"_SPIPRIsp
" objix_pix to "_SPIPRIpg
"\n", SPIFFS_FH_OFFS(fs
, cur_fd
->file_nbr
), cur_fd
->obj_id
, spix
, new_pix
);
1120 cur_fd
->file_nbr
= 0;
1121 cur_fd
->obj_id
= SPIFFS_OBJ_ID_DELETED
;
1123 } // object index header update
1124 if (cur_fd
->cursor_objix_spix
== spix
) {
1125 if (ev
!= SPIFFS_EV_IX_DEL
) {
1126 SPIFFS_DBG(" callback: setting fd "_SPIPRIfd
":"_SPIPRIid
" span:"_SPIPRIsp
" objix_pix to "_SPIPRIpg
"\n", SPIFFS_FH_OFFS(fs
, cur_fd
->file_nbr
), cur_fd
->obj_id
, spix
, new_pix
);
1127 cur_fd
->cursor_objix_pix
= new_pix
;
1129 cur_fd
->cursor_objix_pix
= 0;
1136 // update index maps
1137 if (ev
== SPIFFS_EV_IX_UPD
|| ev
== SPIFFS_EV_IX_NEW
) {
1138 for (i
= 0; i
< fs
->fd_count
; i
++) {
1139 spiffs_fd
*cur_fd
= &fds
[i
];
1140 // check fd opened, having ix map, match obj id
1141 if (cur_fd
->file_nbr
== 0 ||
1142 cur_fd
->ix_map
== 0 ||
1143 (cur_fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
) != obj_id
) continue;
1144 SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd
":"_SPIPRIid
" span:"_SPIPRIsp
"\n", SPIFFS_FH_OFFS(fs
, cur_fd
->file_nbr
), cur_fd
->obj_id
, spix
);
1145 spiffs_update_ix_map(fs
, cur_fd
, spix
, objix
);
1151 // callback to user if object index header
1152 if (fs
->file_cb_f
&& spix
== 0 && (obj_id_raw
& SPIFFS_OBJ_ID_IX_FLAG
)) {
1153 spiffs_fileop_type op
;
1154 if (ev
== SPIFFS_EV_IX_NEW
) {
1155 op
= SPIFFS_CB_CREATED
;
1156 } else if (ev
== SPIFFS_EV_IX_UPD
||
1157 ev
== SPIFFS_EV_IX_MOV
||
1158 ev
== SPIFFS_EV_IX_UPD_HDR
) {
1159 op
= SPIFFS_CB_UPDATED
;
1160 } else if (ev
== SPIFFS_EV_IX_DEL
) {
1161 op
= SPIFFS_CB_DELETED
;
1163 SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi
"\n", ev
);
1166 fs
->file_cb_f(fs
, op
, obj_id
, new_pix
);
1170 // Open object by id
1171 s32_t
spiffs_object_open_by_id(
1173 spiffs_obj_id obj_id
,
1177 s32_t res
= SPIFFS_OK
;
1180 res
= spiffs_obj_lu_find_id_and_span(fs
, obj_id
| SPIFFS_OBJ_ID_IX_FLAG
, 0, 0, &pix
);
1181 SPIFFS_CHECK_RES(res
);
1183 res
= spiffs_object_open_by_page(fs
, pix
, fd
, flags
, mode
);
1188 // Open object by page index
1189 s32_t
spiffs_object_open_by_page(
1196 s32_t res
= SPIFFS_OK
;
1197 spiffs_page_object_ix_header oix_hdr
;
1198 spiffs_obj_id obj_id
;
1200 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1201 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, pix
), sizeof(spiffs_page_object_ix_header
), (u8_t
*)&oix_hdr
);
1202 SPIFFS_CHECK_RES(res
);
1204 spiffs_block_ix bix
= SPIFFS_BLOCK_FOR_PAGE(fs
, pix
);
1205 int entry
= SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs
, pix
);
1207 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
1208 0, SPIFFS_BLOCK_TO_PADDR(fs
, bix
) + entry
* sizeof(spiffs_obj_id
), sizeof(spiffs_obj_id
), (u8_t
*)&obj_id
);
1211 fd
->objix_hdr_pix
= pix
;
1212 fd
->size
= oix_hdr
.size
;
1214 fd
->cursor_objix_pix
= pix
;
1215 fd
->cursor_objix_spix
= 0;
1216 fd
->obj_id
= obj_id
;
1219 SPIFFS_VALIDATE_OBJIX(oix_hdr
.p_hdr
, fd
->obj_id
, 0);
1221 SPIFFS_DBG("open: fd "_SPIPRIfd
" is obj id "_SPIPRIid
"\n", SPIFFS_FH_OFFS(fs
, fd
->file_nbr
), fd
->obj_id
);
1226 #if !SPIFFS_READ_ONLY
1228 // keep current object index (header) page in fs->work buffer
1229 s32_t
spiffs_object_append(spiffs_fd
*fd
, u32_t offset
, u8_t
*data
, u32_t len
) {
1230 spiffs
*fs
= fd
->fs
;
1231 s32_t res
= SPIFFS_OK
;
1234 SPIFFS_DBG("append: "_SPIPRIi
" bytes @ offs "_SPIPRIi
" of size "_SPIPRIi
"\n", len
, offset
, fd
->size
);
1236 if (offset
> fd
->size
) {
1237 SPIFFS_DBGF("append: offset reversed to size\n");
1241 res
= spiffs_gc_check(fs
, len
+ SPIFFS_DATA_PAGE_SIZE(fs
)); // add an extra page of data worth for meta
1242 if (res
!= SPIFFS_OK
) {
1243 SPIFFS_DBG("append: gc check fail "_SPIPRIi
"\n", res
);
1245 SPIFFS_CHECK_RES(res
);
1247 spiffs_page_object_ix_header
*objix_hdr
= (spiffs_page_object_ix_header
*)fs
->work
;
1248 spiffs_page_object_ix
*objix
= (spiffs_page_object_ix
*)fs
->work
;
1249 spiffs_page_header p_hdr
;
1251 spiffs_span_ix cur_objix_spix
= 0;
1252 spiffs_span_ix prev_objix_spix
= (spiffs_span_ix
) - 1;
1253 spiffs_page_ix cur_objix_pix
= fd
->objix_hdr_pix
;
1254 spiffs_page_ix new_objix_hdr_page
;
1256 spiffs_span_ix data_spix
= offset
/ SPIFFS_DATA_PAGE_SIZE(fs
);
1257 spiffs_page_ix data_page
;
1258 u32_t page_offs
= offset
% SPIFFS_DATA_PAGE_SIZE(fs
);
1261 while (res
== SPIFFS_OK
&& written
< len
) {
1262 // calculate object index page span index
1263 cur_objix_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, data_spix
);
1265 // handle storing and loading of object indices
1266 if (cur_objix_spix
!= prev_objix_spix
) {
1267 // new object index page
1268 // within this clause we return directly if something fails, object index mess-up
1270 // store previous object index page, unless first pass
1271 SPIFFS_DBG("append: "_SPIPRIid
" store objix "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
,
1272 cur_objix_pix
, prev_objix_spix
, written
);
1273 if (prev_objix_spix
== 0) {
1274 // this is an update to object index header page
1275 objix_hdr
->size
= offset
+ written
;
1277 // was an empty object, update same page (size was 0xffffffff)
1278 res
= spiffs_page_index_check(fs
, fd
, cur_objix_pix
, 0);
1279 SPIFFS_CHECK_RES(res
);
1280 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_UPDT
,
1281 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, cur_objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1282 SPIFFS_CHECK_RES(res
);
1284 // was a nonempty object, update to new page
1285 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1286 fd
->objix_hdr_pix
, fs
->work
, 0, 0, offset
+ written
, &new_objix_hdr_page
);
1287 SPIFFS_CHECK_RES(res
);
1288 SPIFFS_DBG("append: "_SPIPRIid
" store new objix_hdr, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
,
1289 new_objix_hdr_page
, 0, written
);
1292 // this is an update to an object index page
1293 res
= spiffs_page_index_check(fs
, fd
, cur_objix_pix
, prev_objix_spix
);
1294 SPIFFS_CHECK_RES(res
);
1296 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_UPDT
,
1297 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, cur_objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1298 SPIFFS_CHECK_RES(res
);
1299 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)fs
->work
,
1300 SPIFFS_EV_IX_UPD
, fd
->obj_id
, objix
->p_hdr
.span_ix
, cur_objix_pix
, 0);
1301 // update length in object index header page
1302 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1303 fd
->objix_hdr_pix
, 0, 0, 0, offset
+ written
, &new_objix_hdr_page
);
1304 SPIFFS_CHECK_RES(res
);
1305 SPIFFS_DBG("append: "_SPIPRIid
" store new size I "_SPIPRIi
" in objix_hdr, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
,
1306 offset
+ written
, new_objix_hdr_page
, 0, written
);
1308 fd
->size
= offset
+ written
;
1309 fd
->offset
= offset
+ written
;
1312 // create or load new object index page
1313 if (cur_objix_spix
== 0) {
1314 // load object index header page, must always exist
1315 SPIFFS_DBG("append: "_SPIPRIid
" load objixhdr page "_SPIPRIpg
":"_SPIPRIsp
"\n", fd
->obj_id
, cur_objix_pix
, cur_objix_spix
);
1316 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1317 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, cur_objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1318 SPIFFS_CHECK_RES(res
);
1319 SPIFFS_VALIDATE_OBJIX(objix_hdr
->p_hdr
, fd
->obj_id
, cur_objix_spix
);
1321 spiffs_span_ix len_objix_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, (fd
->size
- 1) / SPIFFS_DATA_PAGE_SIZE(fs
));
1322 // on subsequent passes, create a new object index page
1323 if (written
> 0 || cur_objix_spix
> len_objix_spix
) {
1324 p_hdr
.obj_id
= fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
;
1325 p_hdr
.span_ix
= cur_objix_spix
;
1326 p_hdr
.flags
= 0xff & ~(SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_INDEX
);
1327 res
= spiffs_page_allocate_data(fs
, fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
,
1328 &p_hdr
, 0, 0, 0, 1, &cur_objix_pix
);
1329 SPIFFS_CHECK_RES(res
);
1330 // quick "load" of new object index page
1331 memset(fs
->work
, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs
));
1332 _SPIFFS_MEMCPY(fs
->work
, &p_hdr
, sizeof(spiffs_page_header
));
1333 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)fs
->work
,
1334 SPIFFS_EV_IX_NEW
, fd
->obj_id
, cur_objix_spix
, cur_objix_pix
, 0);
1335 SPIFFS_DBG("append: "_SPIPRIid
" create objix page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
1336 , cur_objix_pix
, cur_objix_spix
, written
);
1338 // on first pass, we load existing object index page
1340 SPIFFS_DBG("append: "_SPIPRIid
" find objix span_ix:"_SPIPRIsp
"\n", fd
->obj_id
, cur_objix_spix
);
1341 if (fd
->cursor_objix_spix
== cur_objix_spix
) {
1342 pix
= fd
->cursor_objix_pix
;
1344 res
= spiffs_obj_lu_find_id_and_span(fs
, fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
, cur_objix_spix
, 0, &pix
);
1345 SPIFFS_CHECK_RES(res
);
1347 SPIFFS_DBG("append: "_SPIPRIid
" found object index at page "_SPIPRIpg
" [fd size "_SPIPRIi
"]\n", fd
->obj_id
, pix
, fd
->size
);
1348 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1349 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1350 SPIFFS_CHECK_RES(res
);
1351 SPIFFS_VALIDATE_OBJIX(objix_hdr
->p_hdr
, fd
->obj_id
, cur_objix_spix
);
1352 cur_objix_pix
= pix
;
1354 fd
->cursor_objix_pix
= cur_objix_pix
;
1355 fd
->cursor_objix_spix
= cur_objix_spix
;
1356 fd
->offset
= offset
+ written
;
1357 fd
->size
= offset
+ written
;
1359 prev_objix_spix
= cur_objix_spix
;
1363 u32_t to_write
= MIN(len
- written
, SPIFFS_DATA_PAGE_SIZE(fs
) - page_offs
);
1364 if (page_offs
== 0) {
1365 // at beginning of a page, allocate and write a new page of data
1366 p_hdr
.obj_id
= fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
;
1367 p_hdr
.span_ix
= data_spix
;
1368 p_hdr
.flags
= 0xff & ~(SPIFFS_PH_FLAG_FINAL
); // finalize immediately
1369 res
= spiffs_page_allocate_data(fs
, fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
,
1370 &p_hdr
, &data
[written
], to_write
, page_offs
, 1, &data_page
);
1371 SPIFFS_DBG("append: "_SPIPRIid
" store new data page, "_SPIPRIpg
":"_SPIPRIsp
" offset:"_SPIPRIi
", len "_SPIPRIi
", written "_SPIPRIi
"\n", fd
->obj_id
,
1372 data_page
, data_spix
, page_offs
, to_write
, written
);
1374 // append to existing page, fill out free data in existing page
1375 if (cur_objix_spix
== 0) {
1376 // get data page from object index header page
1377 data_page
= ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
];
1379 // get data page from object index page
1380 data_page
= ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)];
1383 res
= spiffs_page_data_check(fs
, fd
, data_page
, data_spix
);
1384 SPIFFS_CHECK_RES(res
);
1386 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
1387 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, data_page
) + sizeof(spiffs_page_header
) + page_offs
, to_write
, &data
[written
]);
1388 SPIFFS_DBG("append: "_SPIPRIid
" store to existing data page, "_SPIPRIpg
":"_SPIPRIsp
" offset:"_SPIPRIi
", len "_SPIPRIi
", written "_SPIPRIi
"\n", fd
->obj_id
1389 , data_page
, data_spix
, page_offs
, to_write
, written
);
1392 if (res
!= SPIFFS_OK
) break;
1394 // update memory representation of object index page with new data page
1395 if (cur_objix_spix
== 0) {
1396 // update object index header page
1397 ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
] = data_page
;
1398 SPIFFS_DBG("append: "_SPIPRIid
" wrote page "_SPIPRIpg
" to objix_hdr entry "_SPIPRIsp
" in mem\n", fd
->obj_id
1399 , data_page
, data_spix
);
1400 objix_hdr
->size
= offset
+ written
;
1402 // update object index page
1403 ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)] = data_page
;
1404 SPIFFS_DBG("append: "_SPIPRIid
" wrote page "_SPIPRIpg
" to objix entry "_SPIPRIsp
" in mem\n", fd
->obj_id
1405 , data_page
, (spiffs_span_ix
)SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
));
1411 written
+= to_write
;
1414 fd
->size
= offset
+ written
;
1415 fd
->offset
= offset
+ written
;
1416 fd
->cursor_objix_pix
= cur_objix_pix
;
1417 fd
->cursor_objix_spix
= cur_objix_spix
;
1419 // finalize updated object indices
1420 s32_t res2
= SPIFFS_OK
;
1421 if (cur_objix_spix
!= 0) {
1422 // wrote beyond object index header page
1423 // write last modified object index page, unless object header index page
1424 SPIFFS_DBG("append: "_SPIPRIid
" store objix page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
,
1425 cur_objix_pix
, cur_objix_spix
, written
);
1427 res2
= spiffs_page_index_check(fs
, fd
, cur_objix_pix
, cur_objix_spix
);
1428 SPIFFS_CHECK_RES(res2
);
1430 res2
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_UPDT
,
1431 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, cur_objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1432 SPIFFS_CHECK_RES(res2
);
1433 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)fs
->work
,
1434 SPIFFS_EV_IX_UPD
, fd
->obj_id
, objix
->p_hdr
.span_ix
, cur_objix_pix
, 0);
1436 // update size in object header index page
1437 res2
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1438 fd
->objix_hdr_pix
, 0, 0, 0, offset
+ written
, &new_objix_hdr_page
);
1439 SPIFFS_DBG("append: "_SPIPRIid
" store new size II "_SPIPRIi
" in objix_hdr, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
", res "_SPIPRIi
"\n", fd
->obj_id
1440 , offset
+ written
, new_objix_hdr_page
, 0, written
, res2
);
1441 SPIFFS_CHECK_RES(res2
);
1443 // wrote within object index header page
1445 // wrote to empty object - simply update size and write whole page
1446 objix_hdr
->size
= offset
+ written
;
1447 SPIFFS_DBG("append: "_SPIPRIid
" store fresh objix_hdr page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
1448 , cur_objix_pix
, cur_objix_spix
, written
);
1450 res2
= spiffs_page_index_check(fs
, fd
, cur_objix_pix
, cur_objix_spix
);
1451 SPIFFS_CHECK_RES(res2
);
1453 res2
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_UPDT
,
1454 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, cur_objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1455 SPIFFS_CHECK_RES(res2
);
1456 // callback on object index update
1457 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)fs
->work
,
1458 SPIFFS_EV_IX_UPD_HDR
, fd
->obj_id
, objix_hdr
->p_hdr
.span_ix
, cur_objix_pix
, objix_hdr
->size
);
1460 // modifying object index header page, update size and make new copy
1461 res2
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1462 fd
->objix_hdr_pix
, fs
->work
, 0, 0, offset
+ written
, &new_objix_hdr_page
);
1463 SPIFFS_DBG("append: "_SPIPRIid
" store modified objix_hdr page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", fd
->obj_id
1464 , new_objix_hdr_page
, 0, written
);
1465 SPIFFS_CHECK_RES(res2
);
1470 } // spiffs_object_append
1471 #endif // !SPIFFS_READ_ONLY
1473 #if !SPIFFS_READ_ONLY
1475 // keep current object index (header) page in fs->work buffer
1476 s32_t
spiffs_object_modify(spiffs_fd
*fd
, u32_t offset
, u8_t
*data
, u32_t len
) {
1477 spiffs
*fs
= fd
->fs
;
1478 s32_t res
= SPIFFS_OK
;
1481 res
= spiffs_gc_check(fs
, len
+ SPIFFS_DATA_PAGE_SIZE(fs
));
1482 SPIFFS_CHECK_RES(res
);
1484 spiffs_page_object_ix_header
*objix_hdr
= (spiffs_page_object_ix_header
*)fs
->work
;
1485 spiffs_page_object_ix
*objix
= (spiffs_page_object_ix
*)fs
->work
;
1486 spiffs_page_header p_hdr
;
1488 spiffs_span_ix cur_objix_spix
= 0;
1489 spiffs_span_ix prev_objix_spix
= (spiffs_span_ix
) - 1;
1490 spiffs_page_ix cur_objix_pix
= fd
->objix_hdr_pix
;
1491 spiffs_page_ix new_objix_hdr_pix
;
1493 spiffs_span_ix data_spix
= offset
/ SPIFFS_DATA_PAGE_SIZE(fs
);
1494 spiffs_page_ix data_pix
;
1495 u32_t page_offs
= offset
% SPIFFS_DATA_PAGE_SIZE(fs
);
1499 while (res
== SPIFFS_OK
&& written
< len
) {
1500 // calculate object index page span index
1501 cur_objix_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, data_spix
);
1503 // handle storing and loading of object indices
1504 if (cur_objix_spix
!= prev_objix_spix
) {
1505 // new object index page
1506 // within this clause we return directly if something fails, object index mess-up
1508 // store previous object index (header) page, unless first pass
1509 if (prev_objix_spix
== 0) {
1510 // store previous object index header page
1511 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1512 fd
->objix_hdr_pix
, fs
->work
, 0, 0, 0, &new_objix_hdr_pix
);
1513 SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", new_objix_hdr_pix
, 0, written
);
1514 SPIFFS_CHECK_RES(res
);
1516 // store new version of previous object index page
1517 spiffs_page_ix new_objix_pix
;
1519 res
= spiffs_page_index_check(fs
, fd
, cur_objix_pix
, prev_objix_spix
);
1520 SPIFFS_CHECK_RES(res
);
1522 res
= spiffs_page_move(fs
, fd
->file_nbr
, (u8_t
*)objix
, fd
->obj_id
, 0, cur_objix_pix
, &new_objix_pix
);
1523 SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid
":"_SPIPRIsp
", written "_SPIPRIi
"\n", new_objix_pix
, objix
->p_hdr
.span_ix
, written
);
1524 SPIFFS_CHECK_RES(res
);
1525 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)objix
,
1526 SPIFFS_EV_IX_UPD
, fd
->obj_id
, objix
->p_hdr
.span_ix
, new_objix_pix
, 0);
1530 // load next object index page
1531 if (cur_objix_spix
== 0) {
1532 // load object index header page, must exist
1533 SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg
":"_SPIPRIsp
"\n", cur_objix_pix
, cur_objix_spix
);
1534 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1535 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, cur_objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1536 SPIFFS_CHECK_RES(res
);
1537 SPIFFS_VALIDATE_OBJIX(objix_hdr
->p_hdr
, fd
->obj_id
, cur_objix_spix
);
1539 // load existing object index page on first pass
1541 SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp
"\n", cur_objix_spix
);
1542 if (fd
->cursor_objix_spix
== cur_objix_spix
) {
1543 pix
= fd
->cursor_objix_pix
;
1545 res
= spiffs_obj_lu_find_id_and_span(fs
, fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
, cur_objix_spix
, 0, &pix
);
1546 SPIFFS_CHECK_RES(res
);
1548 SPIFFS_DBG("modify: found object index at page "_SPIPRIpg
"\n", pix
);
1549 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1550 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1551 SPIFFS_CHECK_RES(res
);
1552 SPIFFS_VALIDATE_OBJIX(objix_hdr
->p_hdr
, fd
->obj_id
, cur_objix_spix
);
1553 cur_objix_pix
= pix
;
1555 fd
->cursor_objix_pix
= cur_objix_pix
;
1556 fd
->cursor_objix_spix
= cur_objix_spix
;
1557 fd
->offset
= offset
+ written
;
1558 prev_objix_spix
= cur_objix_spix
;
1561 // write partial data
1562 u32_t to_write
= MIN(len
- written
, SPIFFS_DATA_PAGE_SIZE(fs
) - page_offs
);
1563 spiffs_page_ix orig_data_pix
;
1564 if (cur_objix_spix
== 0) {
1565 // get data page from object index header page
1566 orig_data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
];
1568 // get data page from object index page
1569 orig_data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)];
1572 p_hdr
.obj_id
= fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
;
1573 p_hdr
.span_ix
= data_spix
;
1575 if (page_offs
== 0 && to_write
== SPIFFS_DATA_PAGE_SIZE(fs
)) {
1576 // a full page, allocate and write a new page of data
1577 res
= spiffs_page_allocate_data(fs
, fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
,
1578 &p_hdr
, &data
[written
], to_write
, page_offs
, 1, &data_pix
);
1579 SPIFFS_DBG("modify: store new data page, "_SPIPRIpg
":"_SPIPRIsp
" offset:"_SPIPRIi
", len "_SPIPRIi
", written "_SPIPRIi
"\n", data_pix
, data_spix
, page_offs
, to_write
, written
);
1580 if (res
!= SPIFFS_OK
) break;
1582 // write to existing page, allocate new and copy unmodified data
1584 res
= spiffs_page_data_check(fs
, fd
, orig_data_pix
, data_spix
);
1585 SPIFFS_CHECK_RES(res
);
1587 res
= spiffs_page_allocate_data(fs
, fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
,
1588 &p_hdr
, 0, 0, 0, 0, &data_pix
);
1589 if (res
!= SPIFFS_OK
) break;
1591 // copy unmodified data
1592 if (page_offs
> 0) {
1593 // before modification
1594 res
= spiffs_phys_cpy(fs
, fd
->file_nbr
,
1595 SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + sizeof(spiffs_page_header
),
1596 SPIFFS_PAGE_TO_PADDR(fs
, orig_data_pix
) + sizeof(spiffs_page_header
),
1598 if (res
!= SPIFFS_OK
) break;
1600 if (page_offs
+ to_write
< SPIFFS_DATA_PAGE_SIZE(fs
)) {
1601 // after modification
1602 res
= spiffs_phys_cpy(fs
, fd
->file_nbr
,
1603 SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + sizeof(spiffs_page_header
) + page_offs
+ to_write
,
1604 SPIFFS_PAGE_TO_PADDR(fs
, orig_data_pix
) + sizeof(spiffs_page_header
) + page_offs
+ to_write
,
1605 SPIFFS_DATA_PAGE_SIZE(fs
) - (page_offs
+ to_write
));
1606 if (res
!= SPIFFS_OK
) break;
1609 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
1611 SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + sizeof(spiffs_page_header
) + page_offs
, to_write
, &data
[written
]);
1612 if (res
!= SPIFFS_OK
) break;
1613 p_hdr
.flags
&= ~SPIFFS_PH_FLAG_FINAL
;
1614 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
1616 SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + offsetof(spiffs_page_header
, flags
),
1618 (u8_t
*)&p_hdr
.flags
);
1619 if (res
!= SPIFFS_OK
) break;
1621 SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg
", dst:"_SPIPRIpg
":"_SPIPRIsp
" offset:"_SPIPRIi
", len "_SPIPRIi
", written "_SPIPRIi
"\n", orig_data_pix
, data_pix
, data_spix
, page_offs
, to_write
, written
);
1624 // delete original data page
1625 res
= spiffs_page_delete(fs
, orig_data_pix
);
1626 if (res
!= SPIFFS_OK
) break;
1627 // update memory representation of object index page with new data page
1628 if (cur_objix_spix
== 0) {
1629 // update object index header page
1630 ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
] = data_pix
;
1631 SPIFFS_DBG("modify: wrote page "_SPIPRIpg
" to objix_hdr entry "_SPIPRIsp
" in mem\n", data_pix
, data_spix
);
1633 // update object index page
1634 ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)] = data_pix
;
1635 SPIFFS_DBG("modify: wrote page "_SPIPRIpg
" to objix entry "_SPIPRIsp
" in mem\n", data_pix
, (spiffs_span_ix
)SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
));
1641 written
+= to_write
;
1644 fd
->offset
= offset
+ written
;
1645 fd
->cursor_objix_pix
= cur_objix_pix
;
1646 fd
->cursor_objix_spix
= cur_objix_spix
;
1648 // finalize updated object indices
1649 s32_t res2
= SPIFFS_OK
;
1650 if (cur_objix_spix
!= 0) {
1651 // wrote beyond object index header page
1652 // write last modified object index page
1653 // move and update page
1654 spiffs_page_ix new_objix_pix
;
1656 res2
= spiffs_page_index_check(fs
, fd
, cur_objix_pix
, cur_objix_spix
);
1657 SPIFFS_CHECK_RES(res2
);
1659 res2
= spiffs_page_move(fs
, fd
->file_nbr
, (u8_t
*)objix
, fd
->obj_id
, 0, cur_objix_pix
, &new_objix_pix
);
1660 SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", new_objix_pix
, cur_objix_spix
, written
);
1661 fd
->cursor_objix_pix
= new_objix_pix
;
1662 fd
->cursor_objix_spix
= cur_objix_spix
;
1663 SPIFFS_CHECK_RES(res2
);
1664 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)objix
,
1665 SPIFFS_EV_IX_UPD
, fd
->obj_id
, objix
->p_hdr
.span_ix
, new_objix_pix
, 0);
1668 // wrote within object index header page
1669 res2
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1670 fd
->objix_hdr_pix
, fs
->work
, 0, 0, 0, &new_objix_hdr_pix
);
1671 SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg
":"_SPIPRIsp
", written "_SPIPRIi
"\n", new_objix_hdr_pix
, 0, written
);
1672 SPIFFS_CHECK_RES(res2
);
1676 } // spiffs_object_modify
1677 #endif // !SPIFFS_READ_ONLY
1679 static s32_t
spiffs_object_find_object_index_header_by_name_v(
1681 spiffs_obj_id obj_id
,
1682 spiffs_block_ix bix
,
1684 const void *user_const_p
,
1688 spiffs_page_object_ix_header objix_hdr
;
1689 spiffs_page_ix pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, ix_entry
);
1690 if (obj_id
== SPIFFS_OBJ_ID_FREE
|| obj_id
== SPIFFS_OBJ_ID_DELETED
||
1691 (obj_id
& SPIFFS_OBJ_ID_IX_FLAG
) == 0) {
1692 return SPIFFS_VIS_COUNTINUE
;
1694 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
1695 0, SPIFFS_PAGE_TO_PADDR(fs
, pix
), sizeof(spiffs_page_object_ix_header
), (u8_t
*)&objix_hdr
);
1696 SPIFFS_CHECK_RES(res
);
1697 if (objix_hdr
.p_hdr
.span_ix
== 0 &&
1698 (objix_hdr
.p_hdr
.flags
& (SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_IXDELE
)) ==
1699 (SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_IXDELE
)) {
1700 if (strcmp((const char *)user_const_p
, (char *)objix_hdr
.name
) == 0) {
1705 return SPIFFS_VIS_COUNTINUE
;
1708 // Finds object index header page by name
1709 s32_t
spiffs_object_find_object_index_header_by_name(
1711 const u8_t name
[SPIFFS_OBJ_NAME_LEN
],
1712 spiffs_page_ix
*pix
) {
1714 spiffs_block_ix bix
;
1717 res
= spiffs_obj_lu_find_entry_visitor(fs
,
1718 fs
->cursor_block_ix
,
1719 fs
->cursor_obj_lu_entry
,
1722 spiffs_object_find_object_index_header_by_name_v
,
1728 if (res
== SPIFFS_VIS_END
) {
1729 res
= SPIFFS_ERR_NOT_FOUND
;
1731 SPIFFS_CHECK_RES(res
);
1734 *pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, entry
);
1737 fs
->cursor_block_ix
= bix
;
1738 fs
->cursor_obj_lu_entry
= entry
;
1743 #if !SPIFFS_READ_ONLY
1744 // Truncates object to new size. If new size is null, object may be removed totally
1745 s32_t
spiffs_object_truncate(
1749 s32_t res
= SPIFFS_OK
;
1750 spiffs
*fs
= fd
->fs
;
1752 if ((fd
->size
== SPIFFS_UNDEFINED_LEN
|| fd
->size
== 0) && !remove_full
) {
1757 // need 2 pages if not removing: object index page + possibly chopped data page
1758 if (remove_full
== 0) {
1759 res
= spiffs_gc_check(fs
, SPIFFS_DATA_PAGE_SIZE(fs
) * 2);
1760 SPIFFS_CHECK_RES(res
);
1763 spiffs_page_ix objix_pix
= fd
->objix_hdr_pix
;
1764 spiffs_span_ix data_spix
= (fd
->size
> 0 ? fd
->size
- 1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs
);
1765 u32_t cur_size
= fd
->size
== (u32_t
)SPIFFS_UNDEFINED_LEN
? 0 : fd
->size
;
1766 spiffs_span_ix cur_objix_spix
= 0;
1767 spiffs_span_ix prev_objix_spix
= (spiffs_span_ix
) - 1;
1768 spiffs_page_object_ix_header
*objix_hdr
= (spiffs_page_object_ix_header
*)fs
->work
;
1769 spiffs_page_object_ix
*objix
= (spiffs_page_object_ix
*)fs
->work
;
1770 spiffs_page_ix data_pix
;
1771 spiffs_page_ix new_objix_hdr_pix
;
1773 // before truncating, check if object is to be fully removed and mark this
1774 if (remove_full
&& new_size
== 0) {
1775 u8_t flags
= ~(SPIFFS_PH_FLAG_USED
| SPIFFS_PH_FLAG_INDEX
| SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_IXDELE
);
1776 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_UPDT
,
1777 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, fd
->objix_hdr_pix
) + offsetof(spiffs_page_header
, flags
),
1780 SPIFFS_CHECK_RES(res
);
1783 // delete from end of object until desired len is reached
1784 while (cur_size
> new_size
) {
1785 cur_objix_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, data_spix
);
1787 // put object index for current data span index in work buffer
1788 if (prev_objix_spix
!= cur_objix_spix
) {
1789 if (prev_objix_spix
!= (spiffs_span_ix
) - 1) {
1790 // remove previous object index page
1791 SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg
":"_SPIPRIsp
"\n", objix_pix
, prev_objix_spix
);
1793 res
= spiffs_page_index_check(fs
, fd
, objix_pix
, prev_objix_spix
);
1794 SPIFFS_CHECK_RES(res
);
1796 res
= spiffs_page_delete(fs
, objix_pix
);
1797 SPIFFS_CHECK_RES(res
);
1798 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)0,
1799 SPIFFS_EV_IX_DEL
, fd
->obj_id
, objix
->p_hdr
.span_ix
, objix_pix
, 0);
1800 if (prev_objix_spix
> 0) {
1801 // Update object index header page, unless we totally want to remove the file.
1802 // If fully removing, we're not keeping consistency as good as when storing the header between chunks,
1803 // would we be aborted. But when removing full files, a crammed system may otherwise
1804 // report ERR_FULL a la windows. We cannot have that.
1805 // Hence, take the risk - if aborted, a file check would free the lost pages and mend things
1806 // as the file is marked as fully deleted in the beginning.
1807 if (remove_full
== 0) {
1808 SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg
":"_SPIPRIsp
" to size "_SPIPRIi
"\n", fd
->objix_hdr_pix
, prev_objix_spix
, cur_size
);
1809 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1810 fd
->objix_hdr_pix
, 0, 0, 0, cur_size
, &new_objix_hdr_pix
);
1811 SPIFFS_CHECK_RES(res
);
1813 fd
->size
= cur_size
;
1816 // load current object index (header) page
1817 if (cur_objix_spix
== 0) {
1818 objix_pix
= fd
->objix_hdr_pix
;
1820 res
= spiffs_obj_lu_find_id_and_span(fs
, fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
, cur_objix_spix
, 0, &objix_pix
);
1821 SPIFFS_CHECK_RES(res
);
1824 SPIFFS_DBG("truncate: load objix page "_SPIPRIpg
":"_SPIPRIsp
" for data spix:"_SPIPRIsp
"\n", objix_pix
, cur_objix_spix
, data_spix
);
1825 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
1826 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
1827 SPIFFS_CHECK_RES(res
);
1828 SPIFFS_VALIDATE_OBJIX(objix_hdr
->p_hdr
, fd
->obj_id
, cur_objix_spix
);
1829 fd
->cursor_objix_pix
= objix_pix
;
1830 fd
->cursor_objix_spix
= cur_objix_spix
;
1831 fd
->offset
= cur_size
;
1833 prev_objix_spix
= cur_objix_spix
;
1836 if (cur_objix_spix
== 0) {
1837 // get data page from object index header page
1838 data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
];
1839 ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
] = SPIFFS_OBJ_ID_FREE
;
1841 // get data page from object index page
1842 data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)];
1843 ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)] = SPIFFS_OBJ_ID_FREE
;
1846 SPIFFS_DBG("truncate: got data pix "_SPIPRIpg
"\n", data_pix
);
1848 if (new_size
== 0 || remove_full
|| cur_size
- new_size
>= SPIFFS_DATA_PAGE_SIZE(fs
)) {
1849 // delete full data page
1850 res
= spiffs_page_data_check(fs
, fd
, data_pix
, data_spix
);
1851 if (res
!= SPIFFS_ERR_DELETED
&& res
!= SPIFFS_OK
&& res
!= SPIFFS_ERR_INDEX_REF_FREE
) {
1852 SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi
"\n", res
);
1856 if (res
== SPIFFS_OK
) {
1857 res
= spiffs_page_delete(fs
, data_pix
);
1858 if (res
!= SPIFFS_OK
) {
1859 SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi
"\n", res
);
1862 } else if (res
== SPIFFS_ERR_DELETED
|| res
== SPIFFS_ERR_INDEX_REF_FREE
) {
1866 // update current size
1867 if (cur_size
% SPIFFS_DATA_PAGE_SIZE(fs
) == 0) {
1868 cur_size
-= SPIFFS_DATA_PAGE_SIZE(fs
);
1870 cur_size
-= cur_size
% SPIFFS_DATA_PAGE_SIZE(fs
);
1872 fd
->size
= cur_size
;
1873 fd
->offset
= cur_size
;
1874 SPIFFS_DBG("truncate: delete data page "_SPIPRIpg
" for data spix:"_SPIPRIsp
", cur_size:"_SPIPRIi
"\n", data_pix
, data_spix
, cur_size
);
1876 // delete last page, partially
1877 spiffs_page_header p_hdr
;
1878 spiffs_page_ix new_data_pix
;
1879 u32_t bytes_to_remove
= SPIFFS_DATA_PAGE_SIZE(fs
) - (new_size
% SPIFFS_DATA_PAGE_SIZE(fs
));
1880 SPIFFS_DBG("truncate: delete "_SPIPRIi
" bytes from data page "_SPIPRIpg
" for data spix:"_SPIPRIsp
", cur_size:"_SPIPRIi
"\n", bytes_to_remove
, data_pix
, data_spix
, cur_size
);
1882 res
= spiffs_page_data_check(fs
, fd
, data_pix
, data_spix
);
1883 if (res
!= SPIFFS_OK
) break;
1885 p_hdr
.obj_id
= fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
;
1886 p_hdr
.span_ix
= data_spix
;
1888 // allocate new page and copy unmodified data
1889 res
= spiffs_page_allocate_data(fs
, fd
->obj_id
& ~SPIFFS_OBJ_ID_IX_FLAG
,
1890 &p_hdr
, 0, 0, 0, 0, &new_data_pix
);
1891 if (res
!= SPIFFS_OK
) break;
1892 res
= spiffs_phys_cpy(fs
, 0,
1893 SPIFFS_PAGE_TO_PADDR(fs
, new_data_pix
) + sizeof(spiffs_page_header
),
1894 SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + sizeof(spiffs_page_header
),
1895 SPIFFS_DATA_PAGE_SIZE(fs
) - bytes_to_remove
);
1896 if (res
!= SPIFFS_OK
) break;
1897 // delete original data page
1898 res
= spiffs_page_delete(fs
, data_pix
);
1899 if (res
!= SPIFFS_OK
) break;
1900 p_hdr
.flags
&= ~SPIFFS_PH_FLAG_FINAL
;
1901 res
= _spiffs_wr(fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_UPDT
,
1903 SPIFFS_PAGE_TO_PADDR(fs
, new_data_pix
) + offsetof(spiffs_page_header
, flags
),
1905 (u8_t
*)&p_hdr
.flags
);
1906 if (res
!= SPIFFS_OK
) break;
1908 // update memory representation of object index page with new data page
1909 if (cur_objix_spix
== 0) {
1910 // update object index header page
1911 ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
] = new_data_pix
;
1912 SPIFFS_DBG("truncate: wrote page "_SPIPRIpg
" to objix_hdr entry "_SPIPRIsp
" in mem\n", new_data_pix
, (spiffs_span_ix
)SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
));
1914 // update object index page
1915 ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)] = new_data_pix
;
1916 SPIFFS_DBG("truncate: wrote page "_SPIPRIpg
" to objix entry "_SPIPRIsp
" in mem\n", new_data_pix
, (spiffs_span_ix
)SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
));
1918 cur_size
= new_size
;
1919 fd
->size
= new_size
;
1920 fd
->offset
= cur_size
;
1926 // update object indices
1927 if (cur_objix_spix
== 0) {
1928 // update object index header page
1929 if (cur_size
== 0) {
1931 // remove object altogether
1932 SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg
"\n", objix_pix
);
1934 res
= spiffs_page_index_check(fs
, fd
, objix_pix
, 0);
1935 SPIFFS_CHECK_RES(res
);
1937 res
= spiffs_page_delete(fs
, objix_pix
);
1938 SPIFFS_CHECK_RES(res
);
1939 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)0,
1940 SPIFFS_EV_IX_DEL
, fd
->obj_id
, 0, objix_pix
, 0);
1942 // make uninitialized object
1943 SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg
"\n", objix_pix
);
1944 memset(fs
->work
+ sizeof(spiffs_page_object_ix_header
), 0xff,
1945 SPIFFS_CFG_LOG_PAGE_SZ(fs
) - sizeof(spiffs_page_object_ix_header
));
1946 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1947 objix_pix
, fs
->work
, 0, 0, SPIFFS_UNDEFINED_LEN
, &new_objix_hdr_pix
);
1948 SPIFFS_CHECK_RES(res
);
1951 // update object index header page
1952 SPIFFS_DBGF("truncate: update object index header page with indices and size\n");
1953 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1954 objix_pix
, fs
->work
, 0, 0, cur_size
, &new_objix_hdr_pix
);
1955 SPIFFS_CHECK_RES(res
);
1958 // update both current object index page and object index header page
1959 spiffs_page_ix new_objix_pix
;
1961 res
= spiffs_page_index_check(fs
, fd
, objix_pix
, cur_objix_spix
);
1962 SPIFFS_CHECK_RES(res
);
1964 // move and update object index page
1965 res
= spiffs_page_move(fs
, fd
->file_nbr
, (u8_t
*)objix_hdr
, fd
->obj_id
, 0, objix_pix
, &new_objix_pix
);
1966 SPIFFS_CHECK_RES(res
);
1967 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)objix_hdr
,
1968 SPIFFS_EV_IX_UPD
, fd
->obj_id
, objix
->p_hdr
.span_ix
, new_objix_pix
, 0);
1969 SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg
":"_SPIPRIsp
"\n", new_objix_pix
, cur_objix_spix
);
1970 fd
->cursor_objix_pix
= new_objix_pix
;
1971 fd
->cursor_objix_spix
= cur_objix_spix
;
1972 fd
->offset
= cur_size
;
1973 // update object index header page with new size
1974 res
= spiffs_object_update_index_hdr(fs
, fd
, fd
->obj_id
,
1975 fd
->objix_hdr_pix
, 0, 0, 0, cur_size
, &new_objix_hdr_pix
);
1976 SPIFFS_CHECK_RES(res
);
1978 fd
->size
= cur_size
;
1981 } // spiffs_object_truncate
1982 #endif // !SPIFFS_READ_ONLY
1984 s32_t
spiffs_object_read(
1989 s32_t res
= SPIFFS_OK
;
1990 spiffs
*fs
= fd
->fs
;
1991 spiffs_page_ix objix_pix
;
1992 spiffs_page_ix data_pix
;
1993 spiffs_span_ix data_spix
= offset
/ SPIFFS_DATA_PAGE_SIZE(fs
);
1994 u32_t cur_offset
= offset
;
1995 spiffs_span_ix cur_objix_spix
;
1996 spiffs_span_ix prev_objix_spix
= (spiffs_span_ix
) - 1;
1997 spiffs_page_object_ix_header
*objix_hdr
= (spiffs_page_object_ix_header
*)fs
->work
;
1998 spiffs_page_object_ix
*objix
= (spiffs_page_object_ix
*)fs
->work
;
2000 while (cur_offset
< offset
+ len
) {
2002 // check if we have a memory, index map and if so, if we're within index map's range
2003 // and if so, if the entry is populated
2004 if (fd
->ix_map
&& data_spix
>= fd
->ix_map
->start_spix
&& data_spix
<= fd
->ix_map
->end_spix
2005 && fd
->ix_map
->map_buf
[data_spix
- fd
->ix_map
->start_spix
]) {
2006 data_pix
= fd
->ix_map
->map_buf
[data_spix
- fd
->ix_map
->start_spix
];
2009 cur_objix_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, data_spix
);
2010 if (prev_objix_spix
!= cur_objix_spix
) {
2011 // load current object index (header) page
2012 if (cur_objix_spix
== 0) {
2013 objix_pix
= fd
->objix_hdr_pix
;
2015 SPIFFS_DBG("read: find objix "_SPIPRIid
":"_SPIPRIsp
"\n", fd
->obj_id
, cur_objix_spix
);
2016 if (fd
->cursor_objix_spix
== cur_objix_spix
) {
2017 objix_pix
= fd
->cursor_objix_pix
;
2019 res
= spiffs_obj_lu_find_id_and_span(fs
, fd
->obj_id
| SPIFFS_OBJ_ID_IX_FLAG
, cur_objix_spix
, 0, &objix_pix
);
2020 SPIFFS_CHECK_RES(res
);
2023 SPIFFS_DBG("read: load objix page "_SPIPRIpg
":"_SPIPRIsp
" for data spix:"_SPIPRIsp
"\n", objix_pix
, cur_objix_spix
, data_spix
);
2024 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_IX
| SPIFFS_OP_C_READ
,
2025 fd
->file_nbr
, SPIFFS_PAGE_TO_PADDR(fs
, objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
2026 SPIFFS_CHECK_RES(res
);
2027 SPIFFS_VALIDATE_OBJIX(objix
->p_hdr
, fd
->obj_id
, cur_objix_spix
);
2029 fd
->offset
= cur_offset
;
2030 fd
->cursor_objix_pix
= objix_pix
;
2031 fd
->cursor_objix_spix
= cur_objix_spix
;
2033 prev_objix_spix
= cur_objix_spix
;
2036 if (cur_objix_spix
== 0) {
2037 // get data page from object index header page
2038 data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[data_spix
];
2040 // get data page from object index page
2041 data_pix
= ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, data_spix
)];
2046 // all remaining data
2047 u32_t len_to_read
= offset
+ len
- cur_offset
;
2048 // remaining data in page
2049 len_to_read
= MIN(len_to_read
, SPIFFS_DATA_PAGE_SIZE(fs
) - (cur_offset
% SPIFFS_DATA_PAGE_SIZE(fs
)));
2050 // remaining data in file
2051 len_to_read
= MIN(len_to_read
, fd
->size
- cur_offset
);
2052 SPIFFS_DBG("read: offset:"_SPIPRIi
" rd:"_SPIPRIi
" data spix:"_SPIPRIsp
" is data_pix:"_SPIPRIpg
" addr:"_SPIPRIad
"\n", cur_offset
, len_to_read
, data_spix
, data_pix
,
2053 (u32_t
)(SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + sizeof(spiffs_page_header
) + (cur_offset
% SPIFFS_DATA_PAGE_SIZE(fs
))));
2054 if (len_to_read
== 0) {
2055 res
= SPIFFS_ERR_END_OF_OBJECT
;
2058 res
= spiffs_page_data_check(fs
, fd
, data_pix
, data_spix
);
2059 SPIFFS_CHECK_RES(res
);
2061 fs
, SPIFFS_OP_T_OBJ_DA
| SPIFFS_OP_C_READ
,
2063 SPIFFS_PAGE_TO_PADDR(fs
, data_pix
) + sizeof(spiffs_page_header
) + (cur_offset
% SPIFFS_DATA_PAGE_SIZE(fs
)),
2066 SPIFFS_CHECK_RES(res
);
2068 cur_offset
+= len_to_read
;
2069 fd
->offset
= cur_offset
;
2076 #if !SPIFFS_READ_ONLY
2078 spiffs_obj_id min_obj_id
;
2079 spiffs_obj_id max_obj_id
;
2081 const u8_t
*conflicting_name
;
2082 } spiffs_free_obj_id_state
;
2084 static s32_t
spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs
*fs
, spiffs_obj_id id
, spiffs_block_ix bix
, int ix_entry
,
2085 const void *user_const_p
, void *user_var_p
) {
2086 if (id
!= SPIFFS_OBJ_ID_FREE
&& id
!= SPIFFS_OBJ_ID_DELETED
) {
2087 spiffs_obj_id min_obj_id
= *((spiffs_obj_id
*)user_var_p
);
2088 const u8_t
*conflicting_name
= (const u8_t
*)user_const_p
;
2090 // if conflicting name parameter is given, also check if this name is found in object index hdrs
2091 if (conflicting_name
&& (id
& SPIFFS_OBJ_ID_IX_FLAG
)) {
2092 spiffs_page_ix pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, ix_entry
);
2094 spiffs_page_object_ix_header objix_hdr
;
2095 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
2096 0, SPIFFS_PAGE_TO_PADDR(fs
, pix
), sizeof(spiffs_page_object_ix_header
), (u8_t
*)&objix_hdr
);
2097 SPIFFS_CHECK_RES(res
);
2098 if (objix_hdr
.p_hdr
.span_ix
== 0 &&
2099 (objix_hdr
.p_hdr
.flags
& (SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_IXDELE
)) ==
2100 (SPIFFS_PH_FLAG_DELET
| SPIFFS_PH_FLAG_IXDELE
)) {
2101 if (strcmp((const char *)user_const_p
, (char *)objix_hdr
.name
) == 0) {
2102 return SPIFFS_ERR_CONFLICTING_NAME
;
2107 id
&= ~SPIFFS_OBJ_ID_IX_FLAG
;
2108 u32_t bit_ix
= (id
- min_obj_id
) & 7;
2109 int byte_ix
= (id
- min_obj_id
) >> 3;
2110 if (byte_ix
>= 0 && (u32_t
)byte_ix
< SPIFFS_CFG_LOG_PAGE_SZ(fs
)) {
2111 fs
->work
[byte_ix
] |= (1 << bit_ix
);
2114 return SPIFFS_VIS_COUNTINUE
;
2117 static s32_t
spiffs_obj_lu_find_free_obj_id_compact_v(spiffs
*fs
, spiffs_obj_id id
, spiffs_block_ix bix
, int ix_entry
,
2118 const void *user_const_p
, void *user_var_p
) {
2120 if (id
!= SPIFFS_OBJ_ID_FREE
&& id
!= SPIFFS_OBJ_ID_DELETED
&& (id
& SPIFFS_OBJ_ID_IX_FLAG
)) {
2122 const spiffs_free_obj_id_state
*state
= (const spiffs_free_obj_id_state
*)user_const_p
;
2123 spiffs_page_object_ix_header objix_hdr
;
2125 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
2126 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs
, bix
, ix_entry
), sizeof(spiffs_page_object_ix_header
), (u8_t
*)&objix_hdr
);
2127 if (res
== SPIFFS_OK
&& objix_hdr
.p_hdr
.span_ix
== 0 &&
2128 ((objix_hdr
.p_hdr
.flags
& (SPIFFS_PH_FLAG_INDEX
| SPIFFS_PH_FLAG_FINAL
| SPIFFS_PH_FLAG_DELET
)) ==
2129 (SPIFFS_PH_FLAG_DELET
))) {
2130 // ok object look up entry
2131 if (state
->conflicting_name
&& strcmp((const char *)state
->conflicting_name
, (char *)objix_hdr
.name
) == 0) {
2132 return SPIFFS_ERR_CONFLICTING_NAME
;
2135 id
&= ~SPIFFS_OBJ_ID_IX_FLAG
;
2136 if (id
>= state
->min_obj_id
&& id
<= state
->max_obj_id
) {
2137 u8_t
*map
= (u8_t
*)fs
->work
;
2138 int ix
= (id
- state
->min_obj_id
) / state
->compaction
;
2139 //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction);
2144 return SPIFFS_VIS_COUNTINUE
;
2147 // Scans thru all object lookup for object index header pages. If total possible number of
2148 // object ids cannot fit into a work buffer, these are grouped. When a group containing free
2149 // object ids is found, the object lu is again scanned for object ids within group and bitmasked.
2150 // Finally, the bitmask is searched for a free id
2151 s32_t
spiffs_obj_lu_find_free_obj_id(spiffs
*fs
, spiffs_obj_id
*obj_id
, const u8_t
*conflicting_name
) {
2152 s32_t res
= SPIFFS_OK
;
2153 u32_t max_objects
= (fs
->block_count
* SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs
)) / 2;
2154 spiffs_free_obj_id_state state
;
2155 spiffs_obj_id free_obj_id
= SPIFFS_OBJ_ID_FREE
;
2156 state
.min_obj_id
= 1;
2157 state
.max_obj_id
= max_objects
+ 1;
2158 if (state
.max_obj_id
& SPIFFS_OBJ_ID_IX_FLAG
) {
2159 state
.max_obj_id
= ((spiffs_obj_id
) - 1) & ~SPIFFS_OBJ_ID_IX_FLAG
;
2161 state
.compaction
= 0;
2162 state
.conflicting_name
= conflicting_name
;
2163 while (res
== SPIFFS_OK
&& free_obj_id
== SPIFFS_OBJ_ID_FREE
) {
2164 if (state
.max_obj_id
- state
.min_obj_id
<= (spiffs_obj_id
)SPIFFS_CFG_LOG_PAGE_SZ(fs
) * 8) {
2165 // possible to represent in bitmap
2167 SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid
" max:"_SPIPRIid
"\n", state
.min_obj_id
, state
.max_obj_id
);
2169 memset(fs
->work
, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs
));
2170 res
= spiffs_obj_lu_find_entry_visitor(fs
, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v
,
2171 conflicting_name
, &state
.min_obj_id
, 0, 0);
2172 if (res
== SPIFFS_VIS_END
) res
= SPIFFS_OK
;
2173 SPIFFS_CHECK_RES(res
);
2174 // traverse bitmask until found free obj_id
2175 for (i
= 0; i
< SPIFFS_CFG_LOG_PAGE_SZ(fs
); i
++) {
2176 u8_t mask
= fs
->work
[i
];
2180 for (j
= 0; j
< 8; j
++) {
2181 if ((mask
& (1 << j
)) == 0) {
2182 *obj_id
= (i
<< 3) + j
+ state
.min_obj_id
;
2187 return SPIFFS_ERR_FULL
;
2189 // not possible to represent all ids in range in a bitmap, compact and count
2190 if (state
.compaction
!= 0) {
2191 // select element in compacted table, decrease range and recompact
2193 u8_t
*map
= (u8_t
*)fs
->work
;
2194 u8_t min_count
= 0xff;
2196 for (i
= 0; i
< SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(u8_t
); i
++) {
2197 if (map
[i
] < min_count
) {
2200 if (min_count
== 0) {
2206 if (min_count
== state
.compaction
) {
2207 // there are no free objids!
2208 SPIFFS_DBGF("free_obj_id: compacted table is full\n");
2209 return SPIFFS_ERR_FULL
;
2212 SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi
" min_count:"_SPIPRIi
" min:"_SPIPRIid
" max:"_SPIPRIid
" compact:"_SPIPRIi
"\n", min_i
, min_count
, state
.min_obj_id
, state
.max_obj_id
, state
.compaction
);
2214 if (min_count
== 0) {
2215 // no id in this range, skip compacting and use directly
2216 *obj_id
= min_i
* state
.compaction
+ state
.min_obj_id
;
2219 SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi
" min:"_SPIPRIid
" -> "_SPIPRIid
"\n", state
.compaction
, state
.min_obj_id
, state
.min_obj_id
+ min_i
* state
.compaction
);
2220 state
.min_obj_id
+= min_i
* state
.compaction
;
2221 state
.max_obj_id
= state
.min_obj_id
+ state
.compaction
;
2222 // decrease compaction
2224 if ((state
.max_obj_id
- state
.min_obj_id
<= (spiffs_obj_id
)SPIFFS_CFG_LOG_PAGE_SZ(fs
) * 8)) {
2225 // no need for compacting, use bitmap
2229 // in a work memory of log_page_size bytes, we may fit in log_page_size ids
2230 // todo what if compaction is > 255 - then we cannot fit it in a byte
2231 state
.compaction
= (state
.max_obj_id
- state
.min_obj_id
) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(u8_t
)));
2232 SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid
" max:"_SPIPRIid
" compact:"_SPIPRIi
"\n", state
.min_obj_id
, state
.max_obj_id
, state
.compaction
);
2234 memset(fs
->work
, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs
));
2235 res
= spiffs_obj_lu_find_entry_visitor(fs
, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v
, &state
, 0, 0, 0);
2236 if (res
== SPIFFS_VIS_END
) res
= SPIFFS_OK
;
2237 SPIFFS_CHECK_RES(res
);
2238 state
.conflicting_name
= 0; // searched for conflicting name once, no need to do it again
2244 #endif // !SPIFFS_READ_ONLY
2246 #if SPIFFS_TEMPORAL_FD_CACHE
2248 static u32_t
spiffs_hash(spiffs
*fs
, const u8_t
*name
) {
2253 while ((c
= name
[i
++]) && i
< SPIFFS_OBJ_NAME_LEN
) {
2254 hash
= (hash
* 33) ^ c
;
2260 s32_t
spiffs_fd_find_new(spiffs
*fs
, spiffs_fd
**fd
, const char *name
) {
2261 #if SPIFFS_TEMPORAL_FD_CACHE
2263 u16_t min_score
= 0xffff;
2264 u32_t cand_ix
= (u32_t
) - 1;
2265 u32_t name_hash
= name
? spiffs_hash(fs
, (const u8_t
*)name
) : 0;
2266 spiffs_fd
*fds
= (spiffs_fd
*)fs
->fd_space
;
2269 // first, decrease score of all closed descriptors
2270 for (i
= 0; i
< fs
->fd_count
; i
++) {
2271 spiffs_fd
*cur_fd
= &fds
[i
];
2272 if (cur_fd
->file_nbr
== 0) {
2273 if (cur_fd
->score
> 1) { // score == 0 indicates never used fd
2280 // find the free fd with least score or name match
2281 for (i
= 0; i
< fs
->fd_count
; i
++) {
2282 spiffs_fd
*cur_fd
= &fds
[i
];
2283 if (cur_fd
->file_nbr
== 0) {
2284 if (name
&& cur_fd
->name_hash
== name_hash
) {
2288 if (cur_fd
->score
< min_score
) {
2289 min_score
= cur_fd
->score
;
2295 if (cand_ix
!= (u32_t
) - 1) {
2296 spiffs_fd
*cur_fd
= &fds
[cand_ix
];
2298 if (cur_fd
->name_hash
== name_hash
&& cur_fd
->score
> 0) {
2299 // opened an fd with same name hash, assume same file
2300 // set search point to saved obj index page and hope we have a correct match directly
2301 // when start searching - if not, we will just keep searching until it is found
2302 fs
->cursor_block_ix
= SPIFFS_BLOCK_FOR_PAGE(fs
, cur_fd
->objix_hdr_pix
);
2303 fs
->cursor_obj_lu_entry
= SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs
, cur_fd
->objix_hdr_pix
);
2305 if (cur_fd
->score
< 0xffff - SPIFFS_TEMPORAL_CACHE_HIT_SCORE
) {
2306 cur_fd
->score
+= SPIFFS_TEMPORAL_CACHE_HIT_SCORE
;
2308 cur_fd
->score
= 0xffff;
2311 // no hash hit, restore this fd to initial state
2312 cur_fd
->score
= SPIFFS_TEMPORAL_CACHE_HIT_SCORE
;
2313 cur_fd
->name_hash
= name_hash
;
2316 cur_fd
->file_nbr
= cand_ix
+ 1;
2320 return SPIFFS_ERR_OUT_OF_FILE_DESCS
;
2325 spiffs_fd
*fds
= (spiffs_fd
*)fs
->fd_space
;
2326 for (i
= 0; i
< fs
->fd_count
; i
++) {
2327 spiffs_fd
*cur_fd
= &fds
[i
];
2328 if (cur_fd
->file_nbr
== 0) {
2329 cur_fd
->file_nbr
= i
+ 1;
2334 return SPIFFS_ERR_OUT_OF_FILE_DESCS
;
2338 s32_t
spiffs_fd_return(spiffs
*fs
, spiffs_file f
) {
2339 if (f
<= 0 || f
> (s16_t
)fs
->fd_count
) {
2340 return SPIFFS_ERR_BAD_DESCRIPTOR
;
2342 spiffs_fd
*fds
= (spiffs_fd
*)fs
->fd_space
;
2343 spiffs_fd
*fd
= &fds
[f
- 1];
2344 if (fd
->file_nbr
== 0) {
2345 return SPIFFS_ERR_FILE_CLOSED
;
2354 s32_t
spiffs_fd_get(spiffs
*fs
, spiffs_file f
, spiffs_fd
**fd
) {
2355 if (f
<= 0 || f
> (s16_t
)fs
->fd_count
) {
2356 return SPIFFS_ERR_BAD_DESCRIPTOR
;
2358 spiffs_fd
*fds
= (spiffs_fd
*)fs
->fd_space
;
2360 if ((*fd
)->file_nbr
== 0) {
2361 return SPIFFS_ERR_FILE_CLOSED
;
2366 #if SPIFFS_TEMPORAL_FD_CACHE
2367 void spiffs_fd_temporal_cache_rehash(
2369 const char *old_path
,
2370 const char *new_path
) {
2372 u32_t old_hash
= spiffs_hash(fs
, (const u8_t
*)old_path
);
2373 u32_t new_hash
= spiffs_hash(fs
, (const u8_t
*)new_path
);
2374 spiffs_fd
*fds
= (spiffs_fd
*)fs
->fd_space
;
2375 for (i
= 0; i
< fs
->fd_count
; i
++) {
2376 spiffs_fd
*cur_fd
= &fds
[i
];
2377 if (cur_fd
->score
> 0 && cur_fd
->name_hash
== old_hash
) {
2378 cur_fd
->name_hash
= new_hash
;