diff options
| author | Ronald Kinard <[email protected]> | 2017-03-11 23:21:30 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2017-03-11 23:21:30 -0600 |
| commit | 276413d9b3ee6cc6b7e10f931fee30fe32c06d79 (patch) | |
| tree | 6beffcbe915babb3eda6512eff4ae30f74f60344 /ctr-std/src/sys | |
| parent | Merge pull request #26 from FenrirWolf/thread (diff) | |
| parent | Add fs module (diff) | |
| download | ctru-rs-276413d9b3ee6cc6b7e10f931fee30fe32c06d79.tar.xz ctru-rs-276413d9b3ee6cc6b7e10f931fee30fe32c06d79.zip | |
Merge pull request #28 from FenrirWolf/fs
Add std::fs module
Diffstat (limited to 'ctr-std/src/sys')
| -rw-r--r-- | ctr-std/src/sys/unix/fd.rs | 18 | ||||
| -rw-r--r-- | ctr-std/src/sys/unix/fs.rs | 575 | ||||
| -rw-r--r-- | ctr-std/src/sys/unix/mod.rs | 1 |
3 files changed, 594 insertions, 0 deletions
diff --git a/ctr-std/src/sys/unix/fd.rs b/ctr-std/src/sys/unix/fd.rs index 917a34b..91adac1 100644 --- a/ctr-std/src/sys/unix/fd.rs +++ b/ctr-std/src/sys/unix/fd.rs @@ -109,6 +109,24 @@ impl FileDesc { .map(|n| n as usize) } } + + // This is a unix-specific operation that the 3DS likely doesn't support. + // However, there's no reason to make calling this function an error either. + pub fn set_cloexec(&self) -> io::Result<()> { + Ok(()) + } + + // This is a unix-specific operation that the 3DS likely doesn't support. + // However, there's no reason to make calling this function an error either. + pub fn set_nonblocking(&self) -> io::Result<()> { + Ok(()) + } + + // The sdmc and romfs devoptabs definitely don't support this operation. + // Not sure if it will be needed for network support or not. + pub fn duplicate(&self) -> io::Result<FileDesc> { + unimplemented!() + } } impl<'a> Read for &'a FileDesc { diff --git a/ctr-std/src/sys/unix/fs.rs b/ctr-std/src/sys/unix/fs.rs new file mode 100644 index 0000000..06413c3 --- /dev/null +++ b/ctr-std/src/sys/unix/fs.rs @@ -0,0 +1,575 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::unix::prelude::*; + +use ffi::{CString, CStr, OsString, OsStr}; +use fmt; +use io::{self, Error, ErrorKind, SeekFrom}; +use libc::{self, c_int, mode_t}; +use mem; +use path::{Path, PathBuf}; +use ptr; +use sync::Arc; +use sys::fd::FileDesc; +use sys::time::SystemTime; +use sys::{cvt, cvt_r}; +use sys_common::{AsInner, FromInner}; + +use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek as lseek64, + dirent as dirent64, open as open64, ftruncate as ftruncate64, off_t as off64_t}; +use libc::{readdir_r as readdir64_r}; + +pub struct File(FileDesc); + +#[derive(Clone)] +pub struct FileAttr { + stat: stat64, +} + +pub struct ReadDir { + dirp: Dir, + root: Arc<PathBuf>, +} + +struct Dir(*mut libc::DIR); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + entry: dirent64, + root: Arc<PathBuf>, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: i32, + mode: mode_t, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { mode: mode_t } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { mode: mode_t } + +#[derive(Debug)] +pub struct DirBuilder { mode: mode_t } + +impl FileAttr { + pub fn size(&self) -> u64 { self.stat.st_size as u64 } + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } +} + +impl FileAttr { + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime as libc::time_t, + tv_nsec: 0, + })) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime as libc::time_t, + tv_nsec: 0, + })) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Err(io::Error::new(io::ErrorKind::Other, + "creation time is not available on this platform \ + currently")) + } +} + +impl AsInner<stat64> for FileAttr { + fn as_inner(&self) -> &stat64 { &self.stat } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.mode &= !0o222; + } else { + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { self.mode as u32 } +} + +impl FileType { + pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } + pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } + pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } + + pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } +} + +impl FromInner<u32> for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as mode_t } + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + unsafe { + let mut ret = DirEntry { + entry: mem::zeroed(), + root: self.root.clone() + }; + let mut entry_ptr = ptr::null_mut(); + loop { + if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + return Some(Err(Error::last_os_error())) + } + if entry_ptr.is_null() { + return None + } + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)) + } + } + } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + let r = unsafe { libc::closedir(self.0) }; + debug_assert_eq!(r, 0); + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.root.join(OsStr::from_bytes(self.name_bytes())) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result<FileType> { + match self.entry.d_type { + libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), + libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), + libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), + libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), + libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), + libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), + libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), + _ => lstat(&self.path()).map(|m| m.file_type()), + } + } + + pub fn ino(&self) -> u64 { + self.entry.d_ino as u64 + } + + fn name_bytes(&self) -> &[u8] { + unsafe { + CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() + } + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + custom_flags: 0, + mode: 0o666, + } + } + + pub fn read(&mut self, read: bool) { self.read = read; } + pub fn write(&mut self, write: bool) { self.write = write; } + pub fn append(&mut self, append: bool) { self.append = append; } + pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } + pub fn create(&mut self, create: bool) { self.create = create; } + pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } + + pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; } + pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; } + + fn get_access_mode(&self) -> io::Result<c_int> { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(libc::O_RDONLY), + (false, true, false) => Ok(libc::O_WRONLY), + (true, true, false) => Ok(libc::O_RDWR), + (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), + (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), + (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), + } + } + + fn get_creation_mode(&self) -> io::Result<c_int> { + match (self.write, self.append) { + (true, false) => {} + (false, false) => + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + }, + (_, true) => + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + }, + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => libc::O_CREAT, + (false, true, false) => libc::O_TRUNC, + (true, true, false) => libc::O_CREAT | libc::O_TRUNC, + (_, _, true) => libc::O_CREAT | libc::O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let path = cstr(path)?; + File::open_c(&path, opts) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { + let flags = libc::O_CLOEXEC | + opts.get_access_mode()? | + opts.get_creation_mode()? | + (opts.custom_flags as c_int & !libc::O_ACCMODE); + let fd = cvt_r(|| unsafe { + open64(path.as_ptr(), flags, opts.mode as c_int) + })?; + let fd = FileDesc::new(fd); + + // Currently the standard library supports Linux 2.6.18 which did not + // have the O_CLOEXEC flag (passed above). If we're running on an older + // Linux kernel then the flag is just ignored by the OS, so we continue + // to explicitly ask for a CLOEXEC fd here. + // + // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc + // that we support, so we only do this on Linux currently. + if cfg!(target_os = "linux") { + fd.set_cloexec()?; + } + + Ok(File(fd)) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + fstat64(self.0.raw(), &mut stat) + })?; + Ok(FileAttr { stat: stat }) + } + + pub fn fsync(&self) -> io::Result<()> { + cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?; + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { + cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; + return Ok(()); + + unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + return cvt_r(|| unsafe { + ftruncate64(self.0.raw(), size as off64_t) + }).map(|_| ()); + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.0.read_at(buf, offset) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.0.write_at(buf, offset) + } + + pub fn flush(&self) -> io::Result<()> { Ok(()) } + + pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { + let (whence, pos) = match pos { + // Casting to `i64` is fine, too large values will end up as + // negative which will cause an error in `lseek64`. + SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), + SeekFrom::End(off) => (libc::SEEK_END, off), + SeekFrom::Current(off) => (libc::SEEK_CUR, off), + }; + let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; + Ok(n as u64) + } + + pub fn duplicate(&self) -> io::Result<File> { + self.0.duplicate().map(File) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + + pub fn into_fd(self) -> FileDesc { self.0 } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; + Ok(()) + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; + Ok(()) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } +} + +fn cstr(path: &Path) -> io::Result<CString> { + Ok(CString::new(path.as_os_str().as_bytes())?) +} + +impl FromInner<c_int> for File { + fn from_inner(fd: c_int) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn get_path(_fd: c_int) -> Option<PathBuf> { + // FIXME(#24570): implement this for other Unix platforms + None + } + + fn get_mode(_fd: c_int) -> Option<(bool, bool)> { + // FIXME(#24570): implement this for other Unix platforms + None + } + + let fd = self.0.raw(); + let mut b = f.debug_struct("File"); + b.field("fd", &fd); + if let Some(path) = get_path(fd) { + b.field("path", &path); + } + if let Some((read, write)) = get_mode(fd) { + b.field("read", &read).field("write", &write); + } + b.finish() + } +} + +pub fn readdir(p: &Path) -> io::Result<ReadDir> { + let root = Arc::new(p.to_path_buf()); + let p = cstr(p)?; + unsafe { + let ptr = libc::opendir(p.as_ptr()); + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + Ok(ReadDir { dirp: Dir(ptr), root: root }) + } + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::unlink(p.as_ptr()) })?; + Ok(()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = cstr(old)?; + let new = cstr(new)?; + cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; + Ok(()) +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let p = cstr(p)?; + cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::rmdir(p.as_ptr()) })?; + Ok(()) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = lstat(path)?.file_type(); + if filetype.is_symlink() { + unlink(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in readdir(path)? { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + unlink(&child.path())?; + } + } + rmdir(path) +} + +pub fn readlink(p: &Path) -> io::Result<PathBuf> { + let c_path = cstr(p)?; + let p = c_path.as_ptr(); + + let mut buf = Vec::with_capacity(256); + + loop { + let buf_read = cvt(unsafe { + libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) + })? as usize; + + unsafe { buf.set_len(buf_read); } + + if buf_read != buf.capacity() { + buf.shrink_to_fit(); + + return Ok(PathBuf::from(OsString::from_vec(buf))); + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. The length is guaranteed to be + // the same as the capacity due to the if statement above. + buf.reserve(1); + } +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn stat(p: &Path) -> io::Result<FileAttr> { + let p = cstr(p)?; + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + stat64(p.as_ptr(), &mut stat as *mut _ as *mut _) + })?; + Ok(FileAttr { stat: stat }) +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + let p = cstr(p)?; + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + lstat64(p.as_ptr(), &mut stat as *mut _ as *mut _) + })?; + Ok(FileAttr { stat: stat }) +} + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + let path = CString::new(p.as_os_str().as_bytes())?; + let buf; + unsafe { + let r = libc::realpath(path.as_ptr(), ptr::null_mut()); + if r.is_null() { + return Err(io::Error::last_os_error()) + } + buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + } + Ok(PathBuf::from(OsString::from_vec(buf))) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use fs::{File, set_permissions}; + if !from.is_file() { + return Err(Error::new(ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + set_permissions(to, perm)?; + Ok(ret) +} diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs index 76a4555..5e44d34 100644 --- a/ctr-std/src/sys/unix/mod.rs +++ b/ctr-std/src/sys/unix/mod.rs @@ -17,6 +17,7 @@ pub mod condvar; pub mod ext; pub mod fast_thread_local; pub mod fd; +pub mod fs; pub mod stdio; pub mod memchr; pub mod mutex; |