mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	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:
		
							
								
								
									
										104
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								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,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; | ||||
|     } | ||||
|   | ||||
| @@ -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__": | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										86
									
								
								tests/test_paths.sh
									
									
									
									
									
										Executable 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 | ||||
		Reference in New Issue
	
	Block a user