@@ -1758,10 +1758,11 @@ pub fn renameW(self: Dir, old_sub_path_w: []const u16, new_sub_path_w: []const u
1758
1758
return posix .renameatW (self .fd , old_sub_path_w , self .fd , new_sub_path_w );
1759
1759
}
1760
1760
1761
- /// Use with `Dir.symLink` and `symLinkAbsolute` to specify whether the symlink
1762
- /// will point to a file or a directory. This value is ignored on all hosts
1763
- /// except Windows where creating symlinks to different resource types, requires
1764
- /// different flags. By default, `symLinkAbsolute` is assumed to point to a file.
1761
+ /// Use with `Dir.symLink`, `Dir.atomicSymLink`, and `symLinkAbsolute` to
1762
+ /// specify whether the symlink will point to a file or a directory. This value
1763
+ /// is ignored on all hosts except Windows where creating symlinks to different
1764
+ /// resource types, requires different flags. By default, `symLinkAbsolute` is
1765
+ /// assumed to point to a file.
1765
1766
pub const SymLinkFlags = struct {
1766
1767
is_directory : bool = false ,
1767
1768
};
@@ -1847,6 +1848,50 @@ pub fn symLinkW(
1847
1848
return windows .CreateSymbolicLink (self .fd , sym_link_path_w , target_path_w , flags .is_directory );
1848
1849
}
1849
1850
1851
+ /// Same as `symLink`, except tries to create the symbolic link until it
1852
+ /// succeeds or encounters an error other than `error.PathAlreadyExists`.
1853
+ /// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1854
+ /// On WASI, both paths should be encoded as valid UTF-8.
1855
+ /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1856
+ pub fn atomicSymLink (
1857
+ dir : Dir ,
1858
+ target_path : []const u8 ,
1859
+ sym_link_path : []const u8 ,
1860
+ flags : SymLinkFlags ,
1861
+ ) ! void {
1862
+ if (dir .symLink (target_path , sym_link_path , flags )) {
1863
+ return ;
1864
+ } else | err | switch (err ) {
1865
+ error .PathAlreadyExists = > {},
1866
+ else = > | e | return e ,
1867
+ }
1868
+
1869
+ const dirname = path .dirname (sym_link_path ) orelse "." ;
1870
+
1871
+ var rand_buf : [AtomicFile .random_bytes_len ]u8 = undefined ;
1872
+
1873
+ const temp_path_len = dirname .len + 1 + base64_encoder .calcSize (rand_buf .len );
1874
+ var temp_path_buf : [fs .max_path_bytes ]u8 = undefined ;
1875
+
1876
+ if (temp_path_len > temp_path_buf .len ) return error .NameTooLong ;
1877
+ @memcpy (temp_path_buf [0.. dirname .len ], dirname );
1878
+ temp_path_buf [dirname .len ] = path .sep ;
1879
+
1880
+ const temp_path = temp_path_buf [0.. temp_path_len ];
1881
+
1882
+ while (true ) {
1883
+ crypto .random .bytes (rand_buf [0.. ]);
1884
+ _ = base64_encoder .encode (temp_path [dirname .len + 1 .. ], rand_buf [0.. ]);
1885
+
1886
+ if (dir .symLink (target_path , temp_path , flags )) {
1887
+ return dir .rename (temp_path , sym_link_path );
1888
+ } else | err | switch (err ) {
1889
+ error .PathAlreadyExists = > continue ,
1890
+ else = > | e | return e ,
1891
+ }
1892
+ }
1893
+ }
1894
+
1850
1895
pub const ReadLinkError = posix .ReadLinkError ;
1851
1896
1852
1897
/// Read value of a symbolic link.
@@ -2695,8 +2740,11 @@ const builtin = @import("builtin");
2695
2740
const std = @import ("../std.zig" );
2696
2741
const File = std .fs .File ;
2697
2742
const AtomicFile = std .fs .AtomicFile ;
2743
+ const base64_encoder = fs .base64_encoder ;
2744
+ const crypto = std .crypto ;
2698
2745
const posix = std .posix ;
2699
2746
const mem = std .mem ;
2747
+ const path = fs .path ;
2700
2748
const fs = std .fs ;
2701
2749
const Allocator = std .mem .Allocator ;
2702
2750
const assert = std .debug .assert ;
0 commit comments