Skip to content

Commit 692f0c5

Browse files
committed
Naive implementation of resizable entries
Now, with the off, diff, and len parameters in each commit entry, we can build up directory commits that resize entries. This adds complexity but opens up the directory blocks to be much more flexible. The main concern is that resizing entries can push around neighboring entries in surprising ways, such as pushing them into new directory blocks when a directory splits. This can break littlefs's internal logic in how it tracks in-flight entries. The most problematic example being open files. Fortunately, this is helped by a global linked-list of all files and directories opened by the filesystem. As entries change size, the state of open files/dirs may be updated as needed. Note this already needed to exist for the ability to remove files/dirs, which has the same issue.
1 parent e3daee2 commit 692f0c5

File tree

1 file changed

+131
-94
lines changed

1 file changed

+131
-94
lines changed

lfs.c

Lines changed: 131 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,8 @@ struct lfs_region {
489489
LFS_FROM_DISK,
490490
} source;
491491

492-
lfs_off_t oldoff;
493-
lfs_size_t oldlen;
492+
lfs_off_t off;
493+
lfs_ssize_t diff;
494494
union {
495495
struct {
496496
const void *data;
@@ -500,20 +500,20 @@ struct lfs_region {
500500
lfs_off_t off;
501501
} d;
502502
} u;
503-
lfs_size_t newlen;
503+
lfs_size_t len;
504504

505505
struct lfs_region *next;
506506
};
507507

508508
static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
509-
struct lfs_region *region) {
509+
struct lfs_region *regions) {
510510
// increment revision count
511511
dir->d.rev += 1;
512512

513513
// keep pairs in order such that pair[0] is most recent
514514
lfs_pairswap(dir->pair);
515-
for (struct lfs_region *r = region; r; r = r->next) {
516-
dir->d.size += r->newlen - r->oldlen;
515+
for (struct lfs_region *r = regions; r; r = r->next) {
516+
dir->d.size += r->diff;
517517
}
518518

519519
const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]};
@@ -541,19 +541,19 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
541541
return err;
542542
}
543543

544-
struct lfs_region *r = region;
544+
struct lfs_region *r = regions;
545545
int j = 0;
546546
lfs_off_t oldoff = sizeof(dir->d);
547547
lfs_off_t newoff = sizeof(dir->d);
548548
while (newoff < (0x7fffffff & dir->d.size)-4) {
549-
while (r && r->oldoff == oldoff && r->newlen == j) {
550-
oldoff += r->oldlen;
549+
while (r && r->off == oldoff && r->len == j) {
550+
oldoff += r->len - r->diff;
551551
r = r->next;
552552
j = 0;
553553
}
554554

555555
uint8_t data;
556-
if (r && r->oldoff == oldoff) {
556+
if (r && r->off == oldoff) {
557557
if (r->source == LFS_FROM_DISK) {
558558
err = lfs_bd_read(lfs, r->u.d.block,
559559
r->u.d.off + j, &data, 1);
@@ -660,37 +660,21 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
660660
return 0;
661661
}
662662

663-
static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
664-
lfs_entry_t *entry, const void *data) {
665-
lfs_entry_tole32(&entry->d);
666-
int err = lfs_dir_commit(lfs, dir,
667-
&(struct lfs_region){
668-
LFS_FROM_MEM, entry->off, sizeof(entry->d),
669-
{.m.data = &entry->d}, sizeof(entry->d),
670-
data ?
671-
&(struct lfs_region){
672-
LFS_FROM_MEM, entry->off+sizeof(entry->d), entry->d.nlen,
673-
{.m.data = data}, entry->d.nlen}
674-
: NULL});
675-
lfs_entry_fromle32(&entry->d);
676-
return err;
677-
}
678-
679-
static int lfs_dir_append_(lfs_t *lfs, lfs_dir_t *dir,
680-
lfs_entry_t *entry, struct lfs_region *region) {
663+
static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
664+
lfs_entry_t *entry, struct lfs_region *regions) {
681665
// check if we fit, if top bit is set we do not and move on
682666
while (true) {
683667
if (dir->d.size + lfs_entry_size(entry) <= lfs->cfg->block_size) {
684668
entry->off = dir->d.size - 4;
685-
for (struct lfs_region *r = region; r; r = r->next) {
686-
r->oldoff += entry->off;
669+
for (struct lfs_region *r = regions; r; r = r->next) {
670+
r->off += entry->off;
687671
}
688672

689673
lfs_entry_tole32(&entry->d);
690674
int err = lfs_dir_commit(lfs, dir,
691675
&(struct lfs_region){
692-
LFS_FROM_MEM, entry->off, 0,
693-
{.m.data = &entry->d}, 4, region});
676+
LFS_FROM_MEM, entry->off, +4,
677+
{.m.data = &entry->d}, 4, regions});
694678
lfs_entry_fromle32(&entry->d);
695679
return err;
696680
}
@@ -706,11 +690,15 @@ static int lfs_dir_append_(lfs_t *lfs, lfs_dir_t *dir,
706690
dir->d.tail[0] = olddir.d.tail[0];
707691
dir->d.tail[1] = olddir.d.tail[1];
708692
entry->off = dir->d.size - 4;
693+
for (struct lfs_region *r = regions; r; r = r->next) {
694+
r->off += entry->off;
695+
}
696+
709697
lfs_entry_tole32(&entry->d);
710698
err = lfs_dir_commit(lfs, dir,
711699
&(struct lfs_region){
712-
LFS_FROM_MEM, entry->off, 0,
713-
{.m.data = &entry->d}, 4, region});
700+
LFS_FROM_MEM, entry->off, +4,
701+
{.m.data = &entry->d}, 4, regions});
714702
lfs_entry_fromle32(&entry->d);
715703
if (err) {
716704
return err;
@@ -729,60 +717,95 @@ static int lfs_dir_append_(lfs_t *lfs, lfs_dir_t *dir,
729717
}
730718
}
731719

732-
static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
733-
lfs_entry_t *entry, const void *data) {
734-
// check if we fit, if top bit is set we do not and move on
735-
while (true) {
736-
if (dir->d.size + lfs_entry_size(entry) <= lfs->cfg->block_size) {
737-
entry->off = dir->d.size - 4;
720+
static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
721+
lfs_entry_t *entry, struct lfs_region *regions) {
722+
lfs_off_t oldoff = entry->off;
723+
lfs_ssize_t diff = 0;
724+
lfs_size_t len = 0;
725+
struct lfs_region **tail = &regions;
726+
for (struct lfs_region *r = regions; r; r = r->next) {
727+
diff += r->diff;
728+
len += r->len;
729+
tail = &r->next;
730+
}
738731

739-
lfs_entry_tole32(&entry->d);
740-
int err = lfs_dir_commit(lfs, dir,
741-
&(struct lfs_region){
742-
LFS_FROM_MEM, entry->off, 0,
743-
{.m.data = &entry->d}, sizeof(entry->d),
744-
&(struct lfs_region){
745-
LFS_FROM_MEM, entry->off, 0,
746-
{.m.data = data}, entry->d.nlen}});
747-
lfs_entry_fromle32(&entry->d);
748-
return err;
732+
// do we still fit?
733+
if ((0x7fffffff & dir->d.size) + diff <= lfs->cfg->block_size) {
734+
for (struct lfs_region *r = regions; r; r = r->next) {
735+
r->off += entry->off;
749736
}
750737

751-
// we need to allocate a new dir block
752-
if (!(0x80000000 & dir->d.size)) {
753-
lfs_dir_t olddir = *dir;
754-
int err = lfs_dir_alloc(lfs, dir);
755-
if (err) {
756-
return err;
757-
}
738+
lfs_entry_tole32(&entry->d);
739+
int err = lfs_dir_commit(lfs, dir,
740+
&(struct lfs_region){
741+
LFS_FROM_MEM, entry->off, 0,
742+
{.m.data = &entry->d}, sizeof(entry->d), regions});
743+
lfs_entry_fromle32(&entry->d);
744+
if (err) {
745+
return err;
746+
}
747+
} else {
748+
lfs_dir_t olddir = *dir;
758749

759-
dir->d.tail[0] = olddir.d.tail[0];
760-
dir->d.tail[1] = olddir.d.tail[1];
761-
entry->off = dir->d.size - 4;
762-
lfs_entry_tole32(&entry->d);
763-
err = lfs_dir_commit(lfs, dir,
764-
&(struct lfs_region){
765-
LFS_FROM_MEM, entry->off, 0,
766-
{.m.data = &entry->d}, sizeof(entry->d),
767-
&(struct lfs_region){
768-
LFS_FROM_MEM, entry->off, 0,
769-
{.m.data = data}, entry->d.nlen}});
770-
lfs_entry_fromle32(&entry->d);
771-
if (err) {
772-
return err;
773-
}
750+
// mark as moving
751+
entry->d.type |= LFS_STRUCT_MOVED;
752+
int err = lfs_dir_commit(lfs, &olddir,
753+
&(struct lfs_region){
754+
LFS_FROM_MEM, oldoff, 0,
755+
{.m.data = &entry->d.type}, 1});
756+
if (err) {
757+
return err;
758+
}
774759

775-
olddir.d.size |= 0x80000000;
776-
olddir.d.tail[0] = dir->pair[0];
777-
olddir.d.tail[1] = dir->pair[1];
778-
return lfs_dir_commit(lfs, &olddir, NULL);
760+
// append updated entry
761+
entry->d.type &= LFS_STRUCT_MOVED;
762+
lfs_size_t prefix = regions->off;
763+
lfs_size_t suffix = lfs_entry_size(entry) - len - 4;
764+
for (struct lfs_region *r = regions; r; r = r->next) {
765+
r->off = 0;
766+
r->diff = r->len;
779767
}
780768

781-
int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
769+
regions = &(struct lfs_region){
770+
LFS_FROM_DISK, 0, +prefix,
771+
{.d.block = olddir.pair[0], .d.off = entry->off},
772+
prefix, regions};
773+
*tail = &(struct lfs_region){
774+
LFS_FROM_DISK, 0, +suffix,
775+
{.d.block = olddir.pair[0], .d.off = entry->off+prefix+len},
776+
suffix};
777+
778+
// remove old entry
779+
err = lfs_dir_commit(lfs, &olddir,
780+
&(struct lfs_region){
781+
LFS_FROM_MEM, oldoff, -(lfs_entry_size(entry) - diff),
782+
{.m.data = NULL}, 0});
782783
if (err) {
783784
return err;
784785
}
785786
}
787+
788+
// shift over any files/directories that are affected
789+
for (lfs_file_t *f = lfs->files; f; f = f->next) {
790+
if (lfs_paircmp(f->pair, dir->pair) == 0) {
791+
if (f->poff == oldoff) {
792+
f->poff = entry->off;
793+
} else if (f->poff > entry->off) {
794+
f->poff += diff;
795+
}
796+
}
797+
}
798+
799+
for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
800+
if (lfs_paircmp(d->pair, dir->pair) == 0) {
801+
if (d->off > entry->off) {
802+
d->off += diff;
803+
d->pos += diff;
804+
}
805+
}
806+
}
807+
808+
return 0;
786809
}
787810

788811
static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
@@ -805,7 +828,7 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
805828

806829
// shift out the entry
807830
int err = lfs_dir_commit(lfs, dir, &(struct lfs_region){
808-
LFS_FROM_MEM, entry->off, lfs_entry_size(entry),
831+
LFS_FROM_MEM, entry->off, -lfs_entry_size(entry),
809832
{.m.data = NULL}, 0});
810833
if (err) {
811834
return err;
@@ -1026,12 +1049,12 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
10261049
cwd.d.tail[0] = dir.pair[0];
10271050
cwd.d.tail[1] = dir.pair[1];
10281051

1029-
err = lfs_dir_append_(lfs, &cwd, &entry,
1052+
err = lfs_dir_append(lfs, &cwd, &entry,
10301053
&(struct lfs_region){
1031-
LFS_FROM_MEM, 0, 0,
1032-
{.m.data = (uint8_t*)&entry.d + 4}, sizeof(entry.d) - 4,
1054+
LFS_FROM_MEM, 0, +(sizeof(entry.d)-4),
1055+
{.m.data = (uint8_t*)&entry.d+4}, sizeof(entry.d)-4,
10331056
&(struct lfs_region){
1034-
LFS_FROM_MEM, 0, 0,
1057+
LFS_FROM_MEM, 0, +entry.d.nlen,
10351058
{.m.data = path}, entry.d.nlen}});
10361059
if (err) {
10371060
return err;
@@ -1416,12 +1439,12 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
14161439
entry.d.nlen = strlen(path);
14171440
entry.d.u.file.head = 0xffffffff;
14181441
entry.d.u.file.size = 0;
1419-
err = lfs_dir_append_(lfs, &cwd, &entry,
1442+
err = lfs_dir_append(lfs, &cwd, &entry,
14201443
&(struct lfs_region){
1421-
LFS_FROM_MEM, 0, 0,
1422-
{.m.data = (uint8_t*)&entry.d + 4}, sizeof(entry.d) - 4,
1444+
LFS_FROM_MEM, 0, +(sizeof(entry.d)-4),
1445+
{.m.data = (uint8_t*)&entry.d+4}, sizeof(entry.d)-4,
14231446
&(struct lfs_region){
1424-
LFS_FROM_MEM, 0, 0,
1447+
LFS_FROM_MEM, 0, +entry.d.nlen,
14251448
{.m.data = path}, entry.d.nlen}});
14261449
if (err) {
14271450
return err;
@@ -1638,7 +1661,10 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
16381661
entry.d.u.file.head = file->head;
16391662
entry.d.u.file.size = file->size;
16401663

1641-
err = lfs_dir_update(lfs, &cwd, &entry, NULL);
1664+
err = lfs_dir_update(lfs, &cwd, &entry,
1665+
&(struct lfs_region){
1666+
LFS_FROM_MEM, 0, 0,
1667+
{.m.data = (const uint8_t *)&entry.d+4}, sizeof(entry.d)-4});
16421668
if (err) {
16431669
return err;
16441670
}
@@ -2077,17 +2103,25 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
20772103
newentry.d.nlen = strlen(newpath);
20782104

20792105
if (prevexists) {
2080-
err = lfs_dir_update(lfs, &newcwd, &newentry, newpath);
2106+
err = lfs_dir_update(lfs, &newcwd, &newentry,
2107+
&(struct lfs_region){
2108+
LFS_FROM_MEM, 0, 0,
2109+
{.m.data = (const uint8_t*)&newentry.d+4},
2110+
sizeof(newentry.d)-4,
2111+
&(struct lfs_region){
2112+
LFS_FROM_MEM, sizeof(newentry.d)-4, 0,
2113+
{.m.data = newpath}, newentry.d.nlen}});
20812114
if (err) {
20822115
return err;
20832116
}
20842117
} else {
2085-
err = lfs_dir_append_(lfs, &newcwd, &newentry,
2118+
err = lfs_dir_append(lfs, &newcwd, &newentry,
20862119
&(struct lfs_region){
2087-
LFS_FROM_MEM, 0, 0,
2088-
{.m.data = (uint8_t*)&newentry.d + 4}, sizeof(newentry.d) - 4,
2120+
LFS_FROM_MEM, 0, +(sizeof(newentry.d)-4),
2121+
{.m.data = (const uint8_t*)&newentry.d+4},
2122+
sizeof(newentry.d)-4,
20892123
&(struct lfs_region){
2090-
LFS_FROM_MEM, 0, 0,
2124+
LFS_FROM_MEM, 0, +newentry.d.nlen,
20912125
{.m.data = newpath}, newentry.d.nlen}});
20922126
if (err) {
20932127
return err;
@@ -2255,7 +2289,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
22552289
bool valid = false;
22562290
for (int i = 0; i < 2; i++) {
22572291
err = lfs_dir_commit(lfs, &superdir, &(struct lfs_region){
2258-
LFS_FROM_MEM, sizeof(superdir.d), sizeof(superblock.d),
2292+
LFS_FROM_MEM, sizeof(superdir.d), 0,
22592293
{.m.data = &superblock.d}, sizeof(superblock.d)});
22602294
if (err && err != LFS_ERR_CORRUPT) {
22612295
return err;
@@ -2520,7 +2554,10 @@ static int lfs_relocate(lfs_t *lfs,
25202554
entry.d.u.dir[0] = newpair[0];
25212555
entry.d.u.dir[1] = newpair[1];
25222556

2523-
int err = lfs_dir_update(lfs, &parent, &entry, NULL);
2557+
int err = lfs_dir_update(lfs, &parent, &entry,
2558+
&(struct lfs_region){
2559+
LFS_FROM_MEM, 0, 0,
2560+
{.m.data = (const uint8_t*)&entry.d+4}, sizeof(entry.d)-4});
25242561
if (err) {
25252562
return err;
25262563
}

0 commit comments

Comments
 (0)