char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f4a0f5b
main
1use super :: * ; 2 3pub ( super ) fn resolve ( 4caps : & Capabilities , 5label : & str , 6entry : bool , 7write : bool , 8) -> io:: Result < PathTarget > { 9if label. is_empty () { 10return Err ( io:: Error :: new ( 11 io:: ErrorKind :: NotFound , 12"Empty filesystem path" , 13)); 14} 15let path =filesystem_path ( caps, label) ?; 16let target =if entry{ 17PathTarget :: for_entry ( path) 18} else { 19PathTarget :: for_creation ( path) 20}; 21let target =match target{ 22Ok ( target) => target, 23Err ( _) 24if !( if write{ 25 caps. unrestricted_write () 26} else { 27 caps. unrestricted_read () 28}) => 29{ 30return Err ( denied ( label, write)); 31} 32Err ( error) =>return Err ( error), 33}; 34authorize ( caps, & target, label, write) ?; 35Ok ( target) 36} 37 38pub ( super ) fn tree ( 39caps : & Capabilities , 40target : & PathTarget , 41label : & str , 42write : bool , 43) -> io:: Result <()> { 44if write{ 45 caps. check_write_tree ( target) 46} else { 47 caps. check_read_tree ( target) 48} 49. map_err ( |_|{ 50 io:: Error :: new ( 51 io:: ErrorKind :: PermissionDenied , 52format! ( "{} tree {label}" , if write{ "write" } else { "read" }), 53) 54}) 55} 56 57pub ( super ) fn follows_link ( label : & str ) ->bool { 58// A trailing slash names the referent, not the link. 59 label. ends_with ( '/' ) 60 || label. ends_with ( "/." ) 61 ||( cfg! ( windows) &&( label. ends_with ( '\\' ) || label. ends_with ( "\\." ))) 62} 63 64pub ( super ) fn mutable_entry ( target : & PathTarget ) -> io:: Result <( Dir , OsString )> { 65let ( dir, name) =parent ( target. as_path ()) ?; 66if name =="." { 67return Err ( io:: Error :: new ( 68 io:: ErrorKind :: InvalidInput , 69"Cannot mutate a filesystem root" , 70)); 71} 72Ok (( dir, name)) 73} 74 75fn regular ( metadata : & Metadata ) -> io:: Result <()> { 76if metadata. is_file () { 77Ok (()) 78} else { 79Err ( io:: Error :: new ( 80 io:: ErrorKind :: Unsupported , 81"not yet implemented: copying non-regular files" , 82)) 83} 84} 85 86pub ( super ) fn entries ( 87caps : & Capabilities , 88path : & str , 89whole_tree : bool , 90) -> io:: Result < Vec <( String , bool , bool , bool )>> { 91let target =resolve ( caps, path, false , false ) ?; 92if whole_tree{ 93tree ( caps, & target, path, false ) ?; 94} 95let ( parent, name) =parent ( target. as_path ()) ?; 96let directory = parent. open_dir_nofollow ( name) ?; 97let mut entries =Vec :: new (); 98for entryin directory. entries () ?{ 99let entry = entry?; 100let name = entry. file_name (); 101if !whole_tree && caps. check_read ( & target. child ( & name) ?). is_err () { 102continue ; 103} 104let name = name. into_string (). map_err ( |_|{ 105 io:: Error :: new ( 106 io:: ErrorKind :: Unsupported , 107"not yet implemented: non-UTF-8 directory entries" , 108) 109}) ?; 110let kind = entry. file_type () ?; 111 entries. push (( name, kind. is_file (), kind. is_dir (), kind. is_symlink ())); 112} 113Ok ( entries) 114} 115 116pub ( super ) fn ops ( files : Arc < Files >) ->Vec < Op > { 117let ops = files. clone (); 118let read_dir = files. clone (); 119let mkdir = files. clone (); 120let remove = files. clone (); 121let rename = files. clone (); 122vec! [ 123 ops. io_ops ( "op_file_read_dir" , "op_file_read_dir_sync" , move |path : String |{ 124let caps = read_dir. caps . clone (); 125let pending = read_dir. pool . run ( move ||entries ( & caps, & path, false )); 126async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 127}), 128 ops. io_ops ( "op_file_mkdir" , "op_file_mkdir_sync" , 129move |( path, mode): ( String , Option < u32 >) |{ 130let caps = mkdir. caps . clone (); 131let pending = mkdir. pool . run ( move ||{ 132if mode. is_some_and ( |mode| mode >0o7777 ) { 133return Err ( io:: Error :: new ( 134 io:: ErrorKind :: InvalidInput , 135"Invalid directory mode" , 136)); 137} 138let target =resolve ( & caps, & path, true , true ) ?; 139let ( directory, name) =mutable_entry ( & target) ?; 140let mut builder = cap_std:: fs:: DirBuilder :: new (); 141# [ cfg ( unix )] 142{ 143use cap_std:: fs:: DirBuilderExt ; 144if let Some ( mode) = mode{ 145 builder. mode ( mode); 146} 147} 148authorize ( & caps, & target, & path, true ) ?; 149 directory. create_dir_with ( name, & builder) ?; 150Ok ( true ) 151}); 152async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 153}, 154), 155 ops. io_ops ( "op_file_remove" , "op_file_remove_sync" , 156move |( path, recursive): ( String , bool ) |{ 157let caps = remove. caps . clone (); 158let pending = remove. pool . run ( move ||{ 159let target =resolve ( & caps, & path, true , true ) ?; 160if recursive{ 161tree ( & caps, & target, & path, true ) ?; 162} 163let ( directory, name) =mutable_entry ( & target) ?; 164let metadata = directory. symlink_metadata ( & name) ?; 165authorize ( & caps, & target, & path, true ) ?; 166if metadata. is_dir () { 167if recursive{ 168 directory. remove_dir_all ( name) ?; 169} else { 170 directory. remove_dir ( name) ?; 171} 172} else { 173 directory. remove_file_or_symlink ( name) ?; 174} 175Ok ( true ) 176}); 177async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 178}, 179), 180 ops. io_ops ( "op_file_rename" , "op_file_rename_sync" , 181move |( source, destination): ( String , String ) |{ 182let caps = rename. caps . clone (); 183let pending = rename. pool . run ( move ||{ 184let _guard =super :: links:: MUTATIONS 185. lock () 186. unwrap_or_else ( std:: sync:: PoisonError :: into_inner); 187let from =resolve ( & caps, & source, true , false ) ?; 188let to =resolve ( & caps, & destination, true , true ) ?; 189// rename cannot atomically insist that a concurrently replaced leaf is a file. 190tree ( & caps, & from, & source, false ) ?; 191tree ( & caps, & from, & source, true ) ?; 192tree ( & caps, & to, & destination, true ) ?; 193let ( from_dir, from_name) =mutable_entry ( & from) ?; 194let ( to_dir, to_name) =mutable_entry ( & to) ?; 195let metadata = from_dir. symlink_metadata ( & from_name) ?; 196if metadata. is_symlink () { 197let text = from_dir. read_link_contents ( & from_name) ?; 198super :: links:: authorize_target ( & caps, & to, & text, & destination) ?; 199} else if metadata. is_dir () 200 && !( caps. unrestricted_read () && caps. unrestricted_write ()) 201 &&super :: links:: contains_links ( from_dir. open_dir_nofollow ( & from_name) ?) ? 202{ 203return Err ( io:: Error :: new ( 204 io:: ErrorKind :: Unsupported , 205"not yet implemented: rebasing directory symlinks with scoped permissions" , 206)); 207} 208 from_dir. rename ( from_name, & to_dir, to_name) ?; 209Ok ( true ) 210}); 211async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 212}, 213), 214 ops. io_ops ( "op_file_copy" , "op_file_copy_sync" , 215move |( source, destination): ( String , String ) |{ 216let caps = files. caps . clone (); 217let pending = files. pool . run ( move ||{ 218let from =resolve ( & caps, & source, false , false ) ?; 219let to =resolve ( & caps, & destination, false , true ) ?; 220let ( from_dir, from_name) =parent ( from. as_path ()) ?; 221regular ( & from_dir. symlink_metadata ( & from_name) ?) ?; 222let mut read_options =OpenOptions :: new (); 223 read_options 224. read ( true ) 225. follow ( FollowSymlinks :: No ) 226. nonblock ( true ); 227let source_file = from_dir. open_with ( from_name, & read_options) ?; 228let metadata = source_file. metadata () ?; 229regular ( & metadata) ?; 230let ( to_dir, to_name) =mutable_entry ( & to) ?; 231match to_dir. symlink_metadata ( & to_name) { 232Ok ( metadata) =>regular ( & metadata) ?, 233Err ( error) if error. kind () == io:: ErrorKind :: NotFound =>(), 234Err ( error) =>return Err ( error), 235} 236authorize ( & caps, & to, & destination, true ) ?; 237let mut write_options =OpenOptions :: new (); 238 write_options 239. write ( true ) 240. create ( true ) 241. follow ( FollowSymlinks :: No ) 242. nonblock ( true ); 243let mut permissions = metadata. permissions (); 244# [ cfg ( unix )] 245{ 246use cap_std:: fs::{ MetadataExt , OpenOptionsExt , PermissionsExt }; 247let mode = metadata. mode () & 0o777 ; 248 write_options. mode ( mode); 249 permissions. set_mode ( mode); 250} 251let destination_file = to_dir. open_with ( to_name, & write_options) ?; 252regular ( & destination_file. metadata () ?) ?; 253let a = same_file:: Handle :: from_file ( source_file. try_clone () ?. into_std ()) ?; 254let b = same_file:: Handle :: from_file ( destination_file. try_clone () ?. into_std ()) ?; 255if a == b{ 256return Err ( io:: Error :: new ( 257 io:: ErrorKind :: InvalidInput , 258"Source and destination are the same file" , 259)); 260} 261 destination_file. set_permissions ( permissions) ?; 262 destination_file. set_len ( 0 ) ?; 263let mut source_file = source_file. into_std (); 264let mut destination_file = destination_file. into_std (); 265 io:: copy ( & mut source_file, & mut destination_file) ?; 266Ok ( true ) 267}); 268async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 269}, 270), 271]. into_iter (). flatten (). collect () 272}