]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/solid/fs.rs
Auto merge of #103556 - clubby789:specialize-option-partial-eq, r=scottmcm
[rust.git] / library / std / src / sys / solid / fs.rs
1 use super::{abi, error};
2 use crate::{
3     ffi::{CStr, CString, OsStr, OsString},
4     fmt,
5     io::{self, BorrowedCursor, 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]
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 } }
35     }
36
37     #[inline]
38     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(Copy, Clone, Debug, Default)]
81 pub struct FileTimes {}
82
83 #[derive(Clone, PartialEq, Eq, Debug)]
84 pub struct FilePermissions(c_short);
85
86 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
87 pub struct FileType(c_short);
88
89 #[derive(Debug)]
90 pub struct DirBuilder {}
91
92 impl FileAttr {
93     pub fn size(&self) -> u64 {
94         self.stat.st_size as u64
95     }
96
97     pub fn perm(&self) -> FilePermissions {
98         FilePermissions(self.stat.st_mode)
99     }
100
101     pub fn file_type(&self) -> FileType {
102         FileType(self.stat.st_mode)
103     }
104
105     pub fn modified(&self) -> io::Result<SystemTime> {
106         Ok(SystemTime::from_time_t(self.stat.st_mtime))
107     }
108
109     pub fn accessed(&self) -> io::Result<SystemTime> {
110         Ok(SystemTime::from_time_t(self.stat.st_atime))
111     }
112
113     pub fn created(&self) -> io::Result<SystemTime> {
114         Ok(SystemTime::from_time_t(self.stat.st_ctime))
115     }
116 }
117
118 impl FilePermissions {
119     pub fn readonly(&self) -> bool {
120         (self.0 & abi::S_IWRITE) == 0
121     }
122
123     pub fn set_readonly(&mut self, readonly: bool) {
124         if readonly {
125             self.0 &= !abi::S_IWRITE;
126         } else {
127             self.0 |= abi::S_IWRITE;
128         }
129     }
130 }
131
132 impl FileTimes {
133     pub fn set_accessed(&mut self, _t: SystemTime) {}
134     pub fn set_modified(&mut self, _t: SystemTime) {}
135 }
136
137 impl FileType {
138     pub fn is_dir(&self) -> bool {
139         self.is(abi::S_IFDIR)
140     }
141     pub fn is_file(&self) -> bool {
142         self.is(abi::S_IFREG)
143     }
144     pub fn is_symlink(&self) -> bool {
145         false
146     }
147
148     pub fn is(&self, mode: c_short) -> bool {
149         self.0 & abi::S_IFMT == mode
150     }
151 }
152
153 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
154     unsafe {
155         let mut dir = MaybeUninit::uninit();
156         error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
157             cstr(p)?.as_ptr(),
158             dir.as_mut_ptr(),
159         ))
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 })
163     }
164 }
165
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)
171     }
172 }
173
174 impl Iterator for ReadDir {
175     type Item = io::Result<DirEntry>;
176
177     fn next(&mut self) -> Option<io::Result<DirEntry>> {
178         let entry = unsafe {
179             let mut out_entry = MaybeUninit::uninit();
180             match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
181                 self.inner.dirp,
182                 out_entry.as_mut_ptr(),
183             )) {
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())),
187             }
188         };
189
190         (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, inner: Arc::clone(&self.inner) }))
191     }
192 }
193
194 impl Drop for InnerReadDir {
195     fn drop(&mut self) {
196         unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
197     }
198 }
199
200 impl DirEntry {
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(),
204         ))
205     }
206
207     pub fn file_name(&self) -> OsString {
208         OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
209             .to_os_string()
210     }
211
212     pub fn metadata(&self) -> io::Result<FileAttr> {
213         lstat(&self.path())
214     }
215
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()),
224         }
225     }
226 }
227
228 impl OpenOptions {
229     pub fn new() -> OpenOptions {
230         OpenOptions {
231             // generic
232             read: false,
233             write: false,
234             append: false,
235             truncate: false,
236             create: false,
237             create_new: false,
238             // system-specific
239             custom_flags: 0,
240         }
241     }
242
243     pub fn read(&mut self, read: bool) {
244         self.read = read;
245     }
246     pub fn write(&mut self, write: bool) {
247         self.write = write;
248     }
249     pub fn append(&mut self, append: bool) {
250         self.append = append;
251     }
252     pub fn truncate(&mut self, truncate: bool) {
253         self.truncate = truncate;
254     }
255     pub fn create(&mut self, create: bool) {
256         self.create = create;
257     }
258     pub fn create_new(&mut self, create_new: bool) {
259         self.create_new = create_new;
260     }
261
262     pub fn custom_flags(&mut self, flags: i32) {
263         self.custom_flags = flags;
264     }
265     pub fn mode(&mut self, _mode: u32) {}
266
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)),
275         }
276     }
277
278     fn get_creation_mode(&self) -> io::Result<c_int> {
279         match (self.write, self.append) {
280             (true, false) => {}
281             (false, false) => {
282                 if self.truncate || self.create || self.create_new {
283                     return Err(io::Error::from_raw_os_error(libc::EINVAL));
284                 }
285             }
286             (_, true) => {
287                 if self.truncate && !self.create_new {
288                     return Err(io::Error::from_raw_os_error(libc::EINVAL));
289                 }
290             }
291         }
292
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,
299         })
300     }
301 }
302
303 fn cstr(path: &Path) -> io::Result<CString> {
304     let path = path.as_os_str().as_bytes();
305
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",
311         ));
312     }
313
314     // Apply the thread-safety wrapper
315     const SAFE_PREFIX: &[u8] = br"\TS";
316     let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat();
317
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",
322         )
323     })
324 }
325
326 impl File {
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);
331         unsafe {
332             let mut fd = MaybeUninit::uninit();
333             error::SolidError::err_if_negative(abi::SOLID_FS_Open(
334                 fd.as_mut_ptr(),
335                 cstr(path)?.as_ptr(),
336                 flags,
337             ))
338             .map_err(|e| e.as_io_error())?;
339             Ok(File { fd: FileDesc::new(fd.assume_init()) })
340         }
341     }
342
343     pub fn file_attr(&self) -> io::Result<FileAttr> {
344         unsupported()
345     }
346
347     pub fn fsync(&self) -> io::Result<()> {
348         self.flush()
349     }
350
351     pub fn datasync(&self) -> io::Result<()> {
352         self.flush()
353     }
354
355     pub fn truncate(&self, _size: u64) -> io::Result<()> {
356         unsupported()
357     }
358
359     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
360         unsafe {
361             let mut out_num_bytes = MaybeUninit::uninit();
362             error::SolidError::err_if_negative(abi::SOLID_FS_Read(
363                 self.fd.raw(),
364                 buf.as_mut_ptr(),
365                 buf.len(),
366                 out_num_bytes.as_mut_ptr(),
367             ))
368             .map_err(|e| e.as_io_error())?;
369             Ok(out_num_bytes.assume_init())
370         }
371     }
372
373     pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
374         unsafe {
375             let len = cursor.capacity();
376             let mut out_num_bytes = MaybeUninit::uninit();
377             error::SolidError::err_if_negative(abi::SOLID_FS_Read(
378                 self.fd.raw(),
379                 cursor.as_mut().as_mut_ptr() as *mut u8,
380                 len,
381                 out_num_bytes.as_mut_ptr(),
382             ))
383             .map_err(|e| e.as_io_error())?;
384
385             // Safety: `out_num_bytes` is filled by the successful call to
386             // `SOLID_FS_Read`
387             let num_bytes_read = out_num_bytes.assume_init();
388
389             // Safety: `num_bytes_read` bytes were written to the unfilled
390             // portion of the buffer
391             cursor.advance(num_bytes_read);
392
393             Ok(())
394         }
395     }
396
397     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
398         crate::io::default_read_vectored(|buf| self.read(buf), bufs)
399     }
400
401     pub fn is_read_vectored(&self) -> bool {
402         false
403     }
404
405     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
406         unsafe {
407             let mut out_num_bytes = MaybeUninit::uninit();
408             error::SolidError::err_if_negative(abi::SOLID_FS_Write(
409                 self.fd.raw(),
410                 buf.as_ptr(),
411                 buf.len(),
412                 out_num_bytes.as_mut_ptr(),
413             ))
414             .map_err(|e| e.as_io_error())?;
415             Ok(out_num_bytes.assume_init())
416         }
417     }
418
419     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
420         crate::io::default_write_vectored(|buf| self.write(buf), bufs)
421     }
422
423     pub fn is_write_vectored(&self) -> bool {
424         false
425     }
426
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())?;
430         Ok(())
431     }
432
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),
440         };
441         error::SolidError::err_if_negative(unsafe {
442             abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
443         })
444         .map_err(|e| e.as_io_error())?;
445
446         // Get the new offset
447         unsafe {
448             let mut out_offset = MaybeUninit::uninit();
449             error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
450                 self.fd.raw(),
451                 out_offset.as_mut_ptr(),
452             ))
453             .map_err(|e| e.as_io_error())?;
454             Ok(out_offset.assume_init() as u64)
455         }
456     }
457
458     pub fn duplicate(&self) -> io::Result<File> {
459         unsupported()
460     }
461
462     pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
463         unsupported()
464     }
465
466     pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
467         unsupported()
468     }
469 }
470
471 impl Drop for File {
472     fn drop(&mut self) {
473         unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
474     }
475 }
476
477 impl DirBuilder {
478     pub fn new() -> DirBuilder {
479         DirBuilder {}
480     }
481
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())?;
485         Ok(())
486     }
487 }
488
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()
492     }
493 }
494
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"))
498     } else {
499         error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
500             .map_err(|e| e.as_io_error())?;
501         Ok(())
502     }
503 }
504
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())
508     })
509     .map_err(|e| e.as_io_error())?;
510     Ok(())
511 }
512
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())
516     })
517     .map_err(|e| e.as_io_error())?;
518     Ok(())
519 }
520
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())?;
525         Ok(())
526     } else {
527         Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory"))
528     }
529 }
530
531 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
532     for child in readdir(path)? {
533         let child = child?;
534         let child_type = child.file_type()?;
535         if child_type.is_dir() {
536             remove_dir_all(&child.path())?;
537         } else {
538             unlink(&child.path())?;
539         }
540     }
541     rmdir(path)
542 }
543
544 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
545     // This target doesn't support symlinks
546     stat(p)?;
547     Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link"))
548 }
549
550 pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
551     // This target doesn't support symlinks
552     unsupported()
553 }
554
555 pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
556     // This target doesn't support symlinks
557     unsupported()
558 }
559
560 pub fn stat(p: &Path) -> io::Result<FileAttr> {
561     // This target doesn't support symlinks
562     lstat(p)
563 }
564
565 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
566     unsafe {
567         let mut out_stat = MaybeUninit::uninit();
568         error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
569             cstr(p)?.as_ptr(),
570             out_stat.as_mut_ptr(),
571         ))
572         .map_err(|e| e.as_io_error())?;
573         Ok(FileAttr { stat: out_stat.assume_init() })
574     }
575 }
576
577 pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
578     unsupported()
579 }
580
581 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
582     use crate::fs::File;
583
584     let mut reader = File::open(from)?;
585     let mut writer = File::create(to)?;
586
587     io::copy(&mut reader, &mut writer)
588 }