Skip to content

Commit 97d8d5e

Browse files
committed
Fixed issue where a rename causes a split and pushes dir out of sync
The issue happens when a rename causes a split in the destination pair. If the destination pair is the same as the source pair, this triggers the logic to keep both pairs in sync. Unfortunately, this logic didn't work, because the source entry still resides in the old source pair, unlike the destination pair, which is now in the new pair created by the split. The best fix for now is to refetch the source pair after the changes to the destination pair. This isn't the most efficient solution, but fortunately this bug has already been fixed in the revamped move logic in littlefs v2 (currently in progress). Found by ohoc
1 parent 0bb1f7a commit 97d8d5e

File tree

3 files changed

+80
-23
lines changed

3 files changed

+80
-23
lines changed

lfs.c

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
888888
}
889889

890890
// check that entry has not been moved
891-
if (entry->d.type & 0x80) {
891+
if (!lfs->moving && entry->d.type & 0x80) {
892892
int moved = lfs_moved(lfs, &entry->d.u);
893893
if (moved < 0 || moved) {
894894
return (moved < 0) ? moved : LFS_ERR_NOENT;
@@ -1922,7 +1922,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
19221922
// find old entry
19231923
lfs_dir_t oldcwd;
19241924
lfs_entry_t oldentry;
1925-
int err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
1925+
int err = lfs_dir_find(lfs, &oldcwd, &oldentry, &(const char *){oldpath});
1926+
if (err) {
1927+
return err;
1928+
}
1929+
1930+
// mark as moving
1931+
oldentry.d.type |= 0x80;
1932+
err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL);
19261933
if (err) {
19271934
return err;
19281935
}
@@ -1935,11 +1942,9 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
19351942
return err;
19361943
}
19371944

1938-
bool prevexists = (err != LFS_ERR_NOENT);
1939-
bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0);
1940-
19411945
// must have same type
1942-
if (prevexists && preventry.d.type != oldentry.d.type) {
1946+
bool prevexists = (err != LFS_ERR_NOENT);
1947+
if (prevexists && preventry.d.type != (0x7f & oldentry.d.type)) {
19431948
return LFS_ERR_ISDIR;
19441949
}
19451950

@@ -1956,18 +1961,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
19561961
}
19571962
}
19581963

1959-
// mark as moving
1960-
oldentry.d.type |= 0x80;
1961-
err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL);
1962-
if (err) {
1963-
return err;
1964-
}
1965-
1966-
// update pair if newcwd == oldcwd
1967-
if (samepair) {
1968-
newcwd = oldcwd;
1969-
}
1970-
19711964
// move to new location
19721965
lfs_entry_t newentry = preventry;
19731966
newentry.d = oldentry.d;
@@ -1986,10 +1979,13 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
19861979
}
19871980
}
19881981

1989-
// update pair if newcwd == oldcwd
1990-
if (samepair) {
1991-
oldcwd = newcwd;
1982+
// fetch old pair again in case dir block changed
1983+
lfs->moving = true;
1984+
err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
1985+
if (err) {
1986+
return err;
19921987
}
1988+
lfs->moving = false;
19931989

19941990
// remove old entry
19951991
err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
@@ -2087,6 +2083,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
20872083
lfs->files = NULL;
20882084
lfs->dirs = NULL;
20892085
lfs->deorphaned = false;
2086+
lfs->moving = false;
20902087

20912088
return 0;
20922089

lfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ typedef struct lfs {
280280

281281
lfs_free_t free;
282282
bool deorphaned;
283+
bool moving;
283284
} lfs_t;
284285

285286

tests/test_dirs.sh

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,42 @@ tests/test.py << TEST
326326
lfs_unmount(&lfs) => 0;
327327
TEST
328328

329+
echo "--- Multi-block rename ---"
330+
tests/test.py << TEST
331+
lfs_mount(&lfs, &cfg) => 0;
332+
for (int i = 0; i < $LARGESIZE; i++) {
333+
sprintf((char*)buffer, "cactus/test%d", i);
334+
sprintf((char*)wbuffer, "cactus/tedd%d", i);
335+
lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0;
336+
}
337+
lfs_unmount(&lfs) => 0;
338+
TEST
339+
tests/test.py << TEST
340+
lfs_mount(&lfs, &cfg) => 0;
341+
lfs_dir_open(&lfs, &dir[0], "cactus") => 0;
342+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
343+
strcmp(info.name, ".") => 0;
344+
info.type => LFS_TYPE_DIR;
345+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
346+
strcmp(info.name, "..") => 0;
347+
info.type => LFS_TYPE_DIR;
348+
for (int i = 0; i < $LARGESIZE; i++) {
349+
sprintf((char*)buffer, "tedd%d", i);
350+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
351+
strcmp(info.name, (char*)buffer) => 0;
352+
info.type => LFS_TYPE_DIR;
353+
}
354+
lfs_dir_read(&lfs, &dir[0], &info) => 0;
355+
lfs_unmount(&lfs) => 0;
356+
TEST
357+
329358
echo "--- Multi-block remove ---"
330359
tests/test.py << TEST
331360
lfs_mount(&lfs, &cfg) => 0;
332361
lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
333362
334363
for (int i = 0; i < $LARGESIZE; i++) {
335-
sprintf((char*)buffer, "cactus/test%d", i);
364+
sprintf((char*)buffer, "cactus/tedd%d", i);
336365
lfs_remove(&lfs, (char*)buffer) => 0;
337366
}
338367
@@ -391,13 +420,43 @@ tests/test.py << TEST
391420
lfs_unmount(&lfs) => 0;
392421
TEST
393422

423+
echo "--- Multi-block rename with files ---"
424+
tests/test.py << TEST
425+
lfs_mount(&lfs, &cfg) => 0;
426+
for (int i = 0; i < $LARGESIZE; i++) {
427+
sprintf((char*)buffer, "prickly-pear/test%d", i);
428+
sprintf((char*)wbuffer, "prickly-pear/tedd%d", i);
429+
lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0;
430+
}
431+
lfs_unmount(&lfs) => 0;
432+
TEST
433+
tests/test.py << TEST
434+
lfs_mount(&lfs, &cfg) => 0;
435+
lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 0;
436+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
437+
strcmp(info.name, ".") => 0;
438+
info.type => LFS_TYPE_DIR;
439+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
440+
strcmp(info.name, "..") => 0;
441+
info.type => LFS_TYPE_DIR;
442+
for (int i = 0; i < $LARGESIZE; i++) {
443+
sprintf((char*)buffer, "tedd%d", i);
444+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
445+
strcmp(info.name, (char*)buffer) => 0;
446+
info.type => LFS_TYPE_REG;
447+
info.size => 6;
448+
}
449+
lfs_dir_read(&lfs, &dir[0], &info) => 0;
450+
lfs_unmount(&lfs) => 0;
451+
TEST
452+
394453
echo "--- Multi-block remove with files ---"
395454
tests/test.py << TEST
396455
lfs_mount(&lfs, &cfg) => 0;
397456
lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
398457
399458
for (int i = 0; i < $LARGESIZE; i++) {
400-
sprintf((char*)buffer, "prickly-pear/test%d", i);
459+
sprintf((char*)buffer, "prickly-pear/tedd%d", i);
401460
lfs_remove(&lfs, (char*)buffer) => 0;
402461
}
403462

0 commit comments

Comments
 (0)