@@ -190,9 +190,20 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
190
190
/// Writes a file to disk atomically.
191
191
///
192
192
/// This uses `tempfile::persist` to accomplish atomic writes.
193
+ /// If the path is a symlink, it will follow the symlink and write to the actual target.
193
194
pub fn write_atomic < P : AsRef < Path > , C : AsRef < [ u8 ] > > ( path : P , contents : C ) -> Result < ( ) > {
194
195
let path = path. as_ref ( ) ;
195
196
197
+ // Check if the path is a symlink and follow it if it is
198
+ let resolved_path;
199
+ let path = if path. is_symlink ( ) {
200
+ resolved_path = fs:: read_link ( path)
201
+ . with_context ( || format ! ( "failed to read symlink at `{}`" , path. display( ) ) ) ?;
202
+ & resolved_path
203
+ } else {
204
+ path
205
+ } ;
206
+
196
207
// On unix platforms, get the permissions of the original file. Copy only the user/group/other
197
208
// read/write/execute permission bits. The tempfile lib defaults to an initial mode of 0o600,
198
209
// and we'll set the proper permissions after creating the file.
@@ -983,6 +994,34 @@ mod tests {
983
994
}
984
995
}
985
996
997
+
998
+ #[ test]
999
+ fn write_atomic_symlink ( ) {
1000
+ let tmpdir = tempfile:: tempdir ( ) . unwrap ( ) ;
1001
+ let target_path = tmpdir. path ( ) . join ( "target.txt" ) ;
1002
+ let symlink_path = tmpdir. path ( ) . join ( "symlink.txt" ) ;
1003
+
1004
+ // Create initial file
1005
+ write ( & target_path, "initial" ) . unwrap ( ) ;
1006
+
1007
+ // Create symlink
1008
+ #[ cfg( unix) ]
1009
+ std:: os:: unix:: fs:: symlink ( & target_path, & symlink_path) . unwrap ( ) ;
1010
+ #[ cfg( windows) ]
1011
+ std:: os:: windows:: fs:: symlink_file ( & target_path, & symlink_path) . unwrap ( ) ;
1012
+
1013
+ // Write through symlink
1014
+ write_atomic ( & symlink_path, "updated" ) . unwrap ( ) ;
1015
+
1016
+ // Verify both paths show the updated content
1017
+ assert_eq ! ( std:: fs:: read_to_string( & target_path) . unwrap( ) , "updated" ) ;
1018
+ assert_eq ! ( std:: fs:: read_to_string( & symlink_path) . unwrap( ) , "updated" ) ;
1019
+
1020
+ // Verify symlink still exists and points to the same target
1021
+ assert ! ( symlink_path. is_symlink( ) ) ;
1022
+ assert_eq ! ( std:: fs:: read_link( & symlink_path) . unwrap( ) , target_path) ;
1023
+ }
1024
+
986
1025
#[ test]
987
1026
#[ cfg( windows) ]
988
1027
fn test_remove_symlink_dir ( ) {
0 commit comments