@@ -3903,11 +3903,7 @@ static int lfs_dir_prep_remove_nonempty_folders(lfs_t *lfs, struct lfs_remove_st
39033903 uint16_t id = 0 ;
39043904
39053905 // 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.
39113907 while (true) {
39123908 if (p -> dir .m .count == id ) {
39133909 if (!p -> dir .m .split ) {
@@ -3935,7 +3931,11 @@ static int lfs_dir_prep_remove_nonempty_folders(lfs_t *lfs, struct lfs_remove_st
39353931 }
39363932
39373933 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 ;
39393939 return LFS_ERR_NOTEMPTY ;
39403940 }
39413941
@@ -3947,10 +3947,15 @@ static int lfs_dir_prep_remove_nonempty_folders(lfs_t *lfs, struct lfs_remove_st
39473947#endif
39483948
39493949#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.
39503952static 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 ;
39523956
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 ),
39543959 LFS_MKTAG (LFS_TYPE_STRUCT , lfs_tag_id (p -> tag ), 8 ), p -> dir_head );
39553960 if (res < 0 ) {
39563961 return (int )res ;
@@ -3972,7 +3977,20 @@ static int lfs_dir_prep_removal(lfs_t *lfs, struct lfs_remove_state *p,
39723977 }
39733978
39743979 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+ {
39763994 return err ;
39773995 }
39783996 }
@@ -3989,7 +4007,7 @@ static int lfs_dir_prep_removal(lfs_t *lfs, struct lfs_remove_state *p,
39894007 p -> dir .id = 0 ;
39904008 lfs -> mlist = & p -> dir ;
39914009
3992- return 0 ;
4010+ return depth != 0 ;
39934011}
39944012#endif
39954013
@@ -4021,42 +4039,61 @@ static int lfs_dir_finish_removal(lfs_t *lfs, struct lfs_remove_state *p)
40214039#endif
40224040
40234041#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+
40254048 // deorphan if we haven't yet, needed at most once after poweron
40264049 int err = lfs_fs_forceconsistency (lfs );
40274050 if (err ) {
40284051 return err ;
40294052 }
40304053
4054+ bool at_toplevel = true;
40314055 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+ }
40364080
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 ;
40414086 return err ;
40424087 }
4043- }
40444088
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 ) {
40494089 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+ }
40584095 }
4059- }
4096+ } while (! at_toplevel );
40604097
40614098 return 0 ;
40624099}
@@ -4119,7 +4156,7 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath,
41194156 // we're renaming to ourselves??
41204157 return 0 ;
41214158 } 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 );
41234160 if (err ) {
41244161 return err ;
41254162 }
@@ -6089,14 +6126,30 @@ int lfs_remove(lfs_t *lfs, const char *path) {
60896126 }
60906127 LFS_TRACE ("lfs_remove(%p, \"%s\")" , (void * )lfs , path );
60916128
6092- err = lfs_remove_ (lfs , path , NULL );
6129+ err = lfs_remove_ (lfs , path , NULL , false );
60936130
60946131 LFS_TRACE ("lfs_remove -> %d" , err );
60956132 LFS_UNLOCK (lfs -> cfg );
60966133 return err ;
60976134}
60986135#endif
60996136
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+
61006153#ifndef LFS_READONLY
61016154int lfs_atomic_remove (lfs_t * lfs , const char * path ) {
61026155 int err = LFS_LOCK (lfs -> cfg );
@@ -6107,7 +6160,7 @@ int lfs_atomic_remove(lfs_t *lfs, const char *path) {
61076160
61086161 // Note: We pass in a helper pointer here so that this extra
61096162 // 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 );
61116164
61126165 LFS_TRACE ("lfs_atomic_remove -> %d" , err );
61136166 LFS_UNLOCK (lfs -> cfg );
0 commit comments