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