1 /*
2  * Emulating block device, wraps filebd and rambd while providing a bunch
3  * of hooks for testing littlefs in various conditions.
4  *
5  * Copyright (c) 2022, The littlefs authors.
6  * Copyright (c) 2017, Arm Limited. All rights reserved.
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 
10 #ifndef _POSIX_C_SOURCE
11 #define _POSIX_C_SOURCE 199309L
12 #endif
13 
14 #include "bd/lfs_emubd.h"
15 
16 #include <stdlib.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <time.h>
21 
22 #ifdef _WIN32
23 #include <windows.h>
24 #endif
25 
26 
27 // access to lazily-allocated/copy-on-write blocks
28 //
29 // Note we can only modify a block if we have exclusive access to it (rc == 1)
30 //
31 
lfs_emubd_incblock(lfs_emubd_block_t * block)32 static lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) {
33     if (block) {
34         block->rc += 1;
35     }
36     return block;
37 }
38 
lfs_emubd_decblock(lfs_emubd_block_t * block)39 static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
40     if (block) {
41         block->rc -= 1;
42         if (block->rc == 0) {
43             free(block);
44         }
45     }
46 }
47 
lfs_emubd_mutblock(const struct lfs_config * cfg,lfs_emubd_block_t ** block)48 static lfs_emubd_block_t *lfs_emubd_mutblock(
49         const struct lfs_config *cfg,
50         lfs_emubd_block_t **block) {
51     lfs_emubd_t *bd = cfg->context;
52     lfs_emubd_block_t *block_ = *block;
53     if (block_ && block_->rc == 1) {
54         // rc == 1? can modify
55         return block_;
56 
57     } else if (block_) {
58         // rc > 1? need to create a copy
59         lfs_emubd_block_t *nblock = malloc(
60                 sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
61         if (!nblock) {
62             return NULL;
63         }
64 
65         memcpy(nblock, block_,
66                 sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
67         nblock->rc = 1;
68 
69         lfs_emubd_decblock(block_);
70         *block = nblock;
71         return nblock;
72 
73     } else {
74         // no block? need to allocate
75         lfs_emubd_block_t *nblock = malloc(
76                 sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
77         if (!nblock) {
78             return NULL;
79         }
80 
81         nblock->rc = 1;
82         nblock->wear = 0;
83 
84         // zero for consistency
85         memset(nblock->data,
86                 (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
87                 bd->cfg->erase_size);
88 
89         *block = nblock;
90         return nblock;
91     }
92 }
93 
94 
95 // emubd create/destroy
96 
lfs_emubd_create(const struct lfs_config * cfg,const struct lfs_emubd_config * bdcfg)97 int lfs_emubd_create(const struct lfs_config *cfg,
98         const struct lfs_emubd_config *bdcfg) {
99     LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
100                 ".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
101                 "%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
102                 ".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
103                 ".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
104                 ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
105                 ".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
106                 ".powerloss_data=%p, .track_branches=%d})",
107             (void*)cfg, cfg->context,
108             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
109             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
110             (void*)bdcfg,
111             bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
112             bdcfg->erase_count, bdcfg->erase_value, bdcfg->erase_cycles,
113             bdcfg->badblock_behavior, bdcfg->power_cycles,
114             bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
115             bdcfg->powerloss_data, bdcfg->track_branches);
116     lfs_emubd_t *bd = cfg->context;
117     bd->cfg = bdcfg;
118 
119     // allocate our block array, all blocks start as uninitialized
120     bd->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
121     if (!bd->blocks) {
122         LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
123         return LFS_ERR_NOMEM;
124     }
125     memset(bd->blocks, 0, bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
126 
127     // setup testing things
128     bd->readed = 0;
129     bd->proged = 0;
130     bd->erased = 0;
131     bd->power_cycles = bd->cfg->power_cycles;
132     bd->ooo_block = -1;
133     bd->ooo_data = NULL;
134     bd->disk = NULL;
135 
136     if (bd->cfg->disk_path) {
137         bd->disk = malloc(sizeof(lfs_emubd_disk_t));
138         if (!bd->disk) {
139             LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
140             return LFS_ERR_NOMEM;
141         }
142         bd->disk->rc = 1;
143         bd->disk->scratch = NULL;
144 
145         #ifdef _WIN32
146         bd->disk->fd = open(bd->cfg->disk_path,
147                 O_RDWR | O_CREAT | O_BINARY, 0666);
148         #else
149         bd->disk->fd = open(bd->cfg->disk_path,
150                 O_RDWR | O_CREAT, 0666);
151         #endif
152         if (bd->disk->fd < 0) {
153             int err = -errno;
154             LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
155             return err;
156         }
157 
158         // if we're emulating erase values, we can keep a block around in
159         // memory of just the erase state to speed up emulated erases
160         if (bd->cfg->erase_value != -1) {
161             bd->disk->scratch = malloc(bd->cfg->erase_size);
162             if (!bd->disk->scratch) {
163                 LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
164                 return LFS_ERR_NOMEM;
165             }
166             memset(bd->disk->scratch,
167                     bd->cfg->erase_value,
168                     bd->cfg->erase_size);
169 
170             // go ahead and erase all of the disk, otherwise the file will not
171             // match our internal representation
172             for (size_t i = 0; i < bd->cfg->erase_count; i++) {
173                 ssize_t res = write(bd->disk->fd,
174                         bd->disk->scratch,
175                         bd->cfg->erase_size);
176                 if (res < 0) {
177                     int err = -errno;
178                     LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
179                     return err;
180                 }
181             }
182         }
183     }
184 
185     LFS_EMUBD_TRACE("lfs_emubd_create -> %d", 0);
186     return 0;
187 }
188 
lfs_emubd_destroy(const struct lfs_config * cfg)189 int lfs_emubd_destroy(const struct lfs_config *cfg) {
190     LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
191     lfs_emubd_t *bd = cfg->context;
192 
193     // decrement reference counts
194     for (lfs_block_t i = 0; i < bd->cfg->erase_count; i++) {
195         lfs_emubd_decblock(bd->blocks[i]);
196     }
197     free(bd->blocks);
198 
199     // clean up other resources
200     lfs_emubd_decblock(bd->ooo_data);
201     if (bd->disk) {
202         bd->disk->rc -= 1;
203         if (bd->disk->rc == 0) {
204             close(bd->disk->fd);
205             free(bd->disk->scratch);
206             free(bd->disk);
207         }
208     }
209 
210     LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
211     return 0;
212 }
213 
214 
215 // powerloss hook
lfs_emubd_powerloss(const struct lfs_config * cfg)216 static int lfs_emubd_powerloss(const struct lfs_config *cfg) {
217     lfs_emubd_t *bd = cfg->context;
218 
219     // emulate out-of-order writes?
220     lfs_emubd_block_t *ooo_data = NULL;
221     if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
222             && bd->ooo_block != -1) {
223         // since writes between syncs are allowed to be out-of-order, it
224         // shouldn't hurt to restore the first write on powerloss, right?
225         ooo_data = bd->blocks[bd->ooo_block];
226         bd->blocks[bd->ooo_block] = lfs_emubd_incblock(bd->ooo_data);
227 
228         // mirror to disk file?
229         if (bd->disk
230                 && (bd->blocks[bd->ooo_block]
231                     || bd->cfg->erase_value != -1)) {
232             off_t res1 = lseek(bd->disk->fd,
233                     (off_t)bd->ooo_block*bd->cfg->erase_size,
234                     SEEK_SET);
235             if (res1 < 0) {
236                 return -errno;
237             }
238 
239             ssize_t res2 = write(bd->disk->fd,
240                     (bd->blocks[bd->ooo_block])
241                         ? bd->blocks[bd->ooo_block]->data
242                         : bd->disk->scratch,
243                     bd->cfg->erase_size);
244             if (res2 < 0) {
245                 return -errno;
246             }
247         }
248     }
249 
250     // simulate power loss
251     bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
252 
253     // if we continue, undo out-of-order write emulation
254     if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
255             && bd->ooo_block != -1) {
256         lfs_emubd_decblock(bd->blocks[bd->ooo_block]);
257         bd->blocks[bd->ooo_block] = ooo_data;
258 
259         // mirror to disk file?
260         if (bd->disk
261                 && (bd->blocks[bd->ooo_block]
262                     || bd->cfg->erase_value != -1)) {
263             off_t res1 = lseek(bd->disk->fd,
264                     (off_t)bd->ooo_block*bd->cfg->erase_size,
265                     SEEK_SET);
266             if (res1 < 0) {
267                 return -errno;
268             }
269 
270             ssize_t res2 = write(bd->disk->fd,
271                     (bd->blocks[bd->ooo_block])
272                         ? bd->blocks[bd->ooo_block]->data
273                         : bd->disk->scratch,
274                     bd->cfg->erase_size);
275             if (res2 < 0) {
276                 return -errno;
277             }
278         }
279     }
280 
281     return 0;
282 }
283 
284 
285 // block device API
286 
lfs_emubd_read(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)287 int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
288         lfs_off_t off, void *buffer, lfs_size_t size) {
289     LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
290                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
291             (void*)cfg, block, off, buffer, size);
292     lfs_emubd_t *bd = cfg->context;
293 
294     // check if read is valid
295     LFS_ASSERT(block < bd->cfg->erase_count);
296     LFS_ASSERT(off  % bd->cfg->read_size == 0);
297     LFS_ASSERT(size % bd->cfg->read_size == 0);
298     LFS_ASSERT(off+size <= bd->cfg->erase_size);
299 
300     // get the block
301     const lfs_emubd_block_t *b = bd->blocks[block];
302     if (b) {
303         // block bad?
304         if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
305                 bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
306             LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
307             return LFS_ERR_CORRUPT;
308         }
309 
310         // read data
311         memcpy(buffer, &b->data[off], size);
312     } else {
313         // zero for consistency
314         memset(buffer,
315                 (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
316                 size);
317     }
318 
319     // track reads
320     bd->readed += size;
321     if (bd->cfg->read_sleep) {
322         int err = nanosleep(&(struct timespec){
323                 .tv_sec=bd->cfg->read_sleep/1000000000,
324                 .tv_nsec=bd->cfg->read_sleep%1000000000},
325             NULL);
326         if (err) {
327             err = -errno;
328             LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
329             return err;
330         }
331     }
332 
333     LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
334     return 0;
335 }
336 
lfs_emubd_prog(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)337 int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
338         lfs_off_t off, const void *buffer, lfs_size_t size) {
339     LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
340                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
341             (void*)cfg, block, off, buffer, size);
342     lfs_emubd_t *bd = cfg->context;
343 
344     // check if write is valid
345     LFS_ASSERT(block < bd->cfg->erase_count);
346     LFS_ASSERT(off  % bd->cfg->prog_size == 0);
347     LFS_ASSERT(size % bd->cfg->prog_size == 0);
348     LFS_ASSERT(off+size <= bd->cfg->erase_size);
349 
350     // get the block
351     lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
352     if (!b) {
353         LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
354         return LFS_ERR_NOMEM;
355     }
356 
357     // block bad?
358     if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
359         if (bd->cfg->badblock_behavior ==
360                 LFS_EMUBD_BADBLOCK_PROGERROR) {
361             LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
362             return LFS_ERR_CORRUPT;
363         } else if (bd->cfg->badblock_behavior ==
364                 LFS_EMUBD_BADBLOCK_PROGNOOP ||
365                 bd->cfg->badblock_behavior ==
366                 LFS_EMUBD_BADBLOCK_ERASENOOP) {
367             LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
368             return 0;
369         }
370     }
371 
372     // were we erased properly?
373     if (bd->cfg->erase_value != -1) {
374         for (lfs_off_t i = 0; i < size; i++) {
375             LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value);
376         }
377     }
378 
379     // prog data
380     memcpy(&b->data[off], buffer, size);
381 
382     // mirror to disk file?
383     if (bd->disk) {
384         off_t res1 = lseek(bd->disk->fd,
385                 (off_t)block*bd->cfg->erase_size + (off_t)off,
386                 SEEK_SET);
387         if (res1 < 0) {
388             int err = -errno;
389             LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
390             return err;
391         }
392 
393         ssize_t res2 = write(bd->disk->fd, buffer, size);
394         if (res2 < 0) {
395             int err = -errno;
396             LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
397             return err;
398         }
399     }
400 
401     // track progs
402     bd->proged += size;
403     if (bd->cfg->prog_sleep) {
404         int err = nanosleep(&(struct timespec){
405                 .tv_sec=bd->cfg->prog_sleep/1000000000,
406                 .tv_nsec=bd->cfg->prog_sleep%1000000000},
407             NULL);
408         if (err) {
409             err = -errno;
410             LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
411             return err;
412         }
413     }
414 
415     // lose power?
416     if (bd->power_cycles > 0) {
417         bd->power_cycles -= 1;
418         if (bd->power_cycles == 0) {
419             int err = lfs_emubd_powerloss(cfg);
420             if (err) {
421                 LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
422                 return err;
423             }
424         }
425     }
426 
427     LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
428     return 0;
429 }
430 
lfs_emubd_erase(const struct lfs_config * cfg,lfs_block_t block)431 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
432     LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
433             (void*)cfg, block, ((lfs_emubd_t*)cfg->context)->cfg->erase_size);
434     lfs_emubd_t *bd = cfg->context;
435 
436     // check if erase is valid
437     LFS_ASSERT(block < bd->cfg->erase_count);
438 
439     // emulate out-of-order writes? save first write
440     if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
441             && bd->ooo_block == -1) {
442         bd->ooo_block = block;
443         bd->ooo_data = lfs_emubd_incblock(bd->blocks[block]);
444     }
445 
446     // get the block
447     lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
448     if (!b) {
449         LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_NOMEM);
450         return LFS_ERR_NOMEM;
451     }
452 
453     // block bad?
454     if (bd->cfg->erase_cycles) {
455         if (b->wear >= bd->cfg->erase_cycles) {
456             if (bd->cfg->badblock_behavior ==
457                     LFS_EMUBD_BADBLOCK_ERASEERROR) {
458                 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
459                 return LFS_ERR_CORRUPT;
460             } else if (bd->cfg->badblock_behavior ==
461                     LFS_EMUBD_BADBLOCK_ERASENOOP) {
462                 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
463                 return 0;
464             }
465         } else {
466             // mark wear
467             b->wear += 1;
468         }
469     }
470 
471     // emulate an erase value?
472     if (bd->cfg->erase_value != -1) {
473         memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size);
474 
475         // mirror to disk file?
476         if (bd->disk) {
477             off_t res1 = lseek(bd->disk->fd,
478                     (off_t)block*bd->cfg->erase_size,
479                     SEEK_SET);
480             if (res1 < 0) {
481                 int err = -errno;
482                 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
483                 return err;
484             }
485 
486             ssize_t res2 = write(bd->disk->fd,
487                     bd->disk->scratch,
488                     bd->cfg->erase_size);
489             if (res2 < 0) {
490                 int err = -errno;
491                 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
492                 return err;
493             }
494         }
495     }
496 
497     // track erases
498     bd->erased += bd->cfg->erase_size;
499     if (bd->cfg->erase_sleep) {
500         int err = nanosleep(&(struct timespec){
501                 .tv_sec=bd->cfg->erase_sleep/1000000000,
502                 .tv_nsec=bd->cfg->erase_sleep%1000000000},
503             NULL);
504         if (err) {
505             err = -errno;
506             LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
507             return err;
508         }
509     }
510 
511     // lose power?
512     if (bd->power_cycles > 0) {
513         bd->power_cycles -= 1;
514         if (bd->power_cycles == 0) {
515             int err = lfs_emubd_powerloss(cfg);
516             if (err) {
517                 LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
518                 return err;
519             }
520         }
521     }
522 
523     LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
524     return 0;
525 }
526 
lfs_emubd_sync(const struct lfs_config * cfg)527 int lfs_emubd_sync(const struct lfs_config *cfg) {
528     LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
529     lfs_emubd_t *bd = cfg->context;
530 
531     // emulate out-of-order writes? reset first write, writes
532     // cannot be out-of-order across sync
533     if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) {
534         lfs_emubd_decblock(bd->ooo_data);
535         bd->ooo_block = -1;
536         bd->ooo_data = NULL;
537     }
538 
539     LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
540     return 0;
541 }
542 
543 
544 /// Additional extended API for driving test features ///
545 
lfs_emubd_crc_(const struct lfs_config * cfg,lfs_block_t block,uint32_t * crc)546 static int lfs_emubd_crc_(const struct lfs_config *cfg,
547         lfs_block_t block, uint32_t *crc) {
548     lfs_emubd_t *bd = cfg->context;
549 
550     // check if crc is valid
551     LFS_ASSERT(block < cfg->block_count);
552 
553     // crc the block
554     uint32_t crc_ = 0xffffffff;
555     const lfs_emubd_block_t *b = bd->blocks[block];
556     if (b) {
557         crc_ = lfs_crc(crc_, b->data, cfg->block_size);
558     } else {
559         uint8_t erase_value = (bd->cfg->erase_value != -1)
560                 ? bd->cfg->erase_value
561                 : 0;
562         for (lfs_size_t i = 0; i < cfg->block_size; i++) {
563             crc_ = lfs_crc(crc_, &erase_value, 1);
564         }
565     }
566     *crc = 0xffffffff ^ crc_;
567 
568     return 0;
569 }
570 
lfs_emubd_crc(const struct lfs_config * cfg,lfs_block_t block,uint32_t * crc)571 int lfs_emubd_crc(const struct lfs_config *cfg,
572         lfs_block_t block, uint32_t *crc) {
573     LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)",
574             (void*)cfg, block, crc);
575     int err = lfs_emubd_crc_(cfg, block, crc);
576     LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err);
577     return err;
578 }
579 
lfs_emubd_bdcrc(const struct lfs_config * cfg,uint32_t * crc)580 int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) {
581     LFS_EMUBD_TRACE("lfs_emubd_bdcrc(%p, %p)", (void*)cfg, crc);
582 
583     uint32_t crc_ = 0xffffffff;
584     for (lfs_block_t i = 0; i < cfg->block_count; i++) {
585         uint32_t i_crc;
586         int err = lfs_emubd_crc_(cfg, i, &i_crc);
587         if (err) {
588             LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err);
589             return err;
590         }
591 
592         crc_ = lfs_crc(crc_, &i_crc, sizeof(uint32_t));
593     }
594     *crc = 0xffffffff ^ crc_;
595 
596     LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", 0);
597     return 0;
598 }
599 
lfs_emubd_readed(const struct lfs_config * cfg)600 lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) {
601     LFS_EMUBD_TRACE("lfs_emubd_readed(%p)", (void*)cfg);
602     lfs_emubd_t *bd = cfg->context;
603     LFS_EMUBD_TRACE("lfs_emubd_readed -> %"PRIu64, bd->readed);
604     return bd->readed;
605 }
606 
lfs_emubd_proged(const struct lfs_config * cfg)607 lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg) {
608     LFS_EMUBD_TRACE("lfs_emubd_proged(%p)", (void*)cfg);
609     lfs_emubd_t *bd = cfg->context;
610     LFS_EMUBD_TRACE("lfs_emubd_proged -> %"PRIu64, bd->proged);
611     return bd->proged;
612 }
613 
lfs_emubd_erased(const struct lfs_config * cfg)614 lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg) {
615     LFS_EMUBD_TRACE("lfs_emubd_erased(%p)", (void*)cfg);
616     lfs_emubd_t *bd = cfg->context;
617     LFS_EMUBD_TRACE("lfs_emubd_erased -> %"PRIu64, bd->erased);
618     return bd->erased;
619 }
620 
lfs_emubd_setreaded(const struct lfs_config * cfg,lfs_emubd_io_t readed)621 int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed) {
622     LFS_EMUBD_TRACE("lfs_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed);
623     lfs_emubd_t *bd = cfg->context;
624     bd->readed = readed;
625     LFS_EMUBD_TRACE("lfs_emubd_setreaded -> %d", 0);
626     return 0;
627 }
628 
lfs_emubd_setproged(const struct lfs_config * cfg,lfs_emubd_io_t proged)629 int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged) {
630     LFS_EMUBD_TRACE("lfs_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged);
631     lfs_emubd_t *bd = cfg->context;
632     bd->proged = proged;
633     LFS_EMUBD_TRACE("lfs_emubd_setproged -> %d", 0);
634     return 0;
635 }
636 
lfs_emubd_seterased(const struct lfs_config * cfg,lfs_emubd_io_t erased)637 int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
638     LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
639     lfs_emubd_t *bd = cfg->context;
640     bd->erased = erased;
641     LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
642     return 0;
643 }
644 
lfs_emubd_wear(const struct lfs_config * cfg,lfs_block_t block)645 lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
646         lfs_block_t block) {
647     LFS_EMUBD_TRACE("lfs_emubd_wear(%p, %"PRIu32")", (void*)cfg, block);
648     lfs_emubd_t *bd = cfg->context;
649 
650     // check if block is valid
651     LFS_ASSERT(block < bd->cfg->erase_count);
652 
653     // get the wear
654     lfs_emubd_wear_t wear;
655     const lfs_emubd_block_t *b = bd->blocks[block];
656     if (b) {
657         wear = b->wear;
658     } else {
659         wear = 0;
660     }
661 
662     LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIi32, wear);
663     return wear;
664 }
665 
lfs_emubd_setwear(const struct lfs_config * cfg,lfs_block_t block,lfs_emubd_wear_t wear)666 int lfs_emubd_setwear(const struct lfs_config *cfg,
667         lfs_block_t block, lfs_emubd_wear_t wear) {
668     LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32", %"PRIi32")",
669             (void*)cfg, block, wear);
670     lfs_emubd_t *bd = cfg->context;
671 
672     // check if block is valid
673     LFS_ASSERT(block < bd->cfg->erase_count);
674 
675     // set the wear
676     lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
677     if (!b) {
678         LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
679         return LFS_ERR_NOMEM;
680     }
681     b->wear = wear;
682 
683     LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0);
684     return 0;
685 }
686 
lfs_emubd_powercycles(const struct lfs_config * cfg)687 lfs_emubd_spowercycles_t lfs_emubd_powercycles(
688         const struct lfs_config *cfg) {
689     LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg);
690     lfs_emubd_t *bd = cfg->context;
691 
692     LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %"PRIi32, bd->power_cycles);
693     return bd->power_cycles;
694 }
695 
lfs_emubd_setpowercycles(const struct lfs_config * cfg,lfs_emubd_powercycles_t power_cycles)696 int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
697         lfs_emubd_powercycles_t power_cycles) {
698     LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
699             (void*)cfg, power_cycles);
700     lfs_emubd_t *bd = cfg->context;
701 
702     bd->power_cycles = power_cycles;
703 
704     LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %d", 0);
705     return 0;
706 }
707 
lfs_emubd_copy(const struct lfs_config * cfg,lfs_emubd_t * copy)708 int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
709     LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
710     lfs_emubd_t *bd = cfg->context;
711 
712     // lazily copy over our block array
713     copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
714     if (!copy->blocks) {
715         LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
716         return LFS_ERR_NOMEM;
717     }
718 
719     for (size_t i = 0; i < bd->cfg->erase_count; i++) {
720         copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
721     }
722 
723     // other state
724     copy->readed = bd->readed;
725     copy->proged = bd->proged;
726     copy->erased = bd->erased;
727     copy->power_cycles = bd->power_cycles;
728     copy->ooo_block = bd->ooo_block;
729     copy->ooo_data = lfs_emubd_incblock(bd->ooo_data);
730     copy->disk = bd->disk;
731     if (copy->disk) {
732         copy->disk->rc += 1;
733     }
734     copy->cfg = bd->cfg;
735 
736     LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
737     return 0;
738 }
739 
740