Skip to content

Commit a30142e

Browse files
committed
Fixed allocation bugs near the end of storage
Also added better testing specifically for these corner cases around the end of storage
1 parent 210b487 commit a30142e

File tree

3 files changed

+108
-25
lines changed

3 files changed

+108
-25
lines changed

lfs.c

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -256,34 +256,39 @@ static inline bool lfs_pairisnull(const lfs_block_t pair[2]) {
256256
static inline int lfs_paircmp(
257257
const lfs_block_t paira[2],
258258
const lfs_block_t pairb[2]) {
259-
return !((paira[0] == pairb[0] && paira[1] == pairb[1]) ||
260-
(paira[0] == pairb[1] && paira[1] == pairb[0]));
259+
return !(paira[0] == pairb[0] || paira[1] == pairb[1] ||
260+
paira[0] == pairb[1] || paira[1] == pairb[0]);
261261
}
262262

263263
static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
264-
// Allocate pair of dir blocks
264+
// allocate pair of dir blocks
265265
for (int i = 0; i < 2; i++) {
266266
int err = lfs_alloc(lfs, &dir->pair[i]);
267267
if (err) {
268268
return err;
269269
}
270270
}
271271

272-
// Rather than clobbering one of the blocks we just pretend
272+
// we couldn't find unique blocks, we're out of space
273+
if (dir->pair[0] == dir->pair[1]) {
274+
return LFS_ERR_NOSPC;
275+
}
276+
277+
// rather than clobbering one of the blocks we just pretend
273278
// the revision may be valid
274279
int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4);
275280
if (err) {
276281
return err;
277282
}
278283

279-
// Set defaults
284+
// set defaults
280285
dir->d.rev += 1;
281286
dir->d.size = sizeof(dir->d);
282287
dir->d.tail[0] = 0;
283288
dir->d.tail[1] = 0;
284289
dir->off = sizeof(dir->d);
285290

286-
// Don't write out yet, let caller take care of that
291+
// don't write out yet, let caller take care of that
287292
return 0;
288293
}
289294

@@ -484,13 +489,21 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
484489
return lfs_dir_commit(lfs, dir, entry, data);
485490
}
486491

492+
// we need to allocate a new dir block
487493
if (!(0x80000000 & dir->d.size)) {
488494
lfs_dir_t newdir;
489495
int err = lfs_dir_alloc(lfs, &newdir);
490496
if (err) {
491497
return err;
492498
}
493499

500+
// our allocator doesn't track blocks before being appended,
501+
// so even if we found some blocks, they may not be unique
502+
if (entry->d.type == LFS_TYPE_DIR &&
503+
lfs_paircmp(entry->d.u.dir, newdir.pair) == 0) {
504+
return LFS_ERR_NOSPC;
505+
}
506+
494507
newdir.d.tail[0] = dir->d.tail[0];
495508
newdir.d.tail[1] = dir->d.tail[1];
496509
entry->off = newdir.d.size;
@@ -1677,7 +1690,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) {
16771690
.d.tail[1] = lfs->root[1],
16781691
};
16791692

1680-
while (parent.d.tail[0]) {
1693+
while (true) {
16811694
lfs_entry_t entry;
16821695
int err = lfs_dir_fetch(lfs, &parent, parent.d.tail);
16831696
if (err) {
@@ -1699,9 +1712,11 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) {
16991712
return true;
17001713
}
17011714
}
1702-
}
17031715

1704-
return false;
1716+
if (lfs_pairisnull(parent.d.tail)) {
1717+
return false;
1718+
}
1719+
}
17051720
}
17061721

17071722
int lfs_deorphan(lfs_t *lfs) {
@@ -1715,31 +1730,34 @@ int lfs_deorphan(lfs_t *lfs) {
17151730
return err;
17161731
}
17171732

1718-
while (pdir.d.tail[0]) {
1733+
while (!lfs_pairisnull(pdir.d.tail)) {
17191734
int err = lfs_dir_fetch(lfs, &cdir, pdir.d.tail);
17201735
if (err) {
17211736
return err;
17221737
}
17231738

1724-
// check if we have a parent
1725-
int parent = lfs_parent(lfs, pdir.d.tail);
1726-
if (parent < 0) {
1727-
return parent;
1728-
}
1739+
// only check head blocks
1740+
if (!(0x80000000 & pdir.d.size)) {
1741+
// check if we have a parent
1742+
int parent = lfs_parent(lfs, pdir.d.tail);
1743+
if (parent < 0) {
1744+
return parent;
1745+
}
17291746

1730-
if (!parent) {
1731-
// we are an orphan
1732-
LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]);
1747+
if (!parent) {
1748+
// we are an orphan
1749+
LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]);
17331750

1734-
pdir.d.tail[0] = cdir.d.tail[0];
1735-
pdir.d.tail[1] = cdir.d.tail[1];
1751+
pdir.d.tail[0] = cdir.d.tail[0];
1752+
pdir.d.tail[1] = cdir.d.tail[1];
17361753

1737-
err = lfs_dir_commit(lfs, &pdir, NULL, NULL);
1738-
if (err) {
1739-
return err;
1740-
}
1754+
err = lfs_dir_commit(lfs, &pdir, NULL, NULL);
1755+
if (err) {
1756+
return err;
1757+
}
17411758

1742-
break;
1759+
break;
1760+
}
17431761
}
17441762

17451763
memcpy(&pdir, &cdir, sizeof(pdir));

lfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ typedef struct lfs {
195195
lfs_size_t words; // number of 32-bit words that can fit in a block
196196

197197
lfs_block_t root[2];
198+
lfs_dir_t *scratch;
198199
lfs_file_t *files;
199200

200201
struct {

tests/test_alloc.sh

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,69 @@ tests/test.py << TEST
193193
lfs_unmount(&lfs) => 0;
194194
TEST
195195

196+
echo "--- Dir exhaustion test ---"
197+
tests/test.py << TEST
198+
lfs_mount(&lfs, &cfg) => 0;
199+
lfs_stat(&lfs, "exhaustion", &info) => 0;
200+
lfs_size_t fullsize = info.size;
201+
lfs_remove(&lfs, "exhaustion") => 0;
202+
203+
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
204+
size = strlen("blahblahblahblah");
205+
memcpy(buffer, "blahblahblahblah", size);
206+
for (lfs_size_t i = 0; i < fullsize - 2*512; i += size) {
207+
lfs_file_write(&lfs, &file[0], buffer, size) => size;
208+
}
209+
lfs_file_close(&lfs, &file[0]) => 0;
210+
211+
lfs_mkdir(&lfs, "exhaustiondir") => 0;
212+
lfs_remove(&lfs, "exhaustiondir") => 0;
213+
214+
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
215+
size = strlen("blahblahblahblah");
216+
memcpy(buffer, "blahblahblahblah", size);
217+
lfs_file_write(&lfs, &file[0], buffer, size) => size;
218+
lfs_file_close(&lfs, &file[0]) => 0;
219+
220+
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
221+
lfs_unmount(&lfs) => 0;
222+
TEST
223+
224+
echo "--- Chained dir exhaustion test ---"
225+
tests/test.py << TEST
226+
lfs_mount(&lfs, &cfg) => 0;
227+
lfs_stat(&lfs, "exhaustion", &info) => 0;
228+
lfs_size_t fullsize = info.size;
229+
230+
lfs_remove(&lfs, "exhaustion") => 0;
231+
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
232+
size = strlen("blahblahblahblah");
233+
memcpy(buffer, "blahblahblahblah", size);
234+
for (lfs_size_t i = 0; i < fullsize - 19*512; i += size) {
235+
lfs_file_write(&lfs, &file[0], buffer, size) => size;
236+
}
237+
lfs_file_close(&lfs, &file[0]) => 0;
238+
239+
for (int i = 0; i < 9; i++) {
240+
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
241+
lfs_mkdir(&lfs, (char*)buffer) => 0;
242+
}
243+
244+
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
245+
246+
lfs_remove(&lfs, "exhaustion") => 0;
247+
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
248+
size = strlen("blahblahblahblah");
249+
memcpy(buffer, "blahblahblahblah", size);
250+
for (lfs_size_t i = 0; i < fullsize - 20*512; i += size) {
251+
lfs_file_write(&lfs, &file[0], buffer, size) => size;
252+
}
253+
lfs_file_close(&lfs, &file[0]) => 0;
254+
255+
lfs_mkdir(&lfs, "exhaustiondir") => 0;
256+
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
257+
TEST
258+
259+
196260
echo "--- Results ---"
197261
tests/stats.py

0 commit comments

Comments
 (0)