@@ -3903,11 +3903,7 @@ static int lfs_dir_prep_remove_nonempty_folders(lfs_t *lfs, struct lfs_remove_st
3903
3903
uint16_t id = 0 ;
3904
3904
3905
3905
// Walk tags stored in this directory and check for any directory
3906
- // tags. Removal of directories with a directory in them can lead
3907
- // to additional orphans in the filesystem, so we return
3908
- // LFS_ERR_NOTEMPTY in this case. Otherwise, leave the loaded
3909
- // directory for the tail end of the directory split to leave a proper
3910
- // view of the filesystem after removal.
3906
+ // tags.
3911
3907
while (true) {
3912
3908
if (p -> dir .m .count == id ) {
3913
3909
if (!p -> dir .m .split ) {
@@ -3935,7 +3931,11 @@ static int lfs_dir_prep_remove_nonempty_folders(lfs_t *lfs, struct lfs_remove_st
3935
3931
}
3936
3932
3937
3933
if (lfs_tag_type3 (tag ) == LFS_TYPE_DIR ) {
3938
- // We're not allowed to find leafs
3934
+ // In case we are finding leafs, we need to update our tag, too,
3935
+ // so that the upper routine knows where this folder was in the
3936
+ // parent folder. The parent routine will decide if we need
3937
+ // to return LFS_ERR_NOTEMPTY all the way back out.
3938
+ p -> tag = tag ;
3939
3939
return LFS_ERR_NOTEMPTY ;
3940
3940
}
3941
3941
@@ -3947,10 +3947,15 @@ static int lfs_dir_prep_remove_nonempty_folders(lfs_t *lfs, struct lfs_remove_st
3947
3947
#endif
3948
3948
3949
3949
#ifndef LFS_READONLY
3950
+ // Note: This returns 1 when it had to descend to find a leaf node,
3951
+ // 0 when it didn't have to descend.
3950
3952
static int lfs_dir_prep_removal (lfs_t * lfs , struct lfs_remove_state * p ,
3951
- lfs_dir_prep_helper_t helper ) {
3953
+ lfs_dir_prep_helper_t helper , bool find_leaf ) {
3954
+ lfs_block_t depth = 0 ;
3955
+ lfs_stag_t res ;
3952
3956
3953
- lfs_stag_t res = lfs_dir_get (lfs , & p -> cwd , LFS_MKTAG (0x700 , 0x3ff , 0 ),
3957
+ new_leaf :
3958
+ res = lfs_dir_get (lfs , & p -> cwd , LFS_MKTAG (0x700 , 0x3ff , 0 ),
3954
3959
LFS_MKTAG (LFS_TYPE_STRUCT , lfs_tag_id (p -> tag ), 8 ), p -> dir_head );
3955
3960
if (res < 0 ) {
3956
3961
return (int )res ;
@@ -3972,7 +3977,20 @@ static int lfs_dir_prep_removal(lfs_t *lfs, struct lfs_remove_state *p,
3972
3977
}
3973
3978
3974
3979
err = helper (lfs , p );
3975
- if (err ) {
3980
+ if (err == LFS_ERR_NOTEMPTY && find_leaf ) {
3981
+ if (depth ++ > lfs -> cfg -> block_count / 2 ) {
3982
+ return LFS_ERR_CORRUPT ;
3983
+ }
3984
+
3985
+ // Descend CWD
3986
+ memcpy (& p -> cwd , & p -> dir .m , sizeof (p -> cwd ));
3987
+
3988
+ // And loop
3989
+ goto new_leaf ;
3990
+ }
3991
+
3992
+ if (err )
3993
+ {
3976
3994
return err ;
3977
3995
}
3978
3996
}
@@ -3989,7 +4007,7 @@ static int lfs_dir_prep_removal(lfs_t *lfs, struct lfs_remove_state *p,
3989
4007
p -> dir .id = 0 ;
3990
4008
lfs -> mlist = & p -> dir ;
3991
4009
3992
- return 0 ;
4010
+ return depth != 0 ;
3993
4011
}
3994
4012
#endif
3995
4013
@@ -4021,42 +4039,61 @@ static int lfs_dir_finish_removal(lfs_t *lfs, struct lfs_remove_state *p)
4021
4039
#endif
4022
4040
4023
4041
#ifndef LFS_READONLY
4024
- static int lfs_remove_ (lfs_t * lfs , const char * path , lfs_dir_prep_helper_t helper ) {
4042
+ static int lfs_remove_ (lfs_t * lfs , const char * path , lfs_dir_prep_helper_t helper , bool recursive ) {
4043
+ // For the recursive flag to work, we must have the helper defined.
4044
+ if (recursive && NULL == helper ) {
4045
+ return LFS_ERR_INVAL ;
4046
+ }
4047
+
4025
4048
// deorphan if we haven't yet, needed at most once after poweron
4026
4049
int err = lfs_fs_forceconsistency (lfs );
4027
4050
if (err ) {
4028
4051
return err ;
4029
4052
}
4030
4053
4054
+ bool at_toplevel = true;
4031
4055
struct lfs_remove_state s ;
4032
- s .tag = lfs_dir_find (lfs , & s .cwd , & path , NULL );
4033
- if (s .tag < 0 || lfs_tag_id (s .tag ) == 0x3ff ) {
4034
- return (s .tag < 0 ) ? (int )s .tag : LFS_ERR_INVAL ;
4035
- }
4056
+ do {
4057
+ // Since we want to keep our in-memory usage minimal, we rely on
4058
+ // the disk structure to "recurse". We'll go back to the top
4059
+ // level path every time we need to find a new leaf. Technically
4060
+ // we could remember the top-level path state so we don't have
4061
+ // to search again for this folder entry...
4062
+ at_toplevel = true;
4063
+ s .tag = lfs_dir_find (lfs , & s .cwd , & path , NULL );
4064
+ if (s .tag < 0 || lfs_tag_id (s .tag ) == 0x3ff ) {
4065
+ return (s .tag < 0 ) ? (int )s .tag : LFS_ERR_INVAL ;
4066
+ }
4067
+
4068
+ s .dir .next = lfs -> mlist ;
4069
+ if (lfs_tag_type3 (s .tag ) == LFS_TYPE_DIR ) {
4070
+ err = lfs_dir_prep_removal (lfs , & s , helper , recursive );
4071
+ if (err == 1 ) {
4072
+ // We had to switch folders and we're no longer in the top-level folder.
4073
+ // This means we need to go back from the top after we finish the
4074
+ // removal process.
4075
+ at_toplevel = false;
4076
+ } else if (err < 0 ) {
4077
+ return err ;
4078
+ }
4079
+ }
4036
4080
4037
- s .dir .next = lfs -> mlist ;
4038
- if (lfs_tag_type3 (s .tag ) == LFS_TYPE_DIR ) {
4039
- err = lfs_dir_prep_removal (lfs , & s , helper );
4040
- if (err < 0 ) {
4081
+ // delete the entry
4082
+ err = lfs_dir_commit (lfs , & s .cwd , LFS_MKATTRS (
4083
+ {LFS_MKTAG (LFS_TYPE_DELETE , lfs_tag_id (s .tag ), 0 ), NULL }));
4084
+ if (err ) {
4085
+ lfs -> mlist = s .dir .next ;
4041
4086
return err ;
4042
4087
}
4043
- }
4044
4088
4045
- // delete the entry
4046
- err = lfs_dir_commit (lfs , & s .cwd , LFS_MKATTRS (
4047
- {LFS_MKTAG (LFS_TYPE_DELETE , lfs_tag_id (s .tag ), 0 ), NULL }));
4048
- if (err ) {
4049
4089
lfs -> mlist = s .dir .next ;
4050
- return err ;
4051
- }
4052
-
4053
- lfs -> mlist = s .dir .next ;
4054
- if (lfs_tag_type3 (s .tag ) == LFS_TYPE_DIR ) {
4055
- err = lfs_dir_finish_removal (lfs , & s );
4056
- if (err ) {
4057
- return err ;
4090
+ if (lfs_tag_type3 (s .tag ) == LFS_TYPE_DIR ) {
4091
+ err = lfs_dir_finish_removal (lfs , & s );
4092
+ if (err ) {
4093
+ return err ;
4094
+ }
4058
4095
}
4059
- }
4096
+ } while (! at_toplevel );
4060
4097
4061
4098
return 0 ;
4062
4099
}
@@ -4119,7 +4156,7 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath,
4119
4156
// we're renaming to ourselves??
4120
4157
return 0 ;
4121
4158
} else if (lfs_tag_type3 (n .tag ) == LFS_TYPE_DIR ) {
4122
- err = lfs_dir_prep_removal (lfs , & n , helper );
4159
+ err = lfs_dir_prep_removal (lfs , & n , helper , false );
4123
4160
if (err ) {
4124
4161
return err ;
4125
4162
}
@@ -6089,14 +6126,30 @@ int lfs_remove(lfs_t *lfs, const char *path) {
6089
6126
}
6090
6127
LFS_TRACE ("lfs_remove(%p, \"%s\")" , (void * )lfs , path );
6091
6128
6092
- err = lfs_remove_ (lfs , path , NULL );
6129
+ err = lfs_remove_ (lfs , path , NULL , false );
6093
6130
6094
6131
LFS_TRACE ("lfs_remove -> %d" , err );
6095
6132
LFS_UNLOCK (lfs -> cfg );
6096
6133
return err ;
6097
6134
}
6098
6135
#endif
6099
6136
6137
+ #ifndef LFS_READONLY
6138
+ int lfs_remove_recursive (lfs_t * lfs , const char * path ) {
6139
+ int err = LFS_LOCK (lfs -> cfg );
6140
+ if (err ) {
6141
+ return err ;
6142
+ }
6143
+ LFS_TRACE ("lfs_remove_recursive(%p, \"%s\")" , (void * )lfs , path );
6144
+
6145
+ err = lfs_remove_ (lfs , path , lfs_dir_prep_remove_nonempty_folders , true);
6146
+
6147
+ LFS_TRACE ("lfs_remove_recursive -> %d" , err );
6148
+ LFS_UNLOCK (lfs -> cfg );
6149
+ return err ;
6150
+ }
6151
+ #endif
6152
+
6100
6153
#ifndef LFS_READONLY
6101
6154
int lfs_atomic_remove (lfs_t * lfs , const char * path ) {
6102
6155
int err = LFS_LOCK (lfs -> cfg );
@@ -6107,7 +6160,7 @@ int lfs_atomic_remove(lfs_t *lfs, const char *path) {
6107
6160
6108
6161
// Note: We pass in a helper pointer here so that this extra
6109
6162
// logic can be dropped if it is never referenced
6110
- err = lfs_remove_ (lfs , path , lfs_dir_prep_remove_nonempty_folders );
6163
+ err = lfs_remove_ (lfs , path , lfs_dir_prep_remove_nonempty_folders , false );
6111
6164
6112
6165
LFS_TRACE ("lfs_atomic_remove -> %d" , err );
6113
6166
LFS_UNLOCK (lfs -> cfg );
0 commit comments