1 use super::{abi, error};
2 use crate::{
3     ffi::{CStr, CString, OsStr, OsString},
4     fmt,
5     io::{self, IoSlice, IoSliceMut, SeekFrom},
6     mem::MaybeUninit,
7     os::raw::{c_int, c_short},
8     os::solid::ffi::OsStrExt,
9     path::{Path, PathBuf},
10     sync::Arc,
11     sys::time::SystemTime,
12     sys::unsupported,
13 };
14 
15 pub use crate::sys_common::fs::try_exists;
16 
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)]
24 struct FileDesc {
25     fd: c_int,
26 }
27 
28 impl FileDesc {
29     #[inline]
new(fd: c_int) -> FileDesc30     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 } }
35     }
36 
37     #[inline]
raw(&self) -> c_int38     fn raw(&self) -> c_int {
39         self.fd
40     }
41 }
42 
43 pub struct File {
44     fd: FileDesc,
45 }
46 
47 #[derive(Clone)]
48 pub struct FileAttr {
49     stat: abi::stat,
50 }
51 
52 // all DirEntry's will have a reference to this struct
53 struct InnerReadDir {
54     dirp: abi::S_DIR,
55     root: PathBuf,
56 }
57 
58 pub struct ReadDir {
59     inner: Arc<InnerReadDir>,
60 }
61 
62 pub struct DirEntry {
63     entry: abi::dirent,
64     inner: Arc<InnerReadDir>,
65 }
66 
67 #[derive(Clone, Debug)]
68 pub struct OpenOptions {
69     // generic
70     read: bool,
71     write: bool,
72     append: bool,
73     truncate: bool,
74     create: bool,
75     create_new: bool,
76     // system-specific
77     custom_flags: i32,
78 }
79 
80 #[derive(Clone, PartialEq, Eq, Debug)]
81 pub struct FilePermissions(c_short);
82 
83 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
84 pub struct FileType(c_short);
85 
86 #[derive(Debug)]
87 pub struct DirBuilder {}
88 
89 impl FileAttr {
size(&self) -> u6490     pub fn size(&self) -> u64 {
91         self.stat.st_size as u64
92     }
93 
perm(&self) -> FilePermissions94     pub fn perm(&self) -> FilePermissions {
95         FilePermissions(self.stat.st_mode)
96     }
97 
file_type(&self) -> FileType98     pub fn file_type(&self) -> FileType {
99         FileType(self.stat.st_mode)
100     }
101 
modified(&self) -> io::Result<SystemTime>102     pub fn modified(&self) -> io::Result<SystemTime> {
103         Ok(SystemTime::from_time_t(self.stat.st_mtime))
104     }
105 
accessed(&self) -> io::Result<SystemTime>106     pub fn accessed(&self) -> io::Result<SystemTime> {
107         Ok(SystemTime::from_time_t(self.stat.st_atime))
108     }
109 
created(&self) -> io::Result<SystemTime>110     pub fn created(&self) -> io::Result<SystemTime> {
111         Ok(SystemTime::from_time_t(self.stat.st_ctime))
112     }
113 }
114 
115 impl FilePermissions {
readonly(&self) -> bool116     pub fn readonly(&self) -> bool {
117         (self.0 & abi::S_IWRITE) == 0
118     }
119 
set_readonly(&mut self, readonly: bool)120     pub fn set_readonly(&mut self, readonly: bool) {
121         if readonly {
122             self.0 &= !abi::S_IWRITE;
123         } else {
124             self.0 |= abi::S_IWRITE;
125         }
126     }
127 }
128 
129 impl FileType {
is_dir(&self) -> bool130     pub fn is_dir(&self) -> bool {
131         self.is(abi::S_IFDIR)
132     }
is_file(&self) -> bool133     pub fn is_file(&self) -> bool {
134         self.is(abi::S_IFREG)
135     }
is_symlink(&self) -> bool136     pub fn is_symlink(&self) -> bool {
137         false
138     }
139 
is(&self, mode: c_short) -> bool140     pub fn is(&self, mode: c_short) -> bool {
141         self.0 & abi::S_IFMT == mode
142     }
143 }
144 
readdir(p: &Path) -> io::Result<ReadDir>145 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
146     unsafe {
147         let mut dir = MaybeUninit::uninit();
148         error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
149             cstr(p)?.as_ptr(),
150             dir.as_mut_ptr(),
151         ))
152         .map_err(|e| e.as_io_error())?;
153         let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
154         Ok(ReadDir { inner })
155     }
156 }
157 
158 impl fmt::Debug for ReadDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result159     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160         // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
161         // Thus the result will be e g 'ReadDir("/home")'
162         fmt::Debug::fmt(&*self.inner.root, f)
163     }
164 }
165 
166 impl Iterator for ReadDir {
167     type Item = io::Result<DirEntry>;
168 
next(&mut self) -> Option<io::Result<DirEntry>>169     fn next(&mut self) -> Option<io::Result<DirEntry>> {
170         unsafe {
171             let mut out_dirent = MaybeUninit::uninit();
172             error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
173                 self.inner.dirp,
174                 out_dirent.as_mut_ptr(),
175             ))
176             .ok()?;
177             Some(Ok(DirEntry { entry: out_dirent.assume_init(), inner: Arc::clone(&self.inner) }))
178         }
179     }
180 }
181 
182 impl Drop for InnerReadDir {
drop(&mut self)183     fn drop(&mut self) {
184         unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
185     }
186 }
187 
188 impl DirEntry {
path(&self) -> PathBuf189     pub fn path(&self) -> PathBuf {
190         self.inner.root.join(OsStr::from_bytes(
191             unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(),
192         ))
193     }
194 
file_name(&self) -> OsString195     pub fn file_name(&self) -> OsString {
196         OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
197             .to_os_string()
198     }
199 
metadata(&self) -> io::Result<FileAttr>200     pub fn metadata(&self) -> io::Result<FileAttr> {
201         lstat(&self.path())
202     }
203 
file_type(&self) -> io::Result<FileType>204     pub fn file_type(&self) -> io::Result<FileType> {
205         match self.entry.d_type {
206             abi::DT_CHR => Ok(FileType(abi::S_IFCHR)),
207             abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)),
208             abi::DT_REG => Ok(FileType(abi::S_IFREG)),
209             abi::DT_DIR => Ok(FileType(abi::S_IFDIR)),
210             abi::DT_BLK => Ok(FileType(abi::S_IFBLK)),
211             _ => lstat(&self.path()).map(|m| m.file_type()),
212         }
213     }
214 }
215 
216 impl OpenOptions {
new() -> OpenOptions217     pub fn new() -> OpenOptions {
218         OpenOptions {
219             // generic
220             read: false,
221             write: false,
222             append: false,
223             truncate: false,
224             create: false,
225             create_new: false,
226             // system-specific
227             custom_flags: 0,
228         }
229     }
230 
read(&mut self, read: bool)231     pub fn read(&mut self, read: bool) {
232         self.read = read;
233     }
write(&mut self, write: bool)234     pub fn write(&mut self, write: bool) {
235         self.write = write;
236     }
append(&mut self, append: bool)237     pub fn append(&mut self, append: bool) {
238         self.append = append;
239     }
truncate(&mut self, truncate: bool)240     pub fn truncate(&mut self, truncate: bool) {
241         self.truncate = truncate;
242     }
create(&mut self, create: bool)243     pub fn create(&mut self, create: bool) {
244         self.create = create;
245     }
create_new(&mut self, create_new: bool)246     pub fn create_new(&mut self, create_new: bool) {
247         self.create_new = create_new;
248     }
249 
custom_flags(&mut self, flags: i32)250     pub fn custom_flags(&mut self, flags: i32) {
251         self.custom_flags = flags;
252     }
mode(&mut self, _mode: u32)253     pub fn mode(&mut self, _mode: u32) {}
254 
get_access_mode(&self) -> io::Result<c_int>255     fn get_access_mode(&self) -> io::Result<c_int> {
256         match (self.read, self.write, self.append) {
257             (true, false, false) => Ok(abi::O_RDONLY),
258             (false, true, false) => Ok(abi::O_WRONLY),
259             (true, true, false) => Ok(abi::O_RDWR),
260             (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND),
261             (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND),
262             (false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
263         }
264     }
265 
get_creation_mode(&self) -> io::Result<c_int>266     fn get_creation_mode(&self) -> io::Result<c_int> {
267         match (self.write, self.append) {
268             (true, false) => {}
269             (false, false) => {
270                 if self.truncate || self.create || self.create_new {
271                     return Err(io::Error::from_raw_os_error(libc::EINVAL));
272                 }
273             }
274             (_, true) => {
275                 if self.truncate && !self.create_new {
276                     return Err(io::Error::from_raw_os_error(libc::EINVAL));
277                 }
278             }
279         }
280 
281         Ok(match (self.create, self.truncate, self.create_new) {
282             (false, false, false) => 0,
283             (true, false, false) => abi::O_CREAT,
284             (false, true, false) => abi::O_TRUNC,
285             (true, true, false) => abi::O_CREAT | abi::O_TRUNC,
286             (_, _, true) => abi::O_CREAT | abi::O_EXCL,
287         })
288     }
289 }
290 
cstr(path: &Path) -> io::Result<CString>291 fn cstr(path: &Path) -> io::Result<CString> {
292     Ok(CString::new(path.as_os_str().as_bytes())?)
293 }
294 
295 impl File {
open(path: &Path, opts: &OpenOptions) -> io::Result<File>296     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
297         let flags = opts.get_access_mode()?
298             | opts.get_creation_mode()?
299             | (opts.custom_flags as c_int & !abi::O_ACCMODE);
300         unsafe {
301             let mut fd = MaybeUninit::uninit();
302             error::SolidError::err_if_negative(abi::SOLID_FS_Open(
303                 fd.as_mut_ptr(),
304                 cstr(path)?.as_ptr(),
305                 flags,
306             ))
307             .map_err(|e| e.as_io_error())?;
308             Ok(File { fd: FileDesc::new(fd.assume_init()) })
309         }
310     }
311 
file_attr(&self) -> io::Result<FileAttr>312     pub fn file_attr(&self) -> io::Result<FileAttr> {
313         unsupported()
314     }
315 
fsync(&self) -> io::Result<()>316     pub fn fsync(&self) -> io::Result<()> {
317         self.flush()
318     }
319 
datasync(&self) -> io::Result<()>320     pub fn datasync(&self) -> io::Result<()> {
321         self.flush()
322     }
323 
truncate(&self, _size: u64) -> io::Result<()>324     pub fn truncate(&self, _size: u64) -> io::Result<()> {
325         unsupported()
326     }
327 
read(&self, buf: &mut [u8]) -> io::Result<usize>328     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
329         unsafe {
330             let mut out_num_bytes = MaybeUninit::uninit();
331             error::SolidError::err_if_negative(abi::SOLID_FS_Read(
332                 self.fd.raw(),
333                 buf.as_mut_ptr(),
334                 buf.len(),
335                 out_num_bytes.as_mut_ptr(),
336             ))
337             .map_err(|e| e.as_io_error())?;
338             Ok(out_num_bytes.assume_init())
339         }
340     }
341 
read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>342     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
343         crate::io::default_read_vectored(|buf| self.read(buf), bufs)
344     }
345 
is_read_vectored(&self) -> bool346     pub fn is_read_vectored(&self) -> bool {
347         false
348     }
349 
write(&self, buf: &[u8]) -> io::Result<usize>350     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
351         unsafe {
352             let mut out_num_bytes = MaybeUninit::uninit();
353             error::SolidError::err_if_negative(abi::SOLID_FS_Write(
354                 self.fd.raw(),
355                 buf.as_ptr(),
356                 buf.len(),
357                 out_num_bytes.as_mut_ptr(),
358             ))
359             .map_err(|e| e.as_io_error())?;
360             Ok(out_num_bytes.assume_init())
361         }
362     }
363 
write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize>364     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
365         crate::io::default_write_vectored(|buf| self.write(buf), bufs)
366     }
367 
is_write_vectored(&self) -> bool368     pub fn is_write_vectored(&self) -> bool {
369         false
370     }
371 
flush(&self) -> io::Result<()>372     pub fn flush(&self) -> io::Result<()> {
373         error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) })
374             .map_err(|e| e.as_io_error())?;
375         Ok(())
376     }
377 
seek(&self, pos: SeekFrom) -> io::Result<u64>378     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
379         let (whence, pos) = match pos {
380             // Casting to `i64` is fine, too large values will end up as
381             // negative which will cause an error in `SOLID_FS_Lseek`.
382             SeekFrom::Start(off) => (abi::SEEK_SET, off as i64),
383             SeekFrom::End(off) => (abi::SEEK_END, off),
384             SeekFrom::Current(off) => (abi::SEEK_CUR, off),
385         };
386         error::SolidError::err_if_negative(unsafe {
387             abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
388         })
389         .map_err(|e| e.as_io_error())?;
390 
391         // Get the new offset
392         unsafe {
393             let mut out_offset = MaybeUninit::uninit();
394             error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
395                 self.fd.raw(),
396                 out_offset.as_mut_ptr(),
397             ))
398             .map_err(|e| e.as_io_error())?;
399             Ok(out_offset.assume_init() as u64)
400         }
401     }
402 
duplicate(&self) -> io::Result<File>403     pub fn duplicate(&self) -> io::Result<File> {
404         unsupported()
405     }
406 
set_permissions(&self, _perm: FilePermissions) -> io::Result<()>407     pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
408         unsupported()
409     }
410 }
411 
412 impl Drop for File {
drop(&mut self)413     fn drop(&mut self) {
414         unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
415     }
416 }
417 
418 impl DirBuilder {
new() -> DirBuilder419     pub fn new() -> DirBuilder {
420         DirBuilder {}
421     }
422 
mkdir(&self, p: &Path) -> io::Result<()>423     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
424         error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) })
425             .map_err(|e| e.as_io_error())?;
426         Ok(())
427     }
428 }
429 
430 impl fmt::Debug for File {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result431     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432         f.debug_struct("File").field("fd", &self.fd.raw()).finish()
433     }
434 }
435 
unlink(p: &Path) -> io::Result<()>436 pub fn unlink(p: &Path) -> io::Result<()> {
437     if stat(p)?.file_type().is_dir() {
438         Err(io::Error::new_const(io::ErrorKind::IsADirectory, &"is a directory"))
439     } else {
440         error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
441             .map_err(|e| e.as_io_error())?;
442         Ok(())
443     }
444 }
445 
rename(old: &Path, new: &Path) -> io::Result<()>446 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
447     error::SolidError::err_if_negative(unsafe {
448         abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr())
449     })
450     .map_err(|e| e.as_io_error())?;
451     Ok(())
452 }
453 
set_perm(p: &Path, perm: FilePermissions) -> io::Result<()>454 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
455     error::SolidError::err_if_negative(unsafe {
456         abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into())
457     })
458     .map_err(|e| e.as_io_error())?;
459     Ok(())
460 }
461 
rmdir(p: &Path) -> io::Result<()>462 pub fn rmdir(p: &Path) -> io::Result<()> {
463     if stat(p)?.file_type().is_dir() {
464         error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
465             .map_err(|e| e.as_io_error())?;
466         Ok(())
467     } else {
468         Err(io::Error::new_const(io::ErrorKind::NotADirectory, &"not a directory"))
469     }
470 }
471 
remove_dir_all(path: &Path) -> io::Result<()>472 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
473     for child in readdir(path)? {
474         let child = child?;
475         let child_type = child.file_type()?;
476         if child_type.is_dir() {
477             remove_dir_all(&child.path())?;
478         } else {
479             unlink(&child.path())?;
480         }
481     }
482     rmdir(path)
483 }
484 
readlink(p: &Path) -> io::Result<PathBuf>485 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
486     // This target doesn't support symlinks
487     stat(p)?;
488     Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"not a symbolic link"))
489 }
490 
symlink(_original: &Path, _link: &Path) -> io::Result<()>491 pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
492     // This target doesn't support symlinks
493     unsupported()
494 }
495 
link(_src: &Path, _dst: &Path) -> io::Result<()>496 pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
497     // This target doesn't support symlinks
498     unsupported()
499 }
500 
stat(p: &Path) -> io::Result<FileAttr>501 pub fn stat(p: &Path) -> io::Result<FileAttr> {
502     // This target doesn't support symlinks
503     lstat(p)
504 }
505 
lstat(p: &Path) -> io::Result<FileAttr>506 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
507     unsafe {
508         let mut out_stat = MaybeUninit::uninit();
509         error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
510             cstr(p)?.as_ptr(),
511             out_stat.as_mut_ptr(),
512         ))
513         .map_err(|e| e.as_io_error())?;
514         Ok(FileAttr { stat: out_stat.assume_init() })
515     }
516 }
517 
canonicalize(_p: &Path) -> io::Result<PathBuf>518 pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
519     unsupported()
520 }
521 
copy(from: &Path, to: &Path) -> io::Result<u64>522 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
523     use crate::fs::File;
524 
525     let mut reader = File::open(from)?;
526     let mut writer = File::create(to)?;
527 
528     io::copy(&mut reader, &mut writer)
529 }
530