1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
use crate::fs::{File, OpenOptions, ReadDir}; use crate::wasi::types; use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1; use crate::WasiCtx; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; use std::{io, path::Path}; /// A reference to an open directory on the filesystem. /// /// TODO: Implement `Dir`-using versions of `std::fs`'s free functions: /// `copy`, `create_dir`, `create_dir_all`, `hard_link`, `metadata`, /// `read_link`, `read_to_string`, `remove_dir`, `remove_dir_all`, /// `remove_file`, `rename`, `set_permissions`, `symlink_metadata`, and /// `write`. /// /// Unlike `std::fs`, this API has no `canonicalize`, because absolute paths /// don't interoperate well with the capability-oriented security model. pub struct Dir<'ctx> { ctx: &'ctx WasiCtx, fd: types::Fd, } impl<'ctx> Dir<'ctx> { /// Constructs a new instance of `Self` from the given raw WASI file descriptor. pub unsafe fn from_raw_wasi_fd(ctx: &'ctx WasiCtx, fd: types::Fd) -> Self { Self { ctx, fd } } /// Attempts to open a file in read-only mode. /// /// This corresponds to [`std::fs::File::open`], but only accesses paths /// relative to and within `self`. /// /// TODO: Not yet implemented. Refactor the hostcalls functions to split out the /// encoding/decoding parts from the underlying functionality, so that we can call /// into the underlying functionality directly. /// /// [`std::fs::File::open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<File> { let path = path.as_ref(); let mut fd = types::Fd::from(0); // TODO: Refactor the hostcalls functions to split out the encoding/decoding // parts from the underlying functionality, so that we can call into the // underlying functionality directly. // // TODO: Set the requested rights to be readonly. // // TODO: Handle paths for non-Unix platforms which don't have `as_bytes()` // on `OsStrExt`. unimplemented!("Dir::open_file"); /* wasi_errno_to_io_error(hostcalls::path_open( self.ctx, self.fd, wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW, path.as_os_str().as_bytes(), path.as_os_str().len(), 0, !0, !0, 0, &mut fd, ))?; */ let ctx = self.ctx; Ok(unsafe { File::from_raw_wasi_fd(ctx, fd) }) } /// Opens a file at `path` with the options specified by `self`. /// /// This corresponds to [`std::fs::OpenOptions::open`]. /// /// Instead of being a method on `OpenOptions`, this is a method on `Dir`, /// and it only accesses functions relative to and within `self`. /// /// TODO: Not yet implemented. /// /// [`std::fs::OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open pub fn open_file_with<P: AsRef<Path>>( &mut self, path: P, options: &OpenOptions, ) -> io::Result<File> { unimplemented!("Dir::open_file_with"); } /// Attempts to open a directory. /// /// TODO: Not yet implemented. See the comment in `open_file`. pub fn open_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Self> { let path = path.as_ref(); let mut fd = types::Fd::from(0); // TODO: See the comment in `open_file`. unimplemented!("Dir::open_dir"); /* wasi_errno_to_io_error(hostcalls::path_open( self.ctx, self.fd, wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW, path.as_os_str().as_bytes(), wasi::__WASI_OFLAGS_DIRECTORY, !0, !0, 0, &mut fd, ))?; */ let ctx = self.ctx; Ok(unsafe { Dir::from_raw_wasi_fd(ctx, fd) }) } /// Opens a file in write-only mode. /// /// This corresponds to [`std::fs::File::create`], but only accesses paths /// relative to and within `self`. /// /// TODO: Not yet implemented. See the comment in `open_file`. /// /// [`std::fs::File::create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create pub fn create_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<File> { let path = path.as_ref(); let mut fd = types::Fd::from(0); // TODO: See the comments in `open_file`. // // TODO: Set the requested rights to be read+write. unimplemented!("Dir::create_file"); /* wasi_errno_to_io_error(hostcalls::path_open( self.ctx, self.fd, wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW, path.as_os_str().as_bytes(), path.as_os_str().len(), wasi::__WASI_OFLAGS_CREAT | wasi::__WASI_OFLAGS_TRUNC, !0, !0, 0, &mut fd, ))?; */ let ctx = self.ctx; Ok(unsafe { File::from_raw_wasi_fd(ctx, fd) }) } /// Returns an iterator over the entries within a directory. /// /// This corresponds to [`std::fs::read_dir`], but reads the directory /// represented by `self`. /// /// TODO: Not yet implemented. We may need to wait until we have the ability /// to duplicate file descriptors before we can implement read safely. For /// now, use `into_read` instead. /// /// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html pub fn read(&mut self) -> io::Result<ReadDir> { unimplemented!("Dir::read") } /// Consumes self and returns an iterator over the entries within a directory /// in the manner of `read`. pub fn into_read(self) -> ReadDir { unsafe { ReadDir::from_raw_wasi_fd(self.fd) } } /// Read the entire contents of a file into a bytes vector. /// /// This corresponds to [`std::fs::read`], but only accesses paths /// relative to and within `self`. /// /// [`std::fs::read`]: https://doc.rust-lang.org/std/fs/fn.read.html pub fn read_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Vec<u8>> { use io::Read; let mut file = self.open_file(path)?; let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); file.read_to_end(&mut bytes)?; Ok(bytes) } /// Returns an iterator over the entries within a directory. /// /// This corresponds to [`std::fs::read_dir`], but only accesses paths /// relative to and within `self`. /// /// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html pub fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<ReadDir> { self.open_dir(path)?.read() } } impl<'ctx> Drop for Dir<'ctx> { fn drop(&mut self) { // Note that errors are ignored when closing a file descriptor. The // reason for this is that if an error occurs we don't actually know if // the file descriptor was closed or not, and if we retried (for // something like EINTR), we might close another valid file descriptor // opened after we closed ours. let _ = self.ctx.fd_close(self.fd); } } /// Indicates how large a buffer to pre-allocate before reading the entire file. /// /// Derived from the function of the same name in libstd. fn initial_buffer_size(file: &File) -> usize { // Allocate one extra byte so the buffer doesn't need to grow before the // final `read` call at the end of the file. Don't worry about `usize` // overflow because reading will fail regardless in that case. file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) } // TODO: impl Debug for Dir