Added directory list for synchronizing in flight directories

As it was, if a user operated on a directory while at the same
time iterating over the directory, the directory objects could
fall out of sync. In the best case, files may be skipped while
removing everything in a file, in the worst case, a very poorly
timed directory relocate could be missed.

Simple fix is to add the same directory tracking that is currently
in use for files, at a small code+complexity cost.
This commit is contained in:
Christopher Haster
2017-11-21 20:53:15 -06:00
parent e169d06c57
commit bf78b09d37
3 changed files with 101 additions and 29 deletions

52
lfs.c
View File

@@ -569,7 +569,18 @@ relocate:
// update references if we relocated // update references if we relocated
LFS_DEBUG("Relocating %d %d to %d %d", LFS_DEBUG("Relocating %d %d to %d %d",
oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
return lfs_relocate(lfs, oldpair, dir->pair); int err = lfs_relocate(lfs, oldpair, dir->pair);
if (err) {
return err;
}
}
// shift over any directories that are affected
for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
if (lfs_paircmp(d->pair, dir->pair) == 0) {
d->pair[0] = dir->pair[0];
d->pair[1] = dir->pair[1];
}
} }
return 0; return 0;
@@ -628,7 +639,7 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
} }
static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
// either shift out the one entry or remove the whole dir block // check if we should just drop the directory block
if ((dir->d.size & 0x7fffffff) == sizeof(dir->d)+4 if ((dir->d.size & 0x7fffffff) == sizeof(dir->d)+4
+ lfs_entry_size(entry)) { + lfs_entry_size(entry)) {
lfs_dir_t pdir; lfs_dir_t pdir;
@@ -637,17 +648,15 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
return res; return res;
} }
if (!(pdir.d.size & 0x80000000)) { if (pdir.d.size & 0x80000000) {
return lfs_dir_commit(lfs, dir, (struct lfs_region[]){
{entry->off, lfs_entry_size(entry), NULL, 0},
}, 1);
} else {
pdir.d.size &= dir->d.size | 0x7fffffff; pdir.d.size &= dir->d.size | 0x7fffffff;
pdir.d.tail[0] = dir->d.tail[0]; pdir.d.tail[0] = dir->d.tail[0];
pdir.d.tail[1] = dir->d.tail[1]; pdir.d.tail[1] = dir->d.tail[1];
return lfs_dir_commit(lfs, &pdir, NULL, 0); return lfs_dir_commit(lfs, &pdir, NULL, 0);
} }
} else { }
// shift out the entry
int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){ int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
{entry->off, lfs_entry_size(entry), NULL, 0}, {entry->off, lfs_entry_size(entry), NULL, 0},
}, 1); }, 1);
@@ -655,7 +664,7 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
return err; return err;
} }
// shift over any files that are affected // shift over any files/directories that are affected
for (lfs_file_t *f = lfs->files; f; f = f->next) { for (lfs_file_t *f = lfs->files; f; f = f->next) {
if (lfs_paircmp(f->pair, dir->pair) == 0) { if (lfs_paircmp(f->pair, dir->pair) == 0) {
if (f->poff == entry->off) { if (f->poff == entry->off) {
@@ -667,9 +676,17 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
} }
} }
return 0; for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
if (lfs_paircmp(d->pair, dir->pair) == 0) {
if (d->off > entry->off) {
d->off -= lfs_entry_size(entry);
d->pos -= lfs_entry_size(entry);
} }
} }
}
return 0;
}
static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) {
@@ -894,11 +911,23 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
dir->head[1] = dir->pair[1]; dir->head[1] = dir->pair[1];
dir->pos = sizeof(dir->d) - 2; dir->pos = sizeof(dir->d) - 2;
dir->off = sizeof(dir->d); dir->off = sizeof(dir->d);
// add to list of directories
dir->next = lfs->dirs;
lfs->dirs = dir;
return 0; return 0;
} }
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
// do nothing, dir is always synchronized // remove from list of directories
for (lfs_dir_t **p = &lfs->dirs; *p; p = &(*p)->next) {
if (*p == dir) {
*p = dir->next;
break;
}
}
return 0; return 0;
} }
@@ -1902,6 +1931,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->root[0] = 0xffffffff; lfs->root[0] = 0xffffffff;
lfs->root[1] = 0xffffffff; lfs->root[1] = 0xffffffff;
lfs->files = NULL; lfs->files = NULL;
lfs->dirs = NULL;
lfs->deorphaned = false; lfs->deorphaned = false;
return 0; return 0;

2
lfs.h
View File

@@ -207,6 +207,7 @@ typedef struct lfs_file {
} lfs_file_t; } lfs_file_t;
typedef struct lfs_dir { typedef struct lfs_dir {
struct lfs_dir *next;
lfs_block_t pair[2]; lfs_block_t pair[2];
lfs_off_t off; lfs_off_t off;
@@ -249,6 +250,7 @@ typedef struct lfs {
lfs_block_t root[2]; lfs_block_t root[2];
lfs_file_t *files; lfs_file_t *files;
lfs_dir_t *dirs;
lfs_cache_t rcache; lfs_cache_t rcache;
lfs_cache_t pcache; lfs_cache_t pcache;

View File

@@ -282,6 +282,49 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Recursive remove ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "coldpotato") => LFS_ERR_INVAL;
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
while (true) {
int err = lfs_dir_read(&lfs, &dir[0], &info);
err >= 0 => 1;
if (err == 0) {
break;
}
strcpy((char*)buffer, "coldpotato/");
strcat((char*)buffer, info.name);
lfs_remove(&lfs, (char*)buffer) => 0;
}
lfs_remove(&lfs, "coldpotato") => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "cactus") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Multi-block remove ---" echo "--- Multi-block remove ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -307,9 +350,6 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0; strcmp(info.name, "burito") => 0;
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "coldpotato") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;