@@ -3884,6 +3884,9 @@ struct lfs_remove_state
38843884 lfs_mdir_t cwd ;
38853885 lfs_stag_t tag ;
38863886
3887+ // Global state accumulations from a split folder
3888+ lfs_gstate_t gstate ;
3889+
38873890 // Directory that needs to be removed from linked list pointer
38883891 struct lfs_mlist dir ;
38893892
@@ -3892,10 +3895,61 @@ struct lfs_remove_state
38923895 // the directory.
38933896 lfs_block_t dir_head [2 ];
38943897};
3898+ typedef int (* lfs_dir_prep_helper_t )(lfs_t * , struct lfs_remove_state * );
38953899#endif
38963900
38973901#ifndef LFS_READONLY
3898- static int lfs_dir_prep_removal (lfs_t * lfs , struct lfs_remove_state * p ) {
3902+ static int lfs_dir_prep_remove_nonempty_folders (lfs_t * lfs , struct lfs_remove_state * p ) {
3903+ uint16_t id = 0 ;
3904+
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.
3911+ while (true) {
3912+ if (p -> dir .m .count == id ) {
3913+ if (!p -> dir .m .split ) {
3914+ // We have iterated through the folder to the last
3915+ // tag.
3916+ break ;
3917+ }
3918+
3919+ // Before we fetch the next block, update our view of gstate
3920+ lfs_dir_getgstate (lfs , & p -> dir .m , & p -> gstate );
3921+
3922+ int err = lfs_dir_fetch (lfs , & p -> dir .m , p -> dir .m .tail );
3923+ if (err ) {
3924+ return err ;
3925+ }
3926+
3927+ id = 0 ;
3928+ }
3929+
3930+ char name [lfs -> name_max + 1 ];
3931+ lfs_stag_t tag = lfs_dir_get (lfs , & p -> dir .m , LFS_MKTAG (0x780 , 0x3ff , 0 ),
3932+ LFS_MKTAG (LFS_TYPE_NAME , id , lfs -> name_max + 1 ), name );
3933+ if (tag < 0 ) {
3934+ return tag ;
3935+ }
3936+
3937+ if (lfs_tag_type3 (tag ) == LFS_TYPE_DIR ) {
3938+ // We're not allowed to find leafs
3939+ return LFS_ERR_NOTEMPTY ;
3940+ }
3941+
3942+ id += 1 ;
3943+ }
3944+
3945+ return 0 ;
3946+ }
3947+ #endif
3948+
3949+ #ifndef LFS_READONLY
3950+ static int lfs_dir_prep_removal (lfs_t * lfs , struct lfs_remove_state * p ,
3951+ lfs_dir_prep_helper_t helper ) {
3952+
38993953 lfs_stag_t res = lfs_dir_get (lfs , & p -> cwd , LFS_MKTAG (0x700 , 0x3ff , 0 ),
39003954 LFS_MKTAG (LFS_TYPE_STRUCT , lfs_tag_id (p -> tag ), 8 ), p -> dir_head );
39013955 if (res < 0 ) {
@@ -3908,8 +3962,19 @@ static int lfs_dir_prep_removal(lfs_t *lfs, struct lfs_remove_state *p) {
39083962 return err ;
39093963 }
39103964
3965+ memset (& p -> gstate , 0 , sizeof (p -> gstate ));
3966+
39113967 if (p -> dir .m .count > 0 || p -> dir .m .split ) {
3912- return LFS_ERR_NOTEMPTY ;
3968+ // Normal POSIX behavior wouldn't allow a non-empty
3969+ // folder to be removed/renamed into in this manner
3970+ if (NULL == helper ) {
3971+ return LFS_ERR_NOTEMPTY ;
3972+ }
3973+
3974+ err = helper (lfs , p );
3975+ if (err ) {
3976+ return err ;
3977+ }
39133978 }
39143979
39153980 // mark fs as orphaned
@@ -3937,11 +4002,15 @@ static int lfs_dir_finish_removal(lfs_t *lfs, struct lfs_remove_state *p)
39374002 return err ;
39384003 }
39394004
3940- err = lfs_fs_pred (lfs , p -> dir . m . pair , & p -> cwd );
4005+ err = lfs_fs_pred (lfs , p -> dir_head , & p -> cwd );
39414006 if (err ) {
39424007 return err ;
39434008 }
39444009
4010+ // Merge in gstate from first block splits within the directory;
4011+ // lfs_dir_drop will pick up the last gstate entry.
4012+ lfs_gstate_xor (& lfs -> gdelta , & p -> gstate );
4013+
39454014 err = lfs_dir_drop (lfs , & p -> cwd , & p -> dir .m );
39464015 if (err ) {
39474016 return err ;
@@ -3952,7 +4021,7 @@ static int lfs_dir_finish_removal(lfs_t *lfs, struct lfs_remove_state *p)
39524021#endif
39534022
39544023#ifndef LFS_READONLY
3955- static int lfs_remove_ (lfs_t * lfs , const char * path ) {
4024+ static int lfs_remove_ (lfs_t * lfs , const char * path , lfs_dir_prep_helper_t helper ) {
39564025 // deorphan if we haven't yet, needed at most once after poweron
39574026 int err = lfs_fs_forceconsistency (lfs );
39584027 if (err ) {
@@ -3967,7 +4036,7 @@ static int lfs_remove_(lfs_t *lfs, const char *path) {
39674036
39684037 s .dir .next = lfs -> mlist ;
39694038 if (lfs_tag_type3 (s .tag ) == LFS_TYPE_DIR ) {
3970- err = lfs_dir_prep_removal (lfs , & s );
4039+ err = lfs_dir_prep_removal (lfs , & s , helper );
39714040 if (err < 0 ) {
39724041 return err ;
39734042 }
@@ -3994,7 +4063,8 @@ static int lfs_remove_(lfs_t *lfs, const char *path) {
39944063#endif
39954064
39964065#ifndef LFS_READONLY
3997- static int lfs_rename_ (lfs_t * lfs , const char * oldpath , const char * newpath ) {
4066+ static int lfs_rename_ (lfs_t * lfs , const char * oldpath , const char * newpath ,
4067+ lfs_dir_prep_helper_t helper ) {
39984068 // deorphan if we haven't yet, needed at most once after poweron
39994069 int err = lfs_fs_forceconsistency (lfs );
40004070 if (err ) {
@@ -4049,7 +4119,7 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
40494119 // we're renaming to ourselves??
40504120 return 0 ;
40514121 } else if (lfs_tag_type3 (n .tag ) == LFS_TYPE_DIR ) {
4052- err = lfs_dir_prep_removal (lfs , & n );
4122+ err = lfs_dir_prep_removal (lfs , & n , helper );
40534123 if (err ) {
40544124 return err ;
40554125 }
@@ -6019,14 +6089,32 @@ int lfs_remove(lfs_t *lfs, const char *path) {
60196089 }
60206090 LFS_TRACE ("lfs_remove(%p, \"%s\")" , (void * )lfs , path );
60216091
6022- err = lfs_remove_ (lfs , path );
6092+ err = lfs_remove_ (lfs , path , NULL );
60236093
60246094 LFS_TRACE ("lfs_remove -> %d" , err );
60256095 LFS_UNLOCK (lfs -> cfg );
60266096 return err ;
60276097}
60286098#endif
60296099
6100+ #ifndef LFS_READONLY
6101+ int lfs_atomic_remove (lfs_t * lfs , const char * path ) {
6102+ int err = LFS_LOCK (lfs -> cfg );
6103+ if (err ) {
6104+ return err ;
6105+ }
6106+ LFS_TRACE ("lfs_atomic_remove(%p, \"%s\")" , (void * )lfs , path );
6107+
6108+ // Note: We pass in a helper pointer here so that this extra
6109+ // logic can be dropped if it is never referenced
6110+ err = lfs_remove_ (lfs , path , lfs_dir_prep_remove_nonempty_folders );
6111+
6112+ LFS_TRACE ("lfs_atomic_remove -> %d" , err );
6113+ LFS_UNLOCK (lfs -> cfg );
6114+ return err ;
6115+ }
6116+ #endif
6117+
60306118#ifndef LFS_READONLY
60316119int lfs_rename (lfs_t * lfs , const char * oldpath , const char * newpath ) {
60326120 int err = LFS_LOCK (lfs -> cfg );
@@ -6035,14 +6123,32 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
60356123 }
60366124 LFS_TRACE ("lfs_rename(%p, \"%s\", \"%s\")" , (void * )lfs , oldpath , newpath );
60376125
6038- err = lfs_rename_ (lfs , oldpath , newpath );
6126+ err = lfs_rename_ (lfs , oldpath , newpath , NULL );
60396127
60406128 LFS_TRACE ("lfs_rename -> %d" , err );
60416129 LFS_UNLOCK (lfs -> cfg );
60426130 return err ;
60436131}
60446132#endif
60456133
6134+ #ifndef LFS_READONLY
6135+ int lfs_atomic_rename (lfs_t * lfs , const char * oldpath , const char * newpath ) {
6136+ int err = LFS_LOCK (lfs -> cfg );
6137+ if (err ) {
6138+ return err ;
6139+ }
6140+ LFS_TRACE ("lfs_atomic_rename(%p, \"%s\", \"%s\")" , (void * )lfs , oldpath , newpath );
6141+
6142+ // Note: We pass in a helper pointer here so that this extra
6143+ // logic can be dropped if it is never referenced
6144+ err = lfs_rename_ (lfs , oldpath , newpath , lfs_dir_prep_remove_nonempty_folders );
6145+
6146+ LFS_TRACE ("lfs_atomic_rename -> %d" , err );
6147+ LFS_UNLOCK (lfs -> cfg );
6148+ return err ;
6149+ }
6150+ #endif
6151+
60466152int lfs_stat (lfs_t * lfs , const char * path , struct lfs_info * info ) {
60476153 int err = LFS_LOCK (lfs -> cfg );
60486154 if (err ) {
0 commit comments