@@ -748,82 +748,75 @@ impl EnvUniversal {
748
748
// Write our file contents.
749
749
// \return true on success, false on failure.
750
750
fn save ( & mut self , directory : & wstr ) -> bool {
751
+ use crate :: common:: ScopeGuard ;
751
752
assert ! ( self . ok_to_save, "It's not OK to save" ) ;
752
753
753
- let mut private_file_path = WString :: new ( ) ;
754
-
755
754
// Open adjacent temporary file.
755
+ let mut private_file_path = WString :: new ( ) ;
756
756
let private_fd = self . open_temporary_file ( directory, & mut private_file_path) ;
757
+ // unlink pfp upon failure. In case of success, it (already) won't exist.
758
+ let delete_pfp = ScopeGuard :: new ( private_file_path, |path| {
759
+ wunlink ( path) ;
760
+ } ) ;
761
+ let private_file_path = & delete_pfp;
757
762
758
763
// Write to it.
759
- let mut success = self . write_to_fd ( private_fd. as_raw_fd ( ) , & private_file_path) ;
760
- if !success {
764
+ if !self . write_to_fd ( private_fd. as_raw_fd ( ) , & private_file_path) {
761
765
FLOG ! ( uvar_file, "universal log write_to_fd() failed" ) ;
766
+ return false ;
762
767
}
763
768
764
- if success {
765
- let real_path = wrealpath ( & self . vars_path ) . unwrap_or_else ( || self . vars_path . clone ( ) ) ;
769
+ let real_path = wrealpath ( & self . vars_path ) . unwrap_or_else ( || self . vars_path . clone ( ) ) ;
766
770
767
- // Ensure we maintain ownership and permissions (#2176).
768
- // let mut sbuf : libc::stat = MaybeUninit::uninit();
769
- if let Ok ( md) = wstat ( & real_path) {
770
- if unsafe { libc:: fchown ( private_fd. as_raw_fd ( ) , md. uid ( ) , md. gid ( ) ) } == -1 {
771
- FLOG ! ( uvar_file, "universal log fchown() failed" ) ;
772
- }
773
- #[ allow( clippy:: useless_conversion) ]
774
- let mode: libc:: mode_t = md. mode ( ) . try_into ( ) . unwrap ( ) ;
775
- if unsafe { libc:: fchmod ( private_fd. as_raw_fd ( ) , mode) } == -1 {
776
- FLOG ! ( uvar_file, "universal log fchmod() failed" ) ;
777
- }
771
+ // Ensure we maintain ownership and permissions (#2176).
772
+ // let mut sbuf : libc::stat = MaybeUninit::uninit();
773
+ if let Ok ( md) = wstat ( & real_path) {
774
+ if unsafe { libc:: fchown ( private_fd. as_raw_fd ( ) , md. uid ( ) , md. gid ( ) ) } == -1 {
775
+ FLOG ! ( uvar_file, "universal log fchown() failed" ) ;
778
776
}
779
-
780
- // Linux by default stores the mtime with low precision, low enough that updates that occur
781
- // in quick succession may result in the same mtime (even the nanoseconds field). So
782
- // manually set the mtime of the new file to a high-precision clock. Note that this is only
783
- // necessary because Linux aggressively reuses inodes, causing the ABA problem; on other
784
- // platforms we tend to notice the file has changed due to a different inode (or file size!)
785
- //
786
- // The current time within the Linux kernel is cached, and generally only updated on a timer
787
- // interrupt. So if the timer interrupt is running at 10 milliseconds, the cached time will
788
- // only be updated once every 10 milliseconds.
789
- //
790
- // It's probably worth finding a simpler solution to this. The tests ran into this, but it's
791
- // unlikely to affect users.
792
- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
793
- {
794
- let mut times: [ libc:: timespec ; 2 ] = unsafe { std:: mem:: zeroed ( ) } ;
795
- times[ 0 ] . tv_nsec = libc:: UTIME_OMIT ; // don't change ctime
796
- if unsafe { libc:: clock_gettime ( libc:: CLOCK_REALTIME , & mut times[ 1 ] ) } != 0 {
797
- unsafe {
798
- libc:: futimens ( private_fd. as_raw_fd ( ) , & times[ 0 ] ) ;
799
- }
800
- }
777
+ #[ allow( clippy:: useless_conversion) ]
778
+ let mode: libc:: mode_t = md. mode ( ) . try_into ( ) . unwrap ( ) ;
779
+ if unsafe { libc:: fchmod ( private_fd. as_raw_fd ( ) , mode) } == -1 {
780
+ FLOG ! ( uvar_file, "universal log fchmod() failed" ) ;
801
781
}
782
+ }
802
783
803
- // Apply new file.
804
- success = self . move_new_vars_file_into_place ( & private_file_path, & real_path) ;
805
- if !success {
806
- FLOG ! (
807
- uvar_file,
808
- "universal log move_new_vars_file_into_place() failed"
809
- ) ;
784
+ // Linux by default stores the mtime with low precision, low enough that updates that occur
785
+ // in quick succession may result in the same mtime (even the nanoseconds field). So
786
+ // manually set the mtime of the new file to a high-precision clock. Note that this is only
787
+ // necessary because Linux aggressively reuses inodes, causing the ABA problem; on other
788
+ // platforms we tend to notice the file has changed due to a different inode (or file size!)
789
+ //
790
+ // The current time within the Linux kernel is cached, and generally only updated on a timer
791
+ // interrupt. So if the timer interrupt is running at 10 milliseconds, the cached time will
792
+ // only be updated once every 10 milliseconds.
793
+ //
794
+ // It's probably worth finding a simpler solution to this. The tests ran into this, but it's
795
+ // unlikely to affect users.
796
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
797
+ {
798
+ let mut times: [ libc:: timespec ; 2 ] = unsafe { std:: mem:: zeroed ( ) } ;
799
+ times[ 0 ] . tv_nsec = libc:: UTIME_OMIT ; // don't change ctime
800
+ if unsafe { libc:: clock_gettime ( libc:: CLOCK_REALTIME , & mut times[ 1 ] ) } != 0 {
801
+ unsafe {
802
+ libc:: futimens ( private_fd. as_raw_fd ( ) , & times[ 0 ] ) ;
803
+ }
810
804
}
811
805
}
812
806
813
- if success {
814
- // Since we moved the new file into place, clear the path so we don't try to unlink it.
815
- private_file_path. clear ( ) ;
807
+ // Apply new file.
808
+ if !self . move_new_vars_file_into_place ( & private_file_path, & real_path) {
809
+ FLOG ! (
810
+ uvar_file,
811
+ "universal log move_new_vars_file_into_place() failed"
812
+ ) ;
813
+ return false ;
816
814
}
817
815
818
- // Clean up.
819
- if !private_file_path. is_empty ( ) {
820
- wunlink ( & private_file_path) ;
821
- }
822
- if success {
823
- // All of our modified variables have now been written out.
824
- self . modified . clear ( ) ;
825
- }
826
- success
816
+ // Success at last. All of our modified variables have now been written out.
817
+ self . modified . clear ( ) ;
818
+ ScopeGuard :: cancel ( delete_pfp) ;
819
+ true
827
820
}
828
821
}
829
822
0 commit comments