@@ -608,22 +608,34 @@ impl<OC: ObjectClient + Send + Sync + Clone> Metablock for Superblock<OC> {
608608 return Err ( InodeError :: IsDirectory ( inode. err ( ) ) ) ;
609609 }
610610
611- let write_status = {
612- let inode_state = inode. get_inode_state ( ) ?;
613- inode_state. write_status
614- } ;
611+ let inode_state = inode. get_mut_inode_state ( ) ?;
615612
616- match write_status {
617- WriteStatus :: LocalUnopened | WriteStatus :: LocalOpen | WriteStatus :: PendingRename => {
613+ match inode_state . write_status {
614+ WriteStatus :: LocalOpen | WriteStatus :: PendingRename => {
618615 // In the future, we may permit `unlink` and cancel any in-flight uploads.
619616 warn ! (
620617 parent = parent_ino,
621618 ?name,
619+ write_status = ?inode_state. write_status,
622620 "unlink on local file not allowed until write is complete" ,
623621 ) ;
624622 return Err ( InodeError :: UnlinkNotPermittedWhileWriting ( inode. err ( ) ) ) ;
625623 }
624+ WriteStatus :: LocalUnopened => {
625+ debug ! (
626+ parent = parent_ino,
627+ ?name,
628+ "unlink on LocalUnopened file, no s3 cleanup needed" ,
629+ ) ;
630+
631+ let mut parent_state = parent. get_mut_inode_state ( ) ?;
632+ parent_state. kind_data . remove_child ( inode. name ( ) , inode. ino ( ) ) ;
633+
634+ return Ok ( ( ) ) ;
635+ }
626636 WriteStatus :: Remote => {
637+ drop ( inode_state) ;
638+
627639 let bucket = & self . inner . s3_path . bucket ;
628640 let s3_key = self . inner . full_key_for_inode ( & inode) ;
629641 debug ! ( parent=?parent_ino, ?name, "unlink on remote file will delete key {}" , s3_key) ;
@@ -647,30 +659,7 @@ impl<OC: ObjectClient + Send + Sync + Clone> Metablock for Superblock<OC> {
647659 // The Linux Kernel's VFS should lock both the parent and child (https://www.kernel.org/doc/html/next/filesystems/directory-locking.html).
648660 // However, the superblock is still subject to concurrent changes as we don't hold this lock over remote calls.
649661 let mut parent_state = parent. get_mut_inode_state ( ) ?;
650- match & mut parent_state. kind_data {
651- InodeKindData :: File { .. } => {
652- debug_assert ! ( false , "inodes never change kind" ) ;
653- return Err ( InodeError :: NotADirectory ( parent. err ( ) ) ) ;
654- }
655- InodeKindData :: Directory { children, .. } => {
656- // We want to remove the original child.
657- // If there is a mismatch in inode number between the existing inode and the deleted inode, we invalidate the existing inode's stat.
658- // If for some reason the child with the specified name was already removed, we do nothing as this is the desired state.
659- if let Some ( existing_inode) = children. get ( inode. name ( ) ) {
660- if existing_inode. ino ( ) == inode. ino ( ) {
661- children. remove ( inode. name ( ) ) ;
662- } else {
663- // Mismatch in inode number, thus the existing inode might not be the same one we deleted
664- let mut state = existing_inode. get_mut_inode_state_no_check ( ) ;
665-
666- // Invalidate the inode's stats so we refresh them from S3 when next queried
667- state. stat . update_validity ( Duration :: from_secs ( 0 ) ) ;
668- }
669- } else {
670- debug ! ( "parent did not contain child after deletion during unlink" ) ;
671- }
672- }
673- } ;
662+ parent_state. kind_data . remove_child ( inode. name ( ) , inode. ino ( ) ) ;
674663
675664 Ok ( ( ) )
676665 }
0 commit comments