mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Support removal of entire directories that contain only files
Removing directories containing other directories is problematic from an orphan perspective, but removing a directory only containing files is fairly easy to do. Add logic that checks if a directory only contains files, and if it does, permit removal of this directory.
This commit is contained in:
		
							
								
								
									
										66
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -3190,9 +3190,10 @@ static int lfs_rawstat(lfs_t *lfs, const char *path, struct lfs_info *info) { | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifndef LFS_READONLY | #ifndef LFS_READONLY | ||||||
| static int lfs_dir_prep_dirremoval(lfs_t *lfs, struct lfs_mlist *dir, lfs_mdir_t *newcwd, uint16_t newid) | static int lfs_dir_prep_removal(lfs_t *lfs, struct lfs_mlist *dir, | ||||||
|  |         lfs_mdir_t *newcwd, uint16_t newid, lfs_block_t *pair, | ||||||
|  |         lfs_gstate_t *tmp_gstate) | ||||||
| { | { | ||||||
|     lfs_block_t pair[2]; |  | ||||||
|     lfs_stag_t res = lfs_dir_get(lfs, newcwd, LFS_MKTAG(0x700, 0x3ff, 0), |     lfs_stag_t res = lfs_dir_get(lfs, newcwd, LFS_MKTAG(0x700, 0x3ff, 0), | ||||||
|             LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), pair); |             LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), pair); | ||||||
|     if (res < 0) { |     if (res < 0) { | ||||||
| @@ -3206,7 +3207,46 @@ static int lfs_dir_prep_dirremoval(lfs_t *lfs, struct lfs_mlist *dir, lfs_mdir_t | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (dir->m.count > 0 || dir->m.split) { |     if (dir->m.count > 0 || dir->m.split) { | ||||||
|         return LFS_ERR_NOTEMPTY; |         uint16_t id = 0; | ||||||
|  |  | ||||||
|  |         // Walk tags stored in this directory and check for any directory | ||||||
|  |         // tags.  Removal of directories with a directory in them can lead | ||||||
|  |         // to additional orphans in the filesystem, so we return | ||||||
|  |         // LFS_ERR_NOTEMPTY in this case.  Otherwise, leave the loaded | ||||||
|  |         // directory for the tail end of the directory split to leave a proper | ||||||
|  |         // view of the filesystem after removal. | ||||||
|  |         while (true) { | ||||||
|  |             if (dir->m.count == id) { | ||||||
|  |                 if (!dir->m.split) { | ||||||
|  |                     // We have iterated through the folder to the last | ||||||
|  |                     // tag. | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Before we fetch the next block, update our fetched gstate xor | ||||||
|  |                 lfs_dir_getgstate(lfs, &dir->m, tmp_gstate); | ||||||
|  |  | ||||||
|  |                 err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); | ||||||
|  |                 if (err) { | ||||||
|  |                     return err; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 id = 0; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs_stag_t tag = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x780, 0x3ff, 0), | ||||||
|  |                     LFS_MKTAG(LFS_TYPE_NAME, id, 0), NULL); | ||||||
|  |  | ||||||
|  |             if (tag < 0) { | ||||||
|  |                 return tag; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { | ||||||
|  |                 return LFS_ERR_NOTEMPTY; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             id += 1; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // mark fs as orphaned |     // mark fs as orphaned | ||||||
| @@ -3240,10 +3280,12 @@ static int lfs_rawremove(lfs_t *lfs, const char *path) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct lfs_mlist dir; |     struct lfs_mlist dir; | ||||||
|  |     lfs_block_t pair[2]; | ||||||
|  |     lfs_gstate_t tmp_gstate; | ||||||
|     dir.next = lfs->mlist; |     dir.next = lfs->mlist; | ||||||
|     if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { |     if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { | ||||||
|         // must be empty before removal to prevent orphans |         // must be empty before removal to prevent orphans | ||||||
|         err = lfs_dir_prep_dirremoval(lfs, &dir, &cwd, lfs_tag_id(tag)); |         err = lfs_dir_prep_removal(lfs, &dir, &cwd, lfs_tag_id(tag), pair, &tmp_gstate); | ||||||
|         if (err < 0) { |         if (err < 0) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -3265,11 +3307,15 @@ static int lfs_rawremove(lfs_t *lfs, const char *path) { | |||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         err = lfs_fs_pred(lfs, dir.m.pair, &cwd); |         err = lfs_fs_pred(lfs, pair, &cwd); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // Merge in gstate from first block splits within the directory; | ||||||
|  |         // lfs_dir_drop will pick up the last gstate entry. | ||||||
|  |         lfs_gstate_xor(&lfs->gdelta, &tmp_gstate); | ||||||
|  |  | ||||||
|         err = lfs_dir_drop(lfs, &cwd, &dir.m); |         err = lfs_dir_drop(lfs, &cwd, &dir.m); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -3309,6 +3355,8 @@ static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|     uint16_t newoldid = lfs_tag_id(oldtag); |     uint16_t newoldid = lfs_tag_id(oldtag); | ||||||
|  |  | ||||||
|     struct lfs_mlist prevdir; |     struct lfs_mlist prevdir; | ||||||
|  |     lfs_block_t dir_pair[2]; | ||||||
|  |     lfs_gstate_t tmp_gstate; | ||||||
|     prevdir.next = lfs->mlist; |     prevdir.next = lfs->mlist; | ||||||
|     if (prevtag == LFS_ERR_NOENT) { |     if (prevtag == LFS_ERR_NOENT) { | ||||||
|         // check that name fits |         // check that name fits | ||||||
| @@ -3330,7 +3378,7 @@ static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         return 0; |         return 0; | ||||||
|     } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { |     } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { | ||||||
|         // must be empty before removal to prevent orphans |         // must be empty before removal to prevent orphans | ||||||
|         err = lfs_dir_prep_dirremoval(lfs, &prevdir, &newcwd, newid); |         err = lfs_dir_prep_removal(lfs, &prevdir, &newcwd, newid, dir_pair, &tmp_gstate); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -3375,11 +3423,15 @@ static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         err = lfs_fs_pred(lfs, prevdir.m.pair, &newcwd); |         err = lfs_fs_pred(lfs, dir_pair, &newcwd); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // Merge in gstate from first split blocks in the directory; | ||||||
|  |         // lfs_dir_drop will pick up the other gstate entries. | ||||||
|  |         lfs_gstate_xor(&lfs->gdelta, &tmp_gstate); | ||||||
|  |  | ||||||
|         err = lfs_dir_drop(lfs, &newcwd, &prevdir.m); |         err = lfs_dir_drop(lfs, &newcwd, &prevdir.m); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user