Merge pull request #2654 from Antiklesys/master
[RRG-proxmark3.git] / armsrc / spiffs_nucleus.c
blob2ebfabe0423fda453faa7343baf57dddaeec5b7a
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 #include "spiffs.h"
19 #include "spiffs_nucleus.h"
20 #include "printf.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;
36 #if SPIFFS_PAGE_CHECK
37 spiffs_page_header ph;
38 res = _spiffs_rd(
39 fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
40 fd->file_nbr,
41 SPIFFS_PAGE_TO_PADDR(fs, pix),
42 sizeof(spiffs_page_header),
43 (u8_t *)&ph);
44 SPIFFS_CHECK_RES(res);
45 SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix);
46 #endif
47 return res;
50 #if !SPIFFS_READ_ONLY
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;
65 #if SPIFFS_PAGE_CHECK
66 spiffs_page_header ph;
67 res = _spiffs_rd(
68 fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
69 fd->file_nbr,
70 SPIFFS_PAGE_TO_PADDR(fs, pix),
71 sizeof(spiffs_page_header),
72 (u8_t *)&ph);
73 SPIFFS_CHECK_RES(res);
74 SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix);
75 #endif
76 return res;
78 #endif // !SPIFFS_READ_ONLY
80 #if !SPIFFS_CACHE
82 s32_t spiffs_phys_rd(
83 spiffs *fs,
84 u32_t addr,
85 u32_t len,
86 u8_t *dst) {
87 return SPIFFS_HAL_READ(fs, addr, len, dst);
90 s32_t spiffs_phys_wr(
91 spiffs *fs,
92 u32_t addr,
93 u32_t len,
94 u8_t *src) {
95 return SPIFFS_HAL_WRITE(fs, addr, len, src);
98 #endif
100 #if !SPIFFS_READ_ONLY
101 s32_t spiffs_phys_cpy(
102 spiffs *fs,
103 spiffs_file fh,
104 u32_t dst,
105 u32_t src,
106 u32_t len) {
107 (void)fh;
108 s32_t res;
109 u8_t b[SPIFFS_COPY_BUFFER_STACK];
110 while (len > 0) {
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);
116 len -= chunk_size;
117 src += chunk_size;
118 dst += chunk_size;
120 return SPIFFS_OK;
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(
143 spiffs *fs,
144 spiffs_block_ix starting_block,
145 int starting_lu_entry,
146 u8_t flags,
147 spiffs_obj_id obj_id,
148 spiffs_visitor_f v,
149 const void *user_const_p,
150 void *user_var_p,
151 spiffs_block_ix *block_ix,
152 int *lu_entry) {
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));
162 // wrap initial
163 if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) {
164 cur_entry = 0;
165 cur_block++;
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;
170 } else {
171 // block wrap
172 cur_block = 0;
173 cur_block_addr = 0;
178 // check each block
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);
186 // check each entry
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;
193 if (v) {
194 res = v(
196 (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry - entry_offset],
197 cur_block,
198 cur_entry,
199 user_const_p,
200 user_var_p);
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);
207 res = SPIFFS_OK;
208 cur_entry++;
209 entry_count--;
210 continue;
211 } else {
212 return res;
214 } else {
215 return SPIFFS_OK;
218 entry_count--;
219 cur_entry++;
220 } // per entry
221 obj_lookup_page++;
222 } // per object lookup page
223 cur_entry = 0;
224 cur_block++;
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;
229 } else {
230 // block wrap
231 cur_block = 0;
232 cur_block_addr = 0;
235 } // per block
237 SPIFFS_CHECK_RES(res);
239 return SPIFFS_VIS_END;
242 #if !SPIFFS_READ_ONLY
243 s32_t spiffs_erase_block(
244 spiffs *fs,
245 spiffs_block_ix bix) {
246 s32_t res;
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
251 while (size > 0) {
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);
258 fs->free_blocks++;
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);
266 #if SPIFFS_USE_MAGIC
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);
273 #endif
275 fs->max_erase_count++;
276 if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) {
277 fs->max_erase_count = 0;
280 return res;
282 #endif // !SPIFFS_READ_ONLY
284 #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
285 s32_t spiffs_probe(
286 spiffs_config *cfg) {
287 s32_t res;
288 u32_t paddr;
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];
298 spiffs_block_ix bix;
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]);
305 #else
306 res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]);
307 #endif
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(
338 spiffs *fs,
339 spiffs_obj_id obj_id,
340 spiffs_block_ix bix,
341 int ix_entry,
342 const void *user_const_p,
343 void *user_var_p) {
344 (void)bix;
345 (void)user_const_p;
346 (void)user_var_p;
347 if (obj_id == SPIFFS_OBJ_ID_FREE) {
348 if (ix_entry == 0) {
349 fs->free_blocks++;
350 // todo optimize further, return SPIFFS_NEXT_BLOCK
352 } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
353 fs->stats_p_deleted++;
354 } else {
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(
366 spiffs *fs) {
367 s32_t res;
368 spiffs_block_ix bix;
369 int entry;
370 #if SPIFFS_USE_MAGIC
371 spiffs_block_ix unerased_bix = (spiffs_block_ix) - 1;
372 #endif
374 // find out erase count
375 // if enabled, check magic
376 bix = 0;
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) {
381 #if SPIFFS_USE_MAGIC
382 spiffs_obj_id magic;
383 res = _spiffs_rd(fs,
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
392 unerased_bix = bix;
393 } else {
394 // more than one unerased block, bail out
395 SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS);
398 #endif
399 spiffs_obj_id erase_count;
400 res = _spiffs_rd(fs,
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);
409 bix++;
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) {
416 // wrap, take min
417 erase_count_final = erase_count_min + 1;
418 } else {
419 erase_count_final = erase_count_max + 1;
422 fs->max_erase_count = erase_count_final;
424 #if SPIFFS_USE_MAGIC
425 if (unerased_bix != (spiffs_block_ix) - 1) {
426 // found one unerased block, remedy
427 SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix);
428 #if SPIFFS_READ_ONLY
429 res = SPIFFS_ERR_RO_ABORTED_OPERATION;
430 #else
431 res = spiffs_erase_block(fs, unerased_bix);
432 #endif // SPIFFS_READ_ONLY
433 SPIFFS_CHECK_RES(res);
435 #endif
437 // count blocks
439 fs->free_blocks = 0;
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,
451 &bix,
452 &entry);
454 if (res == SPIFFS_VIS_END) {
455 res = SPIFFS_OK;
458 SPIFFS_CHECK_RES(res);
460 return 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(
467 spiffs *fs,
468 spiffs_block_ix starting_block,
469 int starting_lu_entry,
470 spiffs_block_ix *block_ix,
471 int *lu_entry) {
472 s32_t res;
473 if (!fs->cleaning && fs->free_blocks < 2) {
474 res = spiffs_gc_quick(fs, 0);
475 if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) {
476 res = SPIFFS_OK;
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) {
489 fs->free_blocks--;
492 if (res == SPIFFS_ERR_FULL) {
493 SPIFFS_DBGF("fs full\n");
496 return res;
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(
503 spiffs *fs,
504 spiffs_block_ix starting_block,
505 int starting_lu_entry,
506 spiffs_obj_id obj_id,
507 spiffs_block_ix *block_ix,
508 int *lu_entry) {
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;
514 return res;
518 static s32_t spiffs_obj_lu_find_id_and_span_v(
519 spiffs *fs,
520 spiffs_obj_id obj_id,
521 spiffs_block_ix bix,
522 int ix_entry,
523 const void *user_const_p,
524 void *user_var_p) {
525 s32_t res;
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)) {
536 return SPIFFS_OK;
537 } else {
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(
545 spiffs *fs,
546 spiffs_obj_id obj_id,
547 spiffs_span_ix spix,
548 spiffs_page_ix exclusion_pix,
549 spiffs_page_ix *pix) {
550 s32_t res;
551 spiffs_block_ix bix;
552 int entry;
554 res = spiffs_obj_lu_find_entry_visitor(fs,
555 fs->cursor_block_ix,
556 fs->cursor_obj_lu_entry,
557 SPIFFS_VIS_CHECK_ID,
558 obj_id,
559 spiffs_obj_lu_find_id_and_span_v,
560 exclusion_pix ? &exclusion_pix : 0,
561 &spix,
562 &bix,
563 &entry);
565 if (res == SPIFFS_VIS_END) {
566 res = SPIFFS_ERR_NOT_FOUND;
569 SPIFFS_CHECK_RES(res);
571 if (pix) {
572 *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
575 fs->cursor_block_ix = bix;
576 fs->cursor_obj_lu_entry = entry;
578 return res;
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(
584 spiffs *fs,
585 spiffs_obj_id obj_id,
586 spiffs_span_ix spix,
587 spiffs_page_ix exclusion_pix,
588 spiffs_page_ix *pix) {
589 s32_t res;
590 spiffs_block_ix bix;
591 int entry;
593 res = spiffs_obj_lu_find_entry_visitor(fs,
594 fs->cursor_block_ix,
595 fs->cursor_obj_lu_entry,
596 SPIFFS_VIS_CHECK_PH,
597 obj_id,
598 spiffs_obj_lu_find_id_and_span_v,
599 exclusion_pix ? &exclusion_pix : 0,
600 &spix,
601 &bix,
602 &entry);
604 if (res == SPIFFS_VIS_END) {
605 res = SPIFFS_ERR_NOT_FOUND;
608 SPIFFS_CHECK_RES(res);
610 if (pix) {
611 *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
614 fs->cursor_block_ix = bix;
615 fs->cursor_obj_lu_entry = entry;
617 return res;
620 #if SPIFFS_IX_MAP
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) {
625 #if SPIFFS_SINGLETON
626 (void)fs;
627 #endif
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) {
634 return;
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];
654 } else {
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
661 break;
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,
669 objix_data_pix);
671 map_spix++;
675 typedef struct {
676 spiffs_fd *fd;
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(
683 spiffs *fs,
684 spiffs_obj_id obj_id,
685 spiffs_block_ix bix,
686 int ix_entry,
687 const void *user_const_p,
688 void *user_var_p) {
689 (void)user_const_p;
690 s32_t res;
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",
717 state->fd->obj_id,
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;
725 return res;
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) {
730 s32_t res;
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;
742 state.fd = fd;
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),
748 SPIFFS_VIS_CHECK_ID,
749 fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
750 spiffs_populate_ix_map_v,
752 &state,
756 if (res == SPIFFS_VIS_END) {
757 res = SPIFFS_OK;
760 return res;
763 #endif
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(
771 spiffs *fs,
772 spiffs_obj_id obj_id,
773 spiffs_page_header *ph,
774 u8_t *data,
775 u32_t len,
776 u32_t page_offs,
777 u8_t finalize,
778 spiffs_page_ix *pix) {
779 s32_t res = SPIFFS_OK;
780 spiffs_block_ix bix;
781 int entry;
783 // find free entry
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++;
794 // write page header
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);
800 // write page data
801 if (data) {
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),
812 sizeof(u8_t),
813 (u8_t *)&ph->flags);
814 SPIFFS_CHECK_RES(res);
817 // return written page
818 if (pix) {
819 *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
822 return res;
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(
830 spiffs *fs,
831 spiffs_file fh,
832 u8_t *page_data,
833 spiffs_obj_id obj_id,
834 spiffs_page_header *page_hdr,
835 spiffs_page_ix src_pix,
836 spiffs_page_ix *dst_pix) {
837 s32_t res;
838 u8_t was_final = 0;
839 spiffs_page_header *p_hdr;
840 spiffs_block_ix bix;
841 int entry;
842 spiffs_page_ix free_pix;
844 // find free entry
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;
852 if (page_data) {
853 // got page data
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);
860 } else {
861 // copy 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),
870 (u8_t *)&obj_id);
871 SPIFFS_CHECK_RES(res);
873 fs->stats_p_allocated++;
875 if (was_final) {
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),
881 sizeof(u8_t),
882 (u8_t *)&p_hdr->flags);
883 SPIFFS_CHECK_RES(res);
885 // mark source deleted
886 res = spiffs_page_delete(fs, src_pix);
887 return res;
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(
894 spiffs *fs,
895 spiffs_page_ix pix) {
896 s32_t res;
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),
903 (u8_t *)&d_obj_id);
904 SPIFFS_CHECK_RES(res);
906 fs->stats_p_deleted++;
907 fs->stats_p_allocated--;
909 // mark deleted in source page
910 u8_t flags = 0xff;
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);
916 #endif
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);
923 return res;
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(
930 spiffs *fs,
931 spiffs_obj_id obj_id,
932 const u8_t name[],
933 const u8_t meta[],
934 spiffs_obj_type type,
935 spiffs_page_ix *objix_hdr_pix) {
936 s32_t res = SPIFFS_OK;
937 spiffs_block_ix bix;
938 spiffs_page_object_ix_header oix_hdr;
939 int entry;
941 res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs));
942 SPIFFS_CHECK_RES(res);
944 obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
946 // find free entry
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);
962 oix_hdr.type = type;
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
966 if (meta) {
967 _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN);
968 } else {
969 memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN);
971 #else
972 (void) meta;
973 #endif
975 // update page
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);
983 if (objix_hdr_pix) {
984 *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
987 return res;
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(
997 spiffs *fs,
998 spiffs_fd *fd,
999 spiffs_obj_id obj_id,
1000 spiffs_page_ix objix_hdr_pix,
1001 u8_t *new_objix_hdr_data,
1002 const u8_t name[],
1003 const u8_t meta[],
1004 u32_t size,
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;
1015 } else {
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);
1025 // change name
1026 if (name) {
1027 strncpy((char *)objix_hdr->name, (const char *)name, SPIFFS_OBJ_NAME_LEN - 1);
1029 #if SPIFFS_OBJ_META_LEN
1030 if (meta) {
1031 _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN);
1033 #else
1034 (void) meta;
1035 #endif
1036 if (size) {
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) {
1044 if (new_pix) {
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
1054 return res;
1056 #endif // !SPIFFS_READ_ONLY
1058 void spiffs_cb_object_event(
1059 spiffs *fs,
1060 spiffs_page_object_ix *objix,
1061 int ev,
1062 spiffs_obj_id obj_id_raw,
1063 spiffs_span_ix spix,
1064 spiffs_page_ix new_pix,
1065 u32_t new_size) {
1066 #if SPIFFS_IX_MAP == 0
1067 (void)objix;
1068 #endif
1069 // update index caches in all file descriptors
1070 spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG;
1071 u32_t i;
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
1080 #endif
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
1085 #endif
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;
1093 #if SPIFFS_CACHE_WR
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);
1097 #endif
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;
1104 #if SPIFFS_CACHE_WR
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);
1109 #endif
1111 } else {
1112 // removing file
1113 #if SPIFFS_CACHE_WR
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);
1118 #endif
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;
1128 } else {
1129 cur_fd->cursor_objix_pix = 0;
1132 } // fd update loop
1134 #if SPIFFS_IX_MAP
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);
1149 #endif
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;
1162 } else {
1163 SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev);
1164 return; // bail out
1166 fs->file_cb_f(fs, op, obj_id, new_pix);
1170 // Open object by id
1171 s32_t spiffs_object_open_by_id(
1172 spiffs *fs,
1173 spiffs_obj_id obj_id,
1174 spiffs_fd *fd,
1175 spiffs_flags flags,
1176 spiffs_mode mode) {
1177 s32_t res = SPIFFS_OK;
1178 spiffs_page_ix pix;
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);
1185 return res;
1188 // Open object by page index
1189 s32_t spiffs_object_open_by_page(
1190 spiffs *fs,
1191 spiffs_page_ix pix,
1192 spiffs_fd *fd,
1193 spiffs_flags flags,
1194 spiffs_mode mode) {
1195 (void)mode;
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);
1210 fd->fs = fs;
1211 fd->objix_hdr_pix = pix;
1212 fd->size = oix_hdr.size;
1213 fd->offset = 0;
1214 fd->cursor_objix_pix = pix;
1215 fd->cursor_objix_spix = 0;
1216 fd->obj_id = obj_id;
1217 fd->flags = flags;
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);
1223 return res;
1226 #if !SPIFFS_READ_ONLY
1227 // Append to object
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;
1232 u32_t written = 0;
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");
1238 offset = fd->size;
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);
1260 // write all data
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
1269 if (written > 0) {
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;
1276 if (offset == 0) {
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);
1283 } else {
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);
1291 } else {
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);
1320 } else {
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);
1337 } else {
1338 // on first pass, we load existing object index page
1339 spiffs_page_ix pix;
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;
1343 } else {
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;
1362 // write data
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);
1373 } else {
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];
1378 } else {
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;
1401 } else {
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));
1408 // update internals
1409 page_offs = 0;
1410 data_spix++;
1411 written += to_write;
1412 } // while all data
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);
1442 } else {
1443 // wrote within object index header page
1444 if (offset == 0) {
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);
1459 } else {
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);
1469 return res;
1470 } // spiffs_object_append
1471 #endif // !SPIFFS_READ_ONLY
1473 #if !SPIFFS_READ_ONLY
1474 // Modify object
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;
1479 u32_t written = 0;
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);
1498 // write all data
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
1507 if (written > 0) {
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);
1515 } else {
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);
1538 } else {
1539 // load existing object index page on first pass
1540 spiffs_page_ix pix;
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;
1544 } else {
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];
1567 } else {
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;
1574 p_hdr.flags = 0xff;
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;
1581 } else {
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),
1597 page_offs);
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,
1610 fd->file_nbr,
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,
1615 fd->file_nbr,
1616 SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags),
1617 sizeof(u8_t),
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);
1632 } else {
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));
1638 // update internals
1639 page_offs = 0;
1640 data_spix++;
1641 written += to_write;
1642 } // while all data
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);
1667 } else {
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);
1675 return res;
1676 } // spiffs_object_modify
1677 #endif // !SPIFFS_READ_ONLY
1679 static s32_t spiffs_object_find_object_index_header_by_name_v(
1680 spiffs *fs,
1681 spiffs_obj_id obj_id,
1682 spiffs_block_ix bix,
1683 int ix_entry,
1684 const void *user_const_p,
1685 void *user_var_p) {
1686 (void)user_var_p;
1687 s32_t res;
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) {
1701 return SPIFFS_OK;
1705 return SPIFFS_VIS_COUNTINUE;
1708 // Finds object index header page by name
1709 s32_t spiffs_object_find_object_index_header_by_name(
1710 spiffs *fs,
1711 const u8_t name[SPIFFS_OBJ_NAME_LEN],
1712 spiffs_page_ix *pix) {
1713 s32_t res;
1714 spiffs_block_ix bix;
1715 int entry;
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,
1723 name,
1725 &bix,
1726 &entry);
1728 if (res == SPIFFS_VIS_END) {
1729 res = SPIFFS_ERR_NOT_FOUND;
1731 SPIFFS_CHECK_RES(res);
1733 if (pix) {
1734 *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
1737 fs->cursor_block_ix = bix;
1738 fs->cursor_obj_lu_entry = entry;
1740 return res;
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(
1746 spiffs_fd *fd,
1747 u32_t new_size,
1748 u8_t remove_full) {
1749 s32_t res = SPIFFS_OK;
1750 spiffs *fs = fd->fs;
1752 if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) {
1753 // no op
1754 return res;
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),
1778 sizeof(u8_t),
1779 (u8_t *)&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;
1819 } else {
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;
1840 } else {
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);
1853 break;
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);
1860 break;
1862 } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) {
1863 res = SPIFFS_OK;
1866 // update current size
1867 if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) {
1868 cur_size -= SPIFFS_DATA_PAGE_SIZE(fs);
1869 } else {
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);
1875 } else {
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;
1887 p_hdr.flags = 0xff;
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,
1902 fd->file_nbr,
1903 SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags),
1904 sizeof(u8_t),
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));
1913 } else {
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;
1921 break;
1923 data_spix--;
1924 } // while all data
1926 // update object indices
1927 if (cur_objix_spix == 0) {
1928 // update object index header page
1929 if (cur_size == 0) {
1930 if (remove_full) {
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);
1941 } else {
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);
1950 } else {
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);
1957 } else {
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;
1980 return res;
1981 } // spiffs_object_truncate
1982 #endif // !SPIFFS_READ_ONLY
1984 s32_t spiffs_object_read(
1985 spiffs_fd *fd,
1986 u32_t offset,
1987 u32_t len,
1988 u8_t *dst) {
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) {
2001 #if SPIFFS_IX_MAP
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];
2007 } else {
2008 #endif
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;
2014 } else {
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;
2018 } else {
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];
2039 } else {
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)];
2043 #if SPIFFS_IX_MAP
2045 #endif
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;
2056 break;
2058 res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
2059 SPIFFS_CHECK_RES(res);
2060 res = _spiffs_rd(
2061 fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
2062 fd->file_nbr,
2063 SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)),
2064 len_to_read,
2065 dst);
2066 SPIFFS_CHECK_RES(res);
2067 dst += len_to_read;
2068 cur_offset += len_to_read;
2069 fd->offset = cur_offset;
2070 data_spix++;
2073 return res;
2076 #if !SPIFFS_READ_ONLY
2077 typedef struct {
2078 spiffs_obj_id min_obj_id;
2079 spiffs_obj_id max_obj_id;
2080 u32_t compaction;
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);
2093 int res;
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) {
2119 (void)user_var_p;
2120 if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) {
2121 s32_t res;
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);
2140 map[ix]++;
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
2166 u32_t i, j;
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];
2177 if (mask == 0xff) {
2178 continue;
2180 for (j = 0; j < 8; j++) {
2181 if ((mask & (1 << j)) == 0) {
2182 *obj_id = (i << 3) + j + state.min_obj_id;
2183 return SPIFFS_OK;
2187 return SPIFFS_ERR_FULL;
2188 } else {
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
2192 u32_t i, min_i = 0;
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) {
2198 min_count = map[i];
2199 min_i = i;
2200 if (min_count == 0) {
2201 break;
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;
2217 return SPIFFS_OK;
2218 } else {
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
2226 continue;
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
2242 return res;
2244 #endif // !SPIFFS_READ_ONLY
2246 #if SPIFFS_TEMPORAL_FD_CACHE
2247 // djb2 hash
2248 static u32_t spiffs_hash(spiffs *fs, const u8_t *name) {
2249 (void)fs;
2250 u32_t hash = 5381;
2251 u8_t c;
2252 int i = 0;
2253 while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) {
2254 hash = (hash * 33) ^ c;
2256 return hash;
2258 #endif
2260 s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) {
2261 #if SPIFFS_TEMPORAL_FD_CACHE
2262 u32_t i;
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;
2268 if (name) {
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
2274 cur_fd->score--;
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) {
2285 cand_ix = i;
2286 break;
2288 if (cur_fd->score < min_score) {
2289 min_score = cur_fd->score;
2290 cand_ix = i;
2295 if (cand_ix != (u32_t) - 1) {
2296 spiffs_fd *cur_fd = &fds[cand_ix];
2297 if (name) {
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);
2304 // update score
2305 if (cur_fd->score < 0xffff - SPIFFS_TEMPORAL_CACHE_HIT_SCORE) {
2306 cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE;
2307 } else {
2308 cur_fd->score = 0xffff;
2310 } else {
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;
2317 *fd = cur_fd;
2318 return SPIFFS_OK;
2319 } else {
2320 return SPIFFS_ERR_OUT_OF_FILE_DESCS;
2322 #else
2323 (void)name;
2324 u32_t i;
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;
2330 *fd = cur_fd;
2331 return SPIFFS_OK;
2334 return SPIFFS_ERR_OUT_OF_FILE_DESCS;
2335 #endif
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;
2347 fd->file_nbr = 0;
2348 #if SPIFFS_IX_MAP
2349 fd->ix_map = 0;
2350 #endif
2351 return SPIFFS_OK;
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;
2359 *fd = &fds[f - 1];
2360 if ((*fd)->file_nbr == 0) {
2361 return SPIFFS_ERR_FILE_CLOSED;
2363 return SPIFFS_OK;
2366 #if SPIFFS_TEMPORAL_FD_CACHE
2367 void spiffs_fd_temporal_cache_rehash(
2368 spiffs *fs,
2369 const char *old_path,
2370 const char *new_path) {
2371 u32_t i;
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;
2382 #endif