@@ -15,8 +15,6 @@ use std::path::Path;
1515/// Write `contents` to `path` atomically with `file_mode`, ensuring the
1616/// parent directory exists and is set to `dir_mode`.
1717///
18- /// Internally offloads blocking I/O to [`tokio::task::spawn_blocking`].
19- ///
2018/// # Errors
2119///
2220/// Returns an error if `path` has no parent or no file-name component,
@@ -29,41 +27,37 @@ pub async fn write_atomic_restricted(
2927 file_mode : u32 ,
3028 dir_mode : u32 ,
3129) -> io:: Result < ( ) > {
32- let dest = path. as_ref ( ) . to_owned ( ) ;
30+ let path = path. as_ref ( ) . to_owned ( ) ;
3331 let contents = contents. as_ref ( ) . to_vec ( ) ;
34- let path = dest. clone ( ) ;
3532
36- let tmp_path = tokio:: task:: spawn_blocking ( move || {
37- let parent = path. parent ( ) . ok_or_else ( || {
33+ let parent = path
34+ . parent ( )
35+ . ok_or_else ( || {
3836 io:: Error :: new (
3937 io:: ErrorKind :: InvalidInput ,
4038 format ! ( "{} has no parent directory" , path. display( ) ) ,
4139 )
42- } ) ?;
43-
44- create_dir_with_mode ( parent, dir_mode) ?;
45-
46- let file_name = path
47- . file_name ( )
48- . ok_or_else ( || {
49- io:: Error :: new (
50- io:: ErrorKind :: InvalidInput ,
51- format ! ( "{} has no file name" , path. display( ) ) ,
52- )
53- } ) ?
54- . to_os_string ( ) ;
55- let mut tmp_name = file_name;
56- tmp_name. push ( format ! ( ".tmp.{}" , std:: process:: id( ) ) ) ;
57- let tmp_path = parent. join ( & tmp_name) ;
58-
59- write_file_with_mode ( & tmp_path, & contents, file_mode) ?;
60-
61- io:: Result :: Ok ( tmp_path)
62- } )
63- . await
64- . map_err ( io:: Error :: other) ??;
65-
66- let rename_result = atomic_rename_over ( & tmp_path, & dest) . await ;
40+ } ) ?
41+ . to_owned ( ) ;
42+
43+ create_dir_with_mode ( & parent, dir_mode) . await ?;
44+
45+ let file_name = path
46+ . file_name ( )
47+ . ok_or_else ( || {
48+ io:: Error :: new (
49+ io:: ErrorKind :: InvalidInput ,
50+ format ! ( "{} has no file name" , path. display( ) ) ,
51+ )
52+ } ) ?
53+ . to_os_string ( ) ;
54+ let mut tmp_name = file_name;
55+ tmp_name. push ( format ! ( ".tmp.{}" , std:: process:: id( ) ) ) ;
56+ let tmp_path = parent. join ( & tmp_name) ;
57+
58+ write_file_with_mode ( & tmp_path, & contents, file_mode) . await ?;
59+
60+ let rename_result = atomic_rename_over ( & tmp_path, & path) . await ;
6761 if rename_result. is_err ( ) {
6862 let _ = tokio:: fs:: remove_file ( & tmp_path) . await ;
6963 }
@@ -118,45 +112,44 @@ pub async fn remove_file_if_exists(path: impl AsRef<Path>) -> io::Result<()> {
118112// ---------------------------------------------------------------------------
119113
120114#[ cfg( unix) ]
121- fn create_dir_with_mode ( dir : & Path , mode : u32 ) -> io:: Result < ( ) > {
122- use std:: os:: unix:: fs:: { DirBuilderExt , PermissionsExt } ;
123- if dir. exists ( ) {
124- let current = std:: fs:: metadata ( dir) ?. permissions ( ) . mode ( ) & 0o777 ;
125- if current != mode {
126- std:: fs:: set_permissions ( dir, std:: fs:: Permissions :: from_mode ( mode) ) ?;
115+ async fn create_dir_with_mode ( dir : & Path , mode : u32 ) -> io:: Result < ( ) > {
116+ use std:: os:: unix:: fs:: PermissionsExt ;
117+ match tokio:: fs:: metadata ( dir) . await {
118+ Ok ( meta) => {
119+ let current = meta. permissions ( ) . mode ( ) & 0o777 ;
120+ if current != mode {
121+ tokio:: fs:: set_permissions ( dir, std:: fs:: Permissions :: from_mode ( mode) ) . await ?;
122+ }
123+ }
124+ Err ( e) if e. kind ( ) == io:: ErrorKind :: NotFound => {
125+ let mut builder = tokio:: fs:: DirBuilder :: new ( ) ;
126+ builder. recursive ( true ) . mode ( mode) ;
127+ builder. create ( dir) . await ?;
127128 }
128- } else {
129- std:: fs:: DirBuilder :: new ( )
130- . recursive ( true )
131- . mode ( mode)
132- . create ( dir) ?;
129+ Err ( e) => return Err ( e) ,
133130 }
134131 Ok ( ( ) )
135132}
136133
137134#[ cfg( not( unix) ) ]
138- fn create_dir_with_mode ( dir : & Path , _mode : u32 ) -> io:: Result < ( ) > {
139- std :: fs:: create_dir_all ( dir)
135+ async fn create_dir_with_mode ( dir : & Path , _mode : u32 ) -> io:: Result < ( ) > {
136+ tokio :: fs:: create_dir_all ( dir) . await
140137}
141138
142139#[ cfg( unix) ]
143- fn write_file_with_mode ( path : & Path , contents : & [ u8 ] , mode : u32 ) -> io:: Result < ( ) > {
144- use std:: io:: Write ;
145- use std:: os:: unix:: fs:: OpenOptionsExt ;
146- let mut f = std:: fs:: OpenOptions :: new ( )
147- . write ( true )
148- . create ( true )
149- . truncate ( true )
150- . mode ( mode)
151- . open ( path) ?;
152- f. write_all ( contents) ?;
153- f. sync_all ( ) ?;
140+ async fn write_file_with_mode ( path : & Path , contents : & [ u8 ] , mode : u32 ) -> io:: Result < ( ) > {
141+ use tokio:: io:: AsyncWriteExt ;
142+ let mut opts = tokio:: fs:: OpenOptions :: new ( ) ;
143+ opts. write ( true ) . create ( true ) . truncate ( true ) . mode ( mode) ;
144+ let mut f = opts. open ( path) . await ?;
145+ f. write_all ( contents) . await ?;
146+ f. sync_all ( ) . await ?;
154147 Ok ( ( ) )
155148}
156149
157150#[ cfg( not( unix) ) ]
158- fn write_file_with_mode ( path : & Path , contents : & [ u8 ] , _mode : u32 ) -> io:: Result < ( ) > {
159- std :: fs:: write ( path, contents)
151+ async fn write_file_with_mode ( path : & Path , contents : & [ u8 ] , _mode : u32 ) -> io:: Result < ( ) > {
152+ tokio :: fs:: write ( path, contents) . await
160153}
161154
162155#[ cfg( windows) ]
0 commit comments