diff --git a/lfs.c b/lfs.c index 9ea51b4..9f48d3e 100644 --- a/lfs.c +++ b/lfs.c @@ -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,27 +337,35 @@ 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 - return lfs_pair_commit(lfs, dir->pair, - 5, (struct lfs_commit_region[]){ - {0, sizeof(dir->d), &dir->d}, - {sizeof(dir->d), sizeof(struct lfs_disk_entry), - &(struct lfs_disk_entry){ - .type = LFS_TYPE_DIR, - .len = sizeof(struct lfs_disk_entry)+1, - .u.dir[0] = dir->pair[0], - .u.dir[1] = dir->pair[1], - }}, - {sizeof(dir->d)+sizeof(struct lfs_disk_entry), 1, "."}, - {sizeof(dir->d)+sizeof(struct lfs_disk_entry)+1, - sizeof(struct lfs_disk_entry), - &(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], - }}, - {sizeof(dir->d)+2*sizeof(struct lfs_disk_entry)+1, 2, ".."}, - }); + 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}, + {sizeof(dir->d), sizeof(struct lfs_disk_entry), + &(struct lfs_disk_entry){ + .type = LFS_TYPE_DIR, + .len = sizeof(struct lfs_disk_entry)+1, + .u.dir[0] = dir->pair[0], + .u.dir[1] = dir->pair[1], + }}, + {sizeof(dir->d)+sizeof(struct lfs_disk_entry), 1, "."}, + {sizeof(dir->d)+sizeof(struct lfs_disk_entry)+1, + sizeof(struct lfs_disk_entry), + &(struct lfs_disk_entry){ + .type = LFS_TYPE_DIR, + .len = sizeof(struct lfs_disk_entry)+2, + .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,13 +381,20 @@ 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 - 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; + if (!dir->d.tail[0]) { + entry->dir[0] = dir->pair[0]; + entry->dir[1] = dir->pair[1]; + entry->off = dir->off; + 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, @@ -389,51 +404,92 @@ 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) { - int err = lfs_dir_next(lfs, dir, entry); + const char *pathname = *path; + lfs_size_t pathlen = strcspn(pathname, "/"); + while (true) { + int err = lfs_dir_next(lfs, dir, entry); + if (err) { + return err; + } + + if (entry->d.len - sizeof(entry->d) != pathlen) { + continue; + } + + int ret = lfs_bd_cmp(lfs, entry->dir[0], + 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; } - if (entry->d.len - sizeof(entry->d) != pathlen) { - continue; - } - - int ret = lfs_bd_cmp(lfs, entry->dir[0], - entry->off + sizeof(entry->d), pathlen, path); - if (ret < 0) { - return ret; - } - - // Found match - if (ret == true) { - return 0; - } + *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; } diff --git a/tests/test.py b/tests/test.py index 03369cd..045a26f 100755 --- a/tests/test.py +++ b/tests/test.py @@ -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__": diff --git a/tests/test_dirs.sh b/tests/test_dirs.sh index ae3d12c..685bf16 100755 --- a/tests/test_dirs.sh +++ b/tests/test_dirs.sh @@ -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 diff --git a/tests/test_paths.sh b/tests/test_paths.sh new file mode 100755 index 0000000..b77fb94 --- /dev/null +++ b/tests/test_paths.sh @@ -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