mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	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:
		
							
								
								
									
										52
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -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; | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user