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 :: paths::{ resolve, tree}; 2use super :: * ; 3 4fn overlaps ( directory : & Dir , path : & Path ) -> io:: Result < bool > { 5let root = same_file:: Handle :: from_file ( directory. try_clone () ?. into_std_file ()) ?; 6for ancestorin path. ancestors () { 7let candidate =parent ( ancestor). and_then ( |( parent, name) | parent. open_dir_nofollow ( name)); 8match candidate{ 9Ok ( candidate) =>{ 10let candidate = same_file:: Handle :: from_file ( candidate. into_std_file ()) ?; 11if root == candidate{ 12return Ok ( true ); 13} 14} 15Err ( error) if error. kind () == io:: ErrorKind :: NotFound =>(), 16Err ( error) =>return Err ( error), 17} 18} 19Ok ( false ) 20} 21 22pub ( super ) fn ops ( files : Arc < Files >) ->Vec < Op > { 23let ops = files. clone (); 24let ensure = files. clone (); 25let prepare = files. clone (); 26let read_dir = files. clone (); 27let dir_mode = files. clone (); 28vec! [ 29 ops. io_ops ( 30"op_file_read_tree_dir" , 31"op_file_read_tree_dir_sync" , 32move |path : String |{ 33let caps = read_dir. caps . clone (); 34let pending = read_dir 35. pool 36. run ( move ||super :: paths:: entries ( & caps, & path, true )); 37async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 38}, 39), 40 ops. io_ops ( 41"op_file_copy_dir_mode" , 42"op_file_copy_dir_mode_sync" , 43move |( path, mode): ( String , Option < u32 >) |{ 44let caps = dir_mode. caps . clone (); 45let pending = dir_mode. pool . run ( move ||{ 46let target =resolve ( & caps, & path, false , true ) ?; 47tree ( & caps, & target, & path, true ) ?; 48let ( directory, name) =parent ( target. as_path ()) ?; 49# [ cfg ( unix )] 50{ 51use std:: os:: unix:: fs:: PermissionsExt ; 52if let Some ( mode) = mode{ 53if mode >0o777 { 54return Err ( io:: Error :: new ( 55 io:: ErrorKind :: InvalidInput , 56"Invalid copied directory mode" , 57)); 58} 59use cap_std:: fs:: OpenOptionsExt ; 60let mut flags =OpenOptions :: new (); 61 flags 62. read ( true ) 63. follow ( FollowSymlinks :: No ) 64. nonblock ( true ) 65. custom_flags ( libc:: O_DIRECTORY ); 66let file = directory. open_with ( name, & flags) ?; 67if !file. metadata () ?. is_dir () { 68return Err ( io:: Error :: new ( 69 io:: ErrorKind :: NotADirectory , 70"Copy destination is not a directory" , 71)); 72} 73 file. into_std () 74. set_permissions ( std:: fs:: Permissions :: from_mode ( mode)) ?; 75} 76} 77# [ cfg ( not ( unix ))] 78let _ =( directory. open_dir_nofollow ( name) ?, mode); 79Ok ( true ) 80}); 81async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 82}, 83), 84vec! [ Op :: new ( "op_file_tmp_dir" , |(): () |{ 85let dir = std:: env:: temp_dir () 86. into_os_string () 87. into_string () 88. map_err ( |_|{ 89 io:: Error :: new ( 90 io:: ErrorKind :: Unsupported , 91"not yet implemented: non-UTF-8 temp directory" , 92) 93}); 94Ok ::< _ , Infallible >( response ( dir)) 95})], 96vec! [ Op :: new ( "op_file_path_parent" , move |path : String |{ 97let parsed =filesystem_path ( & files. caps , & path). and_then ( |path|{ 98 path. parent () 99. map ( |parent|{ 100 parent. to_str (). map ( str :: to_owned). ok_or_else ( ||{ 101 io:: Error :: new ( 102 io:: ErrorKind :: Unsupported , 103"not yet implemented: non-UTF-8 paths" , 104) 105}) 106}) 107. transpose () ? 108. ok_or_else ( ||{ 109 io:: Error :: new ( io:: ErrorKind :: InvalidInput , "Filesystem root has no parent" ) 110}) 111}); 112Ok ::< _ , Infallible >( response ( parsed)) 113})], 114vec! [ Op :: new ( 115"op_file_path_join" , 116 |( path, name): ( String , String ) |{ 117let parsed =( ||{ 118let mut components =Path :: new ( & name). components (); 119if !matches! ( components. next (), Some ( Component :: Normal ( _))) 120 || components. next (). is_some () 121{ 122return Err ( io:: Error :: new ( 123 io:: ErrorKind :: InvalidInput , 124"Expected a single entry name" , 125)); 126} 127Path :: new ( & path) 128. join ( name) 129. into_os_string () 130. into_string () 131. map_err ( |_|{ 132 io:: Error :: new ( 133 io:: ErrorKind :: Unsupported , 134"not yet implemented: non-UTF-8 paths" , 135) 136}) 137})(); 138Ok ::< _ , Infallible >( response ( parsed)) 139}, 140)], 141 ops. io_ops ( 142"op_file_ensure_dir" , 143"op_file_ensure_dir_sync" , 144move |( path, mode): ( String , Option < u32 >) |{ 145let caps = ensure. caps . clone (); 146let pending = ensure. pool . run ( move ||{ 147if mode. is_some_and ( |mode| mode >0o7777 ) { 148return Err ( io:: Error :: new ( 149 io:: ErrorKind :: InvalidInput , 150"Invalid directory mode" , 151)); 152} 153let target =resolve ( & caps, & path, false , true ) ?; 154let ( directory, name) =parent ( target. as_path ()) ?; 155let mut builder = cap_std:: fs:: DirBuilder :: new (); 156# [ cfg ( unix )] 157{ 158use cap_std:: fs:: DirBuilderExt ; 159if let Some ( mode) = mode{ 160 builder. mode ( mode); 161} 162} 163match directory. create_dir_with ( & name, & builder) { 164Ok (()) =>(), 165Err ( error) if error. kind () == io:: ErrorKind :: AlreadyExists =>{ 166 directory. open_dir_nofollow ( name) ?; 167} 168Err ( error) =>return Err ( error), 169} 170Ok ( true ) 171}); 172async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 173}, 174), 175 ops. io_ops ( 176"op_file_prepare_copy" , 177"op_file_prepare_copy_sync" , 178move |( source, destination, moving, dereference): ( String , String , bool , bool ) |{ 179let caps = prepare. caps . clone (); 180let pending = prepare. pool . run ( move ||{ 181let from =resolve ( & caps, & source, !dereference, false ) ?; 182let ( from_parent, name) =parent ( from. as_path ()) ?; 183let mut metadata = from_parent. symlink_metadata ( & name) ?; 184if dereference && metadata. is_symlink () { 185return Err ( io:: Error :: new ( 186 io:: ErrorKind :: NotFound , 187"Symbolic link changed or has a missing target" , 188)); 189} 190let to =resolve ( & caps, & destination, metadata. is_symlink (), true ) ?; 191if !( metadata. is_symlink () || metadata. is_file () || metadata. is_dir ()) { 192return Err ( io:: Error :: new ( 193 io:: ErrorKind :: Unsupported , 194"not yet implemented: copying non-regular files" , 195)); 196} 197if moving || metadata. is_dir () { 198tree ( & caps, & from, & source, false ) ?; 199tree ( & caps, & to, & destination, true ) ?; 200} 201if moving{ 202tree ( & caps, & from, & source, true ) ?; 203} 204let mut identity =None ; 205if metadata. is_dir () { 206let source_dir = from_parent. open_dir_nofollow ( name) ?; 207 metadata = source_dir. dir_metadata () ?; 208{ 209use cap_fs_ext:: MetadataExt ; 210 identity =Some ( format! ( "{:x}:{:x}" , metadata. dev (), metadata. ino ())); 211} 212if overlaps ( & source_dir, to. as_path ()) ?{ 213return Err ( io:: Error :: new ( 214 io:: ErrorKind :: InvalidInput , 215"Source and destination trees overlap" , 216)); 217} 218let destination_dir =parent ( to. as_path ()) 219. and_then ( |( parent, name) | parent. open_dir_nofollow ( name)); 220match destination_dir{ 221Ok ( destination_dir) if overlaps ( & destination_dir, from. as_path ()) ? =>{ 222return Err ( io:: Error :: new ( 223 io:: ErrorKind :: InvalidInput , 224"Source and destination trees overlap" , 225)); 226} 227Ok ( _) =>(), 228Err ( error) if error. kind () == io:: ErrorKind :: NotFound =>(), 229Err ( error) =>return Err ( error), 230} 231} 232# [ cfg ( unix )] 233let mode ={ 234use cap_std:: fs:: MetadataExt ; 235Some ( metadata. mode () & 0o777 ) 236}; 237# [ cfg ( not ( unix ))] 238let mode: Option < u32 > =None ; 239let kind: u8 =if metadata. is_symlink () { 2402 241} else if metadata. is_dir () { 2421 243} else { 2440 245}; 246Ok (( kind, mode, identity, super :: links:: text ( from. into_path ()) ?)) 247}); 248async move { Ok ::< _ , Infallible >( receive ( pending). await ) } 249}, 250), 251] 252. into_iter () 253. flatten () 254. collect () 255}