1 use super::{abi, error};
3 ffi::{CStr, CString, OsStr, OsString},
5 io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom},
7 os::raw::{c_int, c_short},
8 os::solid::ffi::OsStrExt,
11 sys::time::SystemTime,
15 pub use crate::sys_common::fs::try_exists;
17 /// A file descriptor.
18 #[derive(Clone, Copy)]
19 #[rustc_layout_scalar_valid_range_start(0)]
20 // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
21 // 32-bit c_int. Below is -2, in two's complement, but that only works out
22 // because c_int is 32 bits.
23 #[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
30 fn new(fd: c_int) -> FileDesc {
31 assert_ne!(fd, -1i32);
32 // Safety: we just asserted that the value is in the valid range and
33 // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
34 unsafe { FileDesc { fd } }
38 fn raw(&self) -> c_int {
52 // all DirEntry's will have a reference to this struct
59 inner: Arc<InnerReadDir>,
64 inner: Arc<InnerReadDir>,
67 #[derive(Clone, Debug)]
68 pub struct OpenOptions {
80 #[derive(Copy, Clone, Debug, Default)]
81 pub struct FileTimes {}
83 #[derive(Clone, PartialEq, Eq, Debug)]
84 pub struct FilePermissions(c_short);
86 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
87 pub struct FileType(c_short);
90 pub struct DirBuilder {}
93 pub fn size(&self) -> u64 {
94 self.stat.st_size as u64
97 pub fn perm(&self) -> FilePermissions {
98 FilePermissions(self.stat.st_mode)
101 pub fn file_type(&self) -> FileType {
102 FileType(self.stat.st_mode)
105 pub fn modified(&self) -> io::Result<SystemTime> {
106 Ok(SystemTime::from_time_t(self.stat.st_mtime))
109 pub fn accessed(&self) -> io::Result<SystemTime> {
110 Ok(SystemTime::from_time_t(self.stat.st_atime))
113 pub fn created(&self) -> io::Result<SystemTime> {
114 Ok(SystemTime::from_time_t(self.stat.st_ctime))
118 impl FilePermissions {
119 pub fn readonly(&self) -> bool {
120 (self.0 & abi::S_IWRITE) == 0
123 pub fn set_readonly(&mut self, readonly: bool) {
125 self.0 &= !abi::S_IWRITE;
127 self.0 |= abi::S_IWRITE;
133 pub fn set_accessed(&mut self, _t: SystemTime) {}
134 pub fn set_modified(&mut self, _t: SystemTime) {}
138 pub fn is_dir(&self) -> bool {
139 self.is(abi::S_IFDIR)
141 pub fn is_file(&self) -> bool {
142 self.is(abi::S_IFREG)
144 pub fn is_symlink(&self) -> bool {
148 pub fn is(&self, mode: c_short) -> bool {
149 self.0 & abi::S_IFMT == mode
153 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
155 let mut dir = MaybeUninit::uninit();
156 error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
160 .map_err(|e| e.as_io_error())?;
161 let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
162 Ok(ReadDir { inner })
166 impl fmt::Debug for ReadDir {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
169 // Thus the result will be e g 'ReadDir("/home")'
170 fmt::Debug::fmt(&*self.inner.root, f)
174 impl Iterator for ReadDir {
175 type Item = io::Result<DirEntry>;
177 fn next(&mut self) -> Option<io::Result<DirEntry>> {
179 let mut out_entry = MaybeUninit::uninit();
180 match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
182 out_entry.as_mut_ptr(),
184 Ok(_) => out_entry.assume_init(),
185 Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None,
186 Err(e) => return Some(Err(e.as_io_error())),
190 (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, inner: Arc::clone(&self.inner) }))
194 impl Drop for InnerReadDir {
196 unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
201 pub fn path(&self) -> PathBuf {
202 self.inner.root.join(OsStr::from_bytes(
203 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(),
207 pub fn file_name(&self) -> OsString {
208 OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
212 pub fn metadata(&self) -> io::Result<FileAttr> {
216 pub fn file_type(&self) -> io::Result<FileType> {
217 match self.entry.d_type {
218 abi::DT_CHR => Ok(FileType(abi::S_IFCHR)),
219 abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)),
220 abi::DT_REG => Ok(FileType(abi::S_IFREG)),
221 abi::DT_DIR => Ok(FileType(abi::S_IFDIR)),
222 abi::DT_BLK => Ok(FileType(abi::S_IFBLK)),
223 _ => lstat(&self.path()).map(|m| m.file_type()),
229 pub fn new() -> OpenOptions {
243 pub fn read(&mut self, read: bool) {
246 pub fn write(&mut self, write: bool) {
249 pub fn append(&mut self, append: bool) {
250 self.append = append;
252 pub fn truncate(&mut self, truncate: bool) {
253 self.truncate = truncate;
255 pub fn create(&mut self, create: bool) {
256 self.create = create;
258 pub fn create_new(&mut self, create_new: bool) {
259 self.create_new = create_new;
262 pub fn custom_flags(&mut self, flags: i32) {
263 self.custom_flags = flags;
265 pub fn mode(&mut self, _mode: u32) {}
267 fn get_access_mode(&self) -> io::Result<c_int> {
268 match (self.read, self.write, self.append) {
269 (true, false, false) => Ok(abi::O_RDONLY),
270 (false, true, false) => Ok(abi::O_WRONLY),
271 (true, true, false) => Ok(abi::O_RDWR),
272 (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND),
273 (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND),
274 (false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
278 fn get_creation_mode(&self) -> io::Result<c_int> {
279 match (self.write, self.append) {
282 if self.truncate || self.create || self.create_new {
283 return Err(io::Error::from_raw_os_error(libc::EINVAL));
287 if self.truncate && !self.create_new {
288 return Err(io::Error::from_raw_os_error(libc::EINVAL));
293 Ok(match (self.create, self.truncate, self.create_new) {
294 (false, false, false) => 0,
295 (true, false, false) => abi::O_CREAT,
296 (false, true, false) => abi::O_TRUNC,
297 (true, true, false) => abi::O_CREAT | abi::O_TRUNC,
298 (_, _, true) => abi::O_CREAT | abi::O_EXCL,
303 fn cstr(path: &Path) -> io::Result<CString> {
304 let path = path.as_os_str().as_bytes();
306 if !path.starts_with(br"\") {
307 // Relative paths aren't supported
308 return Err(crate::io::const_io_error!(
309 crate::io::ErrorKind::Unsupported,
310 "relative path is not supported on this platform",
314 // Apply the thread-safety wrapper
315 const SAFE_PREFIX: &[u8] = br"\TS";
316 let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat();
318 CString::from_vec_with_nul(wrapped_path).map_err(|_| {
319 crate::io::const_io_error!(
320 io::ErrorKind::InvalidInput,
321 "path provided contains a nul byte",
327 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
328 let flags = opts.get_access_mode()?
329 | opts.get_creation_mode()?
330 | (opts.custom_flags as c_int & !abi::O_ACCMODE);
332 let mut fd = MaybeUninit::uninit();
333 error::SolidError::err_if_negative(abi::SOLID_FS_Open(
335 cstr(path)?.as_ptr(),
338 .map_err(|e| e.as_io_error())?;
339 Ok(File { fd: FileDesc::new(fd.assume_init()) })
343 pub fn file_attr(&self) -> io::Result<FileAttr> {
347 pub fn fsync(&self) -> io::Result<()> {
351 pub fn datasync(&self) -> io::Result<()> {
355 pub fn truncate(&self, _size: u64) -> io::Result<()> {
359 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
361 let mut out_num_bytes = MaybeUninit::uninit();
362 error::SolidError::err_if_negative(abi::SOLID_FS_Read(
366 out_num_bytes.as_mut_ptr(),
368 .map_err(|e| e.as_io_error())?;
369 Ok(out_num_bytes.assume_init())
373 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
375 let len = cursor.capacity();
376 let mut out_num_bytes = MaybeUninit::uninit();
377 error::SolidError::err_if_negative(abi::SOLID_FS_Read(
379 cursor.as_mut().as_mut_ptr() as *mut u8,
381 out_num_bytes.as_mut_ptr(),
383 .map_err(|e| e.as_io_error())?;
385 // Safety: `out_num_bytes` is filled by the successful call to
387 let num_bytes_read = out_num_bytes.assume_init();
389 // Safety: `num_bytes_read` bytes were written to the unfilled
390 // portion of the buffer
391 cursor.advance(num_bytes_read);
397 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
398 crate::io::default_read_vectored(|buf| self.read(buf), bufs)
401 pub fn is_read_vectored(&self) -> bool {
405 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
407 let mut out_num_bytes = MaybeUninit::uninit();
408 error::SolidError::err_if_negative(abi::SOLID_FS_Write(
412 out_num_bytes.as_mut_ptr(),
414 .map_err(|e| e.as_io_error())?;
415 Ok(out_num_bytes.assume_init())
419 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
420 crate::io::default_write_vectored(|buf| self.write(buf), bufs)
423 pub fn is_write_vectored(&self) -> bool {
427 pub fn flush(&self) -> io::Result<()> {
428 error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) })
429 .map_err(|e| e.as_io_error())?;
433 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
434 let (whence, pos) = match pos {
435 // Casting to `i64` is fine, too large values will end up as
436 // negative which will cause an error in `SOLID_FS_Lseek`.
437 SeekFrom::Start(off) => (abi::SEEK_SET, off as i64),
438 SeekFrom::End(off) => (abi::SEEK_END, off),
439 SeekFrom::Current(off) => (abi::SEEK_CUR, off),
441 error::SolidError::err_if_negative(unsafe {
442 abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
444 .map_err(|e| e.as_io_error())?;
446 // Get the new offset
448 let mut out_offset = MaybeUninit::uninit();
449 error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
451 out_offset.as_mut_ptr(),
453 .map_err(|e| e.as_io_error())?;
454 Ok(out_offset.assume_init() as u64)
458 pub fn duplicate(&self) -> io::Result<File> {
462 pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
466 pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
473 unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
478 pub fn new() -> DirBuilder {
482 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
483 error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) })
484 .map_err(|e| e.as_io_error())?;
489 impl fmt::Debug for File {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491 f.debug_struct("File").field("fd", &self.fd.raw()).finish()
495 pub fn unlink(p: &Path) -> io::Result<()> {
496 if stat(p)?.file_type().is_dir() {
497 Err(io::const_io_error!(io::ErrorKind::IsADirectory, "is a directory"))
499 error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
500 .map_err(|e| e.as_io_error())?;
505 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
506 error::SolidError::err_if_negative(unsafe {
507 abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr())
509 .map_err(|e| e.as_io_error())?;
513 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
514 error::SolidError::err_if_negative(unsafe {
515 abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into())
517 .map_err(|e| e.as_io_error())?;
521 pub fn rmdir(p: &Path) -> io::Result<()> {
522 if stat(p)?.file_type().is_dir() {
523 error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
524 .map_err(|e| e.as_io_error())?;
527 Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory"))
531 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
532 for child in readdir(path)? {
534 let child_type = child.file_type()?;
535 if child_type.is_dir() {
536 remove_dir_all(&child.path())?;
538 unlink(&child.path())?;
544 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
545 // This target doesn't support symlinks
547 Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link"))
550 pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
551 // This target doesn't support symlinks
555 pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
556 // This target doesn't support symlinks
560 pub fn stat(p: &Path) -> io::Result<FileAttr> {
561 // This target doesn't support symlinks
565 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
567 let mut out_stat = MaybeUninit::uninit();
568 error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
570 out_stat.as_mut_ptr(),
572 .map_err(|e| e.as_io_error())?;
573 Ok(FileAttr { stat: out_stat.assume_init() })
577 pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
581 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
584 let mut reader = File::open(from)?;
585 let mut writer = File::create(to)?;
587 io::copy(&mut reader, &mut writer)