Added path iteration and chained directories

All path iteration all goes through the lfs_dir_find function,
which manages the syntax of paths and updates the path pointer
to just the name stored in the dir entry.

Also added directory chaining, which allows more than one block
per directory. This is a simple linked list.
This commit is contained in:
Christopher Haster
2017-04-01 10:09:17 -05:00
parent 390ca3303f
commit ca01b72a35
4 changed files with 270 additions and 63 deletions

104
lfs.c
View File

@@ -310,7 +310,7 @@ static int lfs_pair_commit(lfs_t *lfs, lfs_block_t pair[2],
/// Directory operations ///
static int lfs_dir_create(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t parent[2]) {
static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t parent[2]) {
// Allocate pair of dir blocks
for (int i = 0; i < 2; i++) {
int err = lfs_alloc(lfs, &dir->pair[i]);
@@ -328,7 +328,7 @@ static int lfs_dir_create(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t parent[2]) {
dir->d.rev += 1;
// Calculate total size
dir->d.size = sizeof(dir->d) + 2*sizeof(struct lfs_disk_entry) + 3;
dir->d.size = sizeof(dir->d);
dir->off = sizeof(dir->d);
// Other defaults
@@ -337,6 +337,13 @@ static int lfs_dir_create(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t parent[2]) {
dir->d.free = lfs->free;
// Write out to memory
if (!parent) {
return lfs_pair_commit(lfs, dir->pair,
1, (struct lfs_commit_region[]){
{0, sizeof(dir->d), &dir->d}
});
} else {
dir->d.size += 2*sizeof(struct lfs_disk_entry) + 3;
return lfs_pair_commit(lfs, dir->pair,
5, (struct lfs_commit_region[]){
{0, sizeof(dir->d), &dir->d},
@@ -353,11 +360,12 @@ static int lfs_dir_create(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t parent[2]) {
&(struct lfs_disk_entry){
.type = LFS_TYPE_DIR,
.len = sizeof(struct lfs_disk_entry)+2,
.u.dir[0] = parent ? parent[0] : dir->pair[0],
.u.dir[1] = parent ? parent[1] : dir->pair[1],
.u.dir[0] = parent[0] ? parent[0] : dir->pair[0],
.u.dir[1] = parent[1] ? parent[1] : dir->pair[1],
}},
{sizeof(dir->d)+2*sizeof(struct lfs_disk_entry)+1, 2, ".."},
});
}
}
static int lfs_dir_fetch(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t pair[2]) {
@@ -373,15 +381,22 @@ static int lfs_dir_fetch(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t pair[2]) {
static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
while (true) {
// TODO iterate down list
if (dir->d.size - dir->off < sizeof(entry->d)) {
if (!dir->d.tail[0]) {
entry->dir[0] = dir->pair[0];
entry->dir[1] = dir->pair[1];
entry->off = dir->off;
if (dir->d.size - dir->off < sizeof(entry->d)) {
return LFS_ERROR_NO_ENTRY;
}
int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
if (err) {
return err;
}
dir->off = sizeof(dir->d);
}
int err = lfs_bd_read(lfs, dir->pair[0], dir->off,
sizeof(entry->d), &entry->d);
if (err) {
@@ -389,18 +404,20 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
}
dir->off += entry->d.len;
// Skip any unknown entries
if ((entry->d.type & 0xf) == 1 || (entry->d.type & 0xf) == 2) {
if (entry->d.type == LFS_TYPE_REG || entry->d.type == LFS_TYPE_DIR) {
entry->dir[0] = dir->pair[0];
entry->dir[1] = dir->pair[1];
entry->off = dir->off - entry->d.len;
return 0;
}
}
}
static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
const char *path, lfs_entry_t *entry) {
// TODO follow directories
lfs_size_t pathlen = strcspn(path, "/");
const char **path, lfs_entry_t *entry) {
while (true) {
const char *pathname = *path;
lfs_size_t pathlen = strcspn(pathname, "/");
while (true) {
int err = lfs_dir_next(lfs, dir, entry);
if (err) {
@@ -412,28 +429,67 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
}
int ret = lfs_bd_cmp(lfs, entry->dir[0],
entry->off + sizeof(entry->d), pathlen, path);
entry->off + sizeof(entry->d), pathlen, pathname);
if (ret < 0) {
return ret;
}
// Found match
if (ret == true) {
break;
}
}
pathname += pathlen;
pathname += strspn(pathname, "/");
if (pathname[0] == '\0') {
return 0;
}
if (entry->d.type != LFS_TYPE_DIR) {
return LFS_ERROR_NOT_DIR;
}
int err = lfs_dir_fetch(lfs, dir, entry->d.u.dir);
if (err) {
return err;
}
*path = pathname;
}
return 0;
}
static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
const char *path, lfs_entry_t *entry) {
const char **path, lfs_entry_t *entry) {
int err = lfs_dir_find(lfs, dir, path, entry);
if (err != LFS_ERROR_NO_ENTRY) {
return err ? err : LFS_ERROR_EXISTS;
}
// Check if we fit
if (dir->d.size + strlen(path) > lfs->block_size - 4) {
return -1; // TODO make fit
if (dir->d.size + sizeof(entry->d) + strlen(*path) > lfs->block_size - 4) {
lfs_dir_t olddir;
memcpy(&olddir, dir, sizeof(olddir));
int err = lfs_dir_alloc(lfs, dir, 0);
if (err) {
return err;
}
entry->dir[0] = dir->pair[0];
entry->dir[1] = dir->pair[1];
entry->off = dir->off;
olddir.d.rev += 1;
olddir.d.tail[0] = dir->pair[0];
olddir.d.tail[1] = dir->pair[1];
olddir.d.free = lfs->free;
return lfs_pair_commit(lfs, olddir.pair,
1, (struct lfs_commit_region[]){
{0, sizeof(olddir.d), &olddir.d}
});
}
return 0;
@@ -448,14 +504,14 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
}
lfs_entry_t entry;
err = lfs_dir_append(lfs, &cwd, path, &entry);
err = lfs_dir_append(lfs, &cwd, &path, &entry);
if (err) {
return err;
}
// Build up new directory
lfs_dir_t dir;
err = lfs_dir_create(lfs, &dir, cwd.pair);
err = lfs_dir_alloc(lfs, &dir, cwd.pair);
if (err) {
return err;
}
@@ -494,7 +550,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
}
lfs_entry_t entry;
err = lfs_dir_find(lfs, dir, path, &entry);
err = lfs_dir_find(lfs, dir, &path, &entry);
if (err) {
return err;
} else if (entry.d.type != LFS_TYPE_DIR) {
@@ -546,12 +602,12 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
}
if (flags & LFS_O_CREAT) {
err = lfs_dir_append(lfs, &cwd, path, &file->entry);
err = lfs_dir_append(lfs, &cwd, &path, &file->entry);
if (err && err != LFS_ERROR_EXISTS) {
return err;
}
} else {
err = lfs_dir_find(lfs, &cwd, path, &file->entry);
err = lfs_dir_find(lfs, &cwd, &path, &file->entry);
if (err) {
return err;
}
@@ -789,11 +845,11 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
// Create free list
lfs->free.begin = 2;
lfs->free.end = lfs->block_count;
lfs->free.end = lfs->block_count-1;
// Write root directory
lfs_dir_t root;
err = lfs_dir_create(lfs, &root, 0);
err = lfs_dir_alloc(lfs, &root, (lfs_block_t[2]){0, 0});
if (err) {
return err;
}

View File

@@ -10,9 +10,8 @@ def generate(test):
template = file.read()
lines = []
for line in re.split('(?<=[;{}])\n', test.read()):
match = re.match('( *)(.*)=>(.*);', line, re.MULTILINE)
match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.MULTILINE)
if match:
tab, test, expect = match.groups()
lines.append(tab+'res = {test};'.format(test=test.strip()))
@@ -33,13 +32,17 @@ def execute():
subprocess.check_call(["./lfs"])
def main(test=None):
if test:
if test and not test.startswith('-'):
with open(test) as file:
generate(file)
else:
generate(sys.stdin)
compile()
if test == '-s':
sys.exit(1)
execute()
if __name__ == "__main__":

View File

@@ -1,6 +1,8 @@
#!/bin/bash
set -eu
LARGESIZE=128
echo "=== Directory tests ==="
rm -rf blocks
tests/test.py << TEST
@@ -62,5 +64,65 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Nested directories ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mkdir(&lfs, "potato/baked") => 0;
lfs_mkdir(&lfs, "potato/sweet") => 0;
lfs_mkdir(&lfs, "potato/fried") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_dir_open(&lfs, &dir[0], "potato") => 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, "baked") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 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 directory ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mkdir(&lfs, "cactus") => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i);
lfs_mkdir(&lfs, (char*)buffer) => 0;
}
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_dir_open(&lfs, &dir[0], "cactus") => 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;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "test%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS_TYPE_DIR;
}
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

86
tests/test_paths.sh Executable file
View File

@@ -0,0 +1,86 @@
#!/bin/bash
set -eu
echo "=== Path tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "soda") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
lfs_mkdir(&lfs, "soda/hotsoda") => 0;
lfs_mkdir(&lfs, "soda/warmsoda") => 0;
lfs_mkdir(&lfs, "soda/coldsoda") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Root path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_stat(&lfs, "tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Redundant slash path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "//tea//hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "///tea///hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dot path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_stat(&lfs, "./tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/././tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/./tea/./hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dot dot path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "coffee/../soda/../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Root dot dot path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py