mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Added atomic move using dirty tag in entry type
The "move problem" has been present in littlefs for a while, but I haven't come across a solution worth implementing for various reasons. The problem is simple: how do we move directory entries across directories atomically? Since multiple directory entries are involved, we can't rely entirely on the atomic block updates. It ends up being a bit of a puzzle. To make the problem more complicated, any directory block update can fail due to wear, and cause the directory block to need to be relocated. This happens rarely, but brings a large number of corner cases. --- The solution in this patch is simple: 1. Mark source as "moving" 2. Copy source to destination 3. Remove source If littlefs ever runs into a "moving" entry, that means a power loss occured during a move. Either the destination entry exists or it doesn't. In this case we just search the entire filesystem for the destination entry. This is expensive, however the chance of a power loss during a move is relatively low.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -32,7 +32,7 @@ size: $(OBJ) | |||||||
|  |  | ||||||
| .SUFFIXES: | .SUFFIXES: | ||||||
| test: test_format test_dirs test_files test_seek test_parallel \ | test: test_format test_dirs test_files test_seek test_parallel \ | ||||||
| 	test_alloc test_paths test_orphan test_corrupt | 	test_alloc test_paths test_orphan test_move test_corrupt | ||||||
| test_%: tests/test_%.sh | test_%: tests/test_%.sh | ||||||
| 	./$< | 	./$< | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										232
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										232
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -253,9 +253,11 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | |||||||
| static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir); | static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir); | ||||||
| static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | ||||||
|         lfs_dir_t *parent, lfs_entry_t *entry); |         lfs_dir_t *parent, lfs_entry_t *entry); | ||||||
|  | static int lfs_moved(lfs_t *lfs, const void *e); | ||||||
| static int lfs_relocate(lfs_t *lfs, | static int lfs_relocate(lfs_t *lfs, | ||||||
|         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]); |         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]); | ||||||
| int lfs_deorphan(lfs_t *lfs); | int lfs_deorphan(lfs_t *lfs); | ||||||
|  | int lfs_deduplicate(lfs_t *lfs); | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Block allocator /// | /// Block allocator /// | ||||||
| @@ -722,8 +724,8 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ((entry->d.type != LFS_TYPE_REG && |             if (((0x7f & entry->d.type) != LFS_TYPE_REG && | ||||||
|                  entry->d.type != LFS_TYPE_DIR) || |                  (0x7f & entry->d.type) != LFS_TYPE_DIR) || | ||||||
|                 entry->d.nlen != pathlen) { |                 entry->d.nlen != pathlen) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| @@ -741,6 +743,16 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // check that entry has not been moved | ||||||
|  |         if (entry->d.type & 0x80) { | ||||||
|  |             int moved = lfs_moved(lfs, &entry->d.u); | ||||||
|  |             if (moved < 0 || moved) { | ||||||
|  |                 return (moved < 0) ? moved : LFS_ERR_NOENT; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             entry->d.type &= ~0x80; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         pathname += pathlen; |         pathname += pathlen; | ||||||
|         pathname += strspn(pathname, "/"); |         pathname += strspn(pathname, "/"); | ||||||
|         if (pathname[0] == '\0') { |         if (pathname[0] == '\0') { | ||||||
| @@ -764,6 +776,14 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | |||||||
|  |  | ||||||
| /// Top level directory operations /// | /// Top level directory operations /// | ||||||
| int lfs_mkdir(lfs_t *lfs, const char *path) { | int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||||
|  |     // make sure directories are clean | ||||||
|  |     if (!lfs->deduplicated) { | ||||||
|  |         int err = lfs_deduplicate(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // fetch parent directory |     // fetch parent directory | ||||||
|     lfs_dir_t cwd; |     lfs_dir_t cwd; | ||||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); |     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||||
| @@ -880,10 +900,26 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { | |||||||
|             return (err == LFS_ERR_NOENT) ? 0 : err; |             return (err == LFS_ERR_NOENT) ? 0 : err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (entry.d.type == LFS_TYPE_REG || |         if ((0x7f & entry.d.type) != LFS_TYPE_REG && | ||||||
|             entry.d.type == LFS_TYPE_DIR) { |             (0x7f & entry.d.type) != LFS_TYPE_DIR) { | ||||||
|             break; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // check that entry has not been moved | ||||||
|  |         if (entry.d.type & 0x80) { | ||||||
|  |             int moved = lfs_moved(lfs, &entry.d.u); | ||||||
|  |             if (moved < 0) { | ||||||
|  |                 return moved; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (moved) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             entry.d.type &= ~0x80; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     info->type = entry.d.type; |     info->type = entry.d.type; | ||||||
| @@ -1113,6 +1149,14 @@ static int lfs_index_traverse(lfs_t *lfs, | |||||||
| /// Top level file operations /// | /// Top level file operations /// | ||||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||||
|         const char *path, int flags) { |         const char *path, int flags) { | ||||||
|  |     // make sure directories are clean | ||||||
|  |     if ((flags & 3) != LFS_O_RDONLY && !lfs->deduplicated) { | ||||||
|  |         int err = lfs_deduplicate(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // allocate entry for file if it doesn't exist |     // allocate entry for file if it doesn't exist | ||||||
|     lfs_dir_t cwd; |     lfs_dir_t cwd; | ||||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); |     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||||
| @@ -1598,6 +1642,14 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_remove(lfs_t *lfs, const char *path) { | int lfs_remove(lfs_t *lfs, const char *path) { | ||||||
|  |     // make sure directories are clean | ||||||
|  |     if (!lfs->deduplicated) { | ||||||
|  |         int err = lfs_deduplicate(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     lfs_dir_t cwd; |     lfs_dir_t cwd; | ||||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); |     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||||
|     if (err) { |     if (err) { | ||||||
| @@ -1654,6 +1706,14 @@ int lfs_remove(lfs_t *lfs, const char *path) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||||
|  |     // make sure directories are clean | ||||||
|  |     if (!lfs->deduplicated) { | ||||||
|  |         int err = lfs_deduplicate(lfs); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // find old entry |     // find old entry | ||||||
|     lfs_dir_t oldcwd; |     lfs_dir_t oldcwd; | ||||||
|     int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root); |     int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root); | ||||||
| @@ -1667,6 +1727,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // mark as moving | ||||||
|  |     oldentry.d.type |= 0x80; | ||||||
|  |     err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |     oldentry.d.type &= ~0x80; | ||||||
|  |  | ||||||
|     // allocate new entry |     // allocate new entry | ||||||
|     lfs_dir_t newcwd; |     lfs_dir_t newcwd; | ||||||
|     err = lfs_dir_fetch(lfs, &newcwd, lfs->root); |     err = lfs_dir_fetch(lfs, &newcwd, lfs->root); | ||||||
| @@ -1716,35 +1784,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // fetch again in case newcwd == oldcwd |  | ||||||
|     err = lfs_dir_fetch(lfs, &oldcwd, oldcwd.pair); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // remove from old location |  | ||||||
|     err = lfs_dir_remove(lfs, &oldcwd, &oldentry); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // shift over any files that are affected |  | ||||||
|     for (lfs_file_t *f = lfs->files; f; f = f->next) { |  | ||||||
|         if (lfs_paircmp(f->pair, oldcwd.pair) == 0) { |  | ||||||
|             if (f->poff == oldentry.off) { |  | ||||||
|                 f->pair[0] = 0xffffffff; |  | ||||||
|                 f->pair[1] = 0xffffffff; |  | ||||||
|             } else if (f->poff > oldentry.off) { |  | ||||||
|                 f->poff -= lfs_entry_size(&oldentry); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // if we were a directory, just run a deorphan step, this should |     // if we were a directory, just run a deorphan step, this should | ||||||
|     // collect us, although is expensive |     // collect us, although is expensive | ||||||
|     if (prevexists && preventry.d.type == LFS_TYPE_DIR) { |     if (prevexists && preventry.d.type == LFS_TYPE_DIR) { | ||||||
| @@ -1754,6 +1793,12 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // just deduplicate | ||||||
|  |     err = lfs_deduplicate(lfs); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1802,6 +1847,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->root[1] = 0xffffffff; |     lfs->root[1] = 0xffffffff; | ||||||
|     lfs->files = NULL; |     lfs->files = NULL; | ||||||
|     lfs->deorphaned = false; |     lfs->deorphaned = false; | ||||||
|  |     lfs->deduplicated = false; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -1979,7 +2025,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             dir.off += lfs_entry_size(&entry); |             dir.off += lfs_entry_size(&entry); | ||||||
|             if ((0xf & entry.d.type) == (0xf & LFS_TYPE_REG)) { |             if ((0x70 & entry.d.type) == (0x70 & LFS_TYPE_REG)) { | ||||||
|                 int err = lfs_index_traverse(lfs, &lfs->rcache, NULL, |                 int err = lfs_index_traverse(lfs, &lfs->rcache, NULL, | ||||||
|                         entry.d.u.file.head, entry.d.u.file.size, cb, data); |                         entry.d.u.file.head, entry.d.u.file.size, cb, data); | ||||||
|                 if (err) { |                 if (err) { | ||||||
| @@ -2069,7 +2115,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (((0xf & entry->d.type) == (0xf & LFS_TYPE_DIR)) && |             if (((0x70 & entry->d.type) == (0x70 & LFS_TYPE_DIR)) && | ||||||
|                  lfs_paircmp(entry->d.u.dir, dir) == 0) { |                  lfs_paircmp(entry->d.u.dir, dir) == 0) { | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
| @@ -2079,6 +2125,46 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int lfs_moved(lfs_t *lfs, const void *e) { | ||||||
|  |     if (lfs_pairisnull(lfs->root)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // skip superblock | ||||||
|  |     lfs_dir_t cwd; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // iterate over all directory directory entries | ||||||
|  |     lfs_entry_t entry; | ||||||
|  |     while (!lfs_pairisnull(cwd.d.tail)) { | ||||||
|  |         int err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         while (true) { | ||||||
|  |             int err = lfs_dir_next(lfs, &cwd, &entry); | ||||||
|  |             if (err && err != LFS_ERR_NOENT) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (err == LFS_ERR_NOENT) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!(0x80 & entry.d.type) &&  | ||||||
|  |                  memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int lfs_relocate(lfs_t *lfs, | static int lfs_relocate(lfs_t *lfs, | ||||||
|         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) { |         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) { | ||||||
|     // find parent |     // find parent | ||||||
| @@ -2197,3 +2283,77 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int lfs_deduplicate(lfs_t *lfs) { | ||||||
|  |     lfs->deduplicated = true; | ||||||
|  |  | ||||||
|  |     if (lfs_pairisnull(lfs->root)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // skip superblock | ||||||
|  |     lfs_dir_t cwd; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // iterate over all directory directory entries | ||||||
|  |     lfs_entry_t entry; | ||||||
|  |     while (!lfs_pairisnull(cwd.d.tail)) { | ||||||
|  |         int err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         while (true) { | ||||||
|  |             int err = lfs_dir_next(lfs, &cwd, &entry); | ||||||
|  |             if (err && err != LFS_ERR_NOENT) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (err == LFS_ERR_NOENT) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // found moved entry | ||||||
|  |             if (entry.d.type & 0x80) { | ||||||
|  |                 int moved = lfs_moved(lfs, &entry.d.u); | ||||||
|  |                 if (moved < 0) { | ||||||
|  |                     return moved; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (moved) { | ||||||
|  |                     LFS_DEBUG("Found move %d %d", | ||||||
|  |                             entry.d.u.dir[0], entry.d.u.dir[1]); | ||||||
|  |                     int err = lfs_dir_remove(lfs, &cwd, &entry); | ||||||
|  |                     if (err) { | ||||||
|  |                         return err; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     // shift over any files that are affected | ||||||
|  |                     for (lfs_file_t *f = lfs->files; f; f = f->next) { | ||||||
|  |                         if (lfs_paircmp(f->pair, cwd.pair) == 0) { | ||||||
|  |                             if (f->poff == entry.off) { | ||||||
|  |                                 f->pair[0] = 0xffffffff; | ||||||
|  |                                 f->pair[1] = 0xffffffff; | ||||||
|  |                             } else if (f->poff > entry.off) { | ||||||
|  |                                 f->poff -= lfs_entry_size(&entry); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     LFS_DEBUG("Found partial move %d %d", | ||||||
|  |                             entry.d.u.dir[0], entry.d.u.dir[1]); | ||||||
|  |                     entry.d.type &= ~0x80; | ||||||
|  |                     int err = lfs_dir_update(lfs, &cwd, &entry, NULL); | ||||||
|  |                     if (err) { | ||||||
|  |                         return err; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -46,7 +46,7 @@ enum lfs_error { | |||||||
| enum lfs_type { | enum lfs_type { | ||||||
|     LFS_TYPE_REG        = 0x11, |     LFS_TYPE_REG        = 0x11, | ||||||
|     LFS_TYPE_DIR        = 0x22, |     LFS_TYPE_DIR        = 0x22, | ||||||
|     LFS_TYPE_SUPERBLOCK = 0xe2, |     LFS_TYPE_SUPERBLOCK = 0x2e, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File open flags | // File open flags | ||||||
| @@ -244,6 +244,7 @@ typedef struct lfs { | |||||||
|  |  | ||||||
|     lfs_free_t free; |     lfs_free_t free; | ||||||
|     bool deorphaned; |     bool deorphaned; | ||||||
|  |     bool deduplicated; | ||||||
| } lfs_t; | } lfs_t; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -434,5 +435,8 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | |||||||
| // Returns a negative error code on failure. | // Returns a negative error code on failure. | ||||||
| int lfs_deorphan(lfs_t *lfs); | int lfs_deorphan(lfs_t *lfs); | ||||||
|  |  | ||||||
|  | // TODO doc | ||||||
|  | int lfs_deduplicate(lfs_t *lfs); | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										236
									
								
								tests/test_move.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										236
									
								
								tests/test_move.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,236 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | set -eu | ||||||
|  |  | ||||||
|  | echo "=== Move tests ===" | ||||||
|  | rm -rf blocks | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mkdir(&lfs, "a") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "b") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "c") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "d") => 0; | ||||||
|  |  | ||||||
|  |     lfs_mkdir(&lfs, "a/hi") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "a/hi/hola") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "a/hi/bonjour") => 0; | ||||||
|  |     lfs_mkdir(&lfs, "a/hi/ohayo") => 0; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||||
|  |     lfs_file_write(&lfs, &file[0], "hola\n", 5) => 5; | ||||||
|  |     lfs_file_write(&lfs, &file[0], "bonjour\n", 8) => 8; | ||||||
|  |     lfs_file_write(&lfs, &file[0], "ohayo\n", 6) => 6; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move file ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "a/hello", "b/hello") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "a") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hi") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move file corrupt source ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | rm -v blocks/7 | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move file corrupt source and dest ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | rm -v blocks/8 | ||||||
|  | rm -v blocks/a | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move dir ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "a/hi", "b/hi") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "a") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hi") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move dir corrupt source ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | rm -v blocks/7 | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hi") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move dir corrupt source and dest ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  | rm -v blocks/9 | ||||||
|  | rm -v blocks/a | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hello") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hi") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  | echo "--- Move check ---" | ||||||
|  | tests/test.py << TEST | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "c/hi") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, ".") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "..") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "hola") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "bonjour") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||||
|  |     strcmp(info.name, "ohayo") => 0; | ||||||
|  |     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||||
|  |     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT; | ||||||
|  |     lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs_file_open(&lfs, &file[0], "c/hello", LFS_O_RDONLY) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, 5) => 5; | ||||||
|  |     memcmp(buffer, "hola\n", 5) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, 8) => 8; | ||||||
|  |     memcmp(buffer, "bonjour\n", 8) => 0; | ||||||
|  |     lfs_file_read(&lfs, &file[0], buffer, 6) => 6; | ||||||
|  |     memcmp(buffer, "ohayo\n", 6) => 0; | ||||||
|  |     lfs_file_close(&lfs, &file[0]) => 0; | ||||||
|  |  | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  | TEST | ||||||
|  |  | ||||||
|  |  | ||||||
|  | echo "--- Results ---" | ||||||
|  | tests/stats.py | ||||||
		Reference in New Issue
	
	Block a user