use crate::os::unix::prelude::*;
use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::fd::FileDesc;
use crate::sys::time::SystemTime;
use crate::sys::{cvt_ocall, cvt_ocall_r};
use crate::sys::unsupported::unsupported;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
use sgx_oc::ocall::DirPtr;
use sgx_oc::{c_int, dirent64, mode_t, off64_t, stat64};
pub use crate::sys_common::fs::try_exists;
pub struct File(FileDesc);
#[derive(Clone)]
pub struct FileAttr {
stat: stat64,
}
struct InnerReadDir {
dirp: Dir,
root: PathBuf,
}
pub struct ReadDir {
inner: Arc<InnerReadDir>,
end_of_stream: bool,
}
impl ReadDir {
fn new(inner: InnerReadDir) -> Self {
Self { inner: Arc::new(inner), end_of_stream: false }
}
}
struct Dir(DirPtr);
unsafe impl Send for Dir {}
unsafe impl Sync for Dir {}
pub struct DirEntry {
entry: dirent64,
dir: Arc<InnerReadDir>,
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
custom_flags: i32,
mode: mode_t,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions {
mode: mode_t,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct FileTimes {
accessed: Option<SystemTime>,
modified: Option<SystemTime>,
}
#[derive(Copy, Clone, Eq, Debug)]
pub struct FileType {
mode: mode_t,
}
impl PartialEq for FileType {
fn eq(&self, other: &Self) -> bool {
self.masked() == other.masked()
}
}
impl core::hash::Hash for FileType {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.masked().hash(state);
}
}
#[derive(Debug)]
pub struct DirBuilder {
mode: mode_t,
}
impl FileAttr {
fn from_stat64(stat: stat64) -> Self {
Self { stat }
}
}
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) }
}
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::new(self.stat.st_mtime, self.stat.st_mtime_nsec))
}
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::new(self.stat.st_atime, self.stat.st_atime_nsec))
}
pub fn created(&self) -> io::Result<SystemTime> {
Err(io::const_io_error!(
io::ErrorKind::Unsupported,
"creation time is not available on this platform \
currently",
))
}
}
impl AsInner<stat64> for FileAttr {
#[inline]
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
}
}
impl FileTimes {
pub fn set_accessed(&mut self, t: SystemTime) {
self.accessed = Some(t);
}
pub fn set_modified(&mut self, t: SystemTime) {
self.modified = Some(t);
}
}
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.masked() == mode
}
fn masked(&self) -> mode_t {
self.mode & libc::S_IFMT
}
}
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 {
fmt::Debug::fmt(&*self.inner.root, f)
}
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
if self.end_of_stream {
return None;
}
unsafe {
let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
let mut entry_ptr = ptr::null_mut();
loop {
if let Err(e) = libc::readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) {
if entry_ptr.is_null() {
self.end_of_stream = true;
}
return Some(Err(e.into()));
}
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) };
assert!(
r.is_ok() || crate::io::Error::last_os_error().is_interrupted(),
"unexpected error during closedir: {:?}",
crate::io::Error::last_os_error()
);
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.dir.root.join(self.file_name_os_str())
}
pub fn file_name(&self) -> OsString {
self.file_name_os_str().to_os_string()
}
pub fn metadata(&self) -> io::Result<FileAttr> {
let fd = cvt_ocall(unsafe { libc::dirfd(self.dir.dirp.0) })?;
let mut stat: stat64 = unsafe { mem::zeroed() };
let dname_bytes: Vec<u8> = self.entry.d_name.iter().map(|b| *b as u8).collect();
let dname = unsafe { libc::shrink_to_fit_cstring(dname_bytes) }?;
cvt_ocall(unsafe { libc::fstatat64(fd, &dname, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
Ok(FileAttr::from_stat64(stat))
}
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 }),
_ => self.metadata().map(|m| m.file_type()),
}
}
pub fn ino(&self) -> u64 {
self.entry.d_ino
}
fn name_bytes(&self) -> &[u8] {
self.name_cstr().to_bytes()
}
fn name_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
}
pub fn file_name_os_str(&self) -> &OsStr {
OsStr::from_bytes(self.name_bytes())
}
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions {
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
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> {
run_path_with_cstr(path, |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_ocall_r(|| unsafe { libc::open64(path, flags, opts.mode) })?;
Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
let fd = self.as_raw_fd();
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt_ocall(unsafe { libc::fstat64(fd, &mut stat) })?;
Ok(FileAttr::from_stat64(stat))
}
pub fn fsync(&self) -> io::Result<()> {
cvt_ocall_r(|| unsafe { libc::fsync(self.as_raw_fd()) })?;
Ok(())
}
pub fn datasync(&self) -> io::Result<()> {
cvt_ocall_r(|| unsafe { libc::fdatasync(self.as_raw_fd()) })
}
pub fn truncate(&self, size: u64) -> io::Result<()> {
let size: off64_t =
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
cvt_ocall_r(|| unsafe { libc::ftruncate64(self.as_raw_fd(), size) }).map(drop)
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
self.0.is_read_vectored()
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.0.read_at(buf, offset)
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
self.0.read_buf(cursor)
}
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
self.0.read_vectored_at(bufs, offset)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
self.0.is_write_vectored()
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.0.write_at(buf, offset)
}
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
self.0.write_vectored_at(bufs, offset)
}
#[inline]
pub fn flush(&self) -> io::Result<()> {
Ok(())
}
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
let (whence, pos) = match pos {
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_ocall(unsafe { libc::lseek64(self.as_raw_fd(), pos, whence) })?;
Ok(n)
}
pub fn duplicate(&self) -> io::Result<File> {
self.0.duplicate().map(File)
}
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
cvt_ocall_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
Ok(())
}
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
let to_timespec = |time: Option<SystemTime>| match time {
Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
"timestamp is too large to set as a file time"
)),
Some(_) => Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
"timestamp is too small to set as a file time"
)),
None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
};
let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
cvt_ocall(unsafe { libc::futimens(self.as_raw_fd(), ×) })?;
Ok(())
}
}
impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder { mode: 0o777 }
}
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
run_path_with_cstr(p, |p| cvt_ocall(unsafe { libc::mkdir(p, self.mode) }).map(|_| ()))
}
pub fn set_mode(&mut self, mode: u32) {
self.mode = mode as mode_t;
}
}
impl AsInner<FileDesc> for File {
#[inline]
fn as_inner(&self) -> &FileDesc {
&self.0
}
}
impl AsInnerMut<FileDesc> for File {
#[inline]
fn as_inner_mut(&mut self) -> &mut FileDesc {
&mut self.0
}
}
impl IntoInner<FileDesc> for File {
fn into_inner(self) -> FileDesc {
self.0
}
}
impl FromInner<FileDesc> for File {
fn from_inner(file_desc: FileDesc) -> Self {
Self(file_desc)
}
}
impl AsFd for File {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for File {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoRawFd for File {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl FromRawFd for File {
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
Self(FromRawFd::from_raw_fd(raw_fd))
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn get_path(fd: c_int) -> Option<PathBuf> {
let mut p = PathBuf::from("/proc/self/fd");
p.push(&fd.to_string());
readlink(&p).ok()
}
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
match unsafe { libc::fcntl_arg0(fd, libc::F_GETFL) } {
Err(_) => None,
Ok(mode) => match mode & libc::O_ACCMODE {
libc::O_RDONLY => Some((true, false)),
libc::O_RDWR => Some((true, true)),
libc::O_WRONLY => Some((false, true)),
_ => None,
}
}
}
let fd = self.as_raw_fd();
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(path: &Path) -> io::Result<ReadDir> {
let ptr = run_path_with_cstr(path, |p| cvt_ocall(unsafe { libc::opendir(p) }))?;
if ptr.is_null() {
Err(Error::last_os_error())
} else {
let root = path.to_path_buf();
let inner = InnerReadDir { dirp: Dir(ptr), root };
Ok(ReadDir {
inner: Arc::new(inner),
end_of_stream: false,
})
}
}
pub fn unlink(p: &Path) -> io::Result<()> {
run_path_with_cstr(p, |p| cvt_ocall(unsafe { libc::unlink(p) }).map(|_| ()))
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
run_path_with_cstr(old, |old| {
run_path_with_cstr(new, |new| {
cvt_ocall(unsafe { libc::rename(old, new) }).map(|_| ())
})
})
}
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
run_path_with_cstr(p, |p| cvt_ocall_r(|| unsafe { libc::chmod(p, perm.mode) }).map(|_| ()))
}
pub fn rmdir(p: &Path) -> io::Result<()> {
run_path_with_cstr(p, |p| cvt_ocall(unsafe { libc::rmdir(p) }).map(|_| ()))
}
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
run_path_with_cstr(p, |p| {
cvt_ocall(unsafe { libc::readlink(p) }).map(|v| PathBuf::from(OsString::from_vec(v)))
})
}
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
run_path_with_cstr(original, |original| {
run_path_with_cstr(link, |link| {
cvt_ocall(unsafe { libc::symlink(original, link) }).map(|_| ())
})
})
}
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
run_path_with_cstr(original, |original| {
run_path_with_cstr(link, |link| {
cvt_ocall(unsafe { libc::linkat(libc::AT_FDCWD, original, libc::AT_FDCWD, link, 0) })
})
})
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
run_path_with_cstr(p, |p| {
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt_ocall(unsafe { libc::stat64(p, &mut stat) })?;
Ok(FileAttr::from_stat64(stat))
})
}
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
run_path_with_cstr(p, |p| {
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt_ocall(unsafe { libc::lstat64(p, &mut stat) })?;
Ok(FileAttr::from_stat64(stat))
})
}
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
run_path_with_cstr(p, |p| {
cvt_ocall(unsafe { libc::realpath(p) }).map(|v| PathBuf::from(OsString::from_vec(v.into_bytes())))
})
}
fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
use crate::fs::File;
use crate::sys_common::fs::NOT_FILE_ERROR;
let reader = File::open(from)?;
let metadata = reader.metadata()?;
if !metadata.is_file() {
return Err(NOT_FILE_ERROR);
}
Ok((reader, metadata))
}
fn open_to_and_set_permissions(
to: &Path,
reader_metadata: crate::fs::Metadata,
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
use crate::fs::OpenOptions;
let perm = reader_metadata.permissions();
let writer = OpenOptions::new()
.mode(perm.mode())
.write(true)
.create(true)
.truncate(true)
.open(to)?;
let writer_metadata = writer.metadata()?;
if writer_metadata.is_file() {
writer.set_permissions(perm)?;
}
Ok((writer, writer_metadata))
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let (mut reader, reader_metadata) = open_from(from)?;
let max_len = u64::MAX;
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
use super::kernel_copy::{copy_regular_files, CopyResult};
match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
CopyResult::Ended(bytes) => Ok(bytes),
CopyResult::Error(e, _) => Err(e),
CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
Ok(bytes) => Ok(bytes + written),
Err(e) => Err(e),
},
}
}
pub fn chown(_path: &Path, _uid: u32, _gid: u32) -> io::Result<()> {
unsupported()
}
pub fn fchown(_fd: c_int, _uid: u32, _gid: u32) -> io::Result<()> {
unsupported()
}
pub fn lchown(_path: &Path, _uid: u32, _gid: u32) -> io::Result<()> {
unsupported()
}
pub fn chroot(_dir: &Path) -> io::Result<()> {
unsupported()
}
mod libc {
pub use sgx_oc::ocall::{
chmod, closedir, dirfd, fchmod, fcntl_arg0, fdatasync, fstat64, fstatat64, fsync, ftruncate64, futimens,
linkat, lseek64, lstat64, mkdir, open64, opendir, readdir64_r, readlink, realpath, rename, rmdir,
stat64, shrink_to_fit_cstring, symlink, unlink,
};
pub use sgx_oc::*;
}
pub use remove_dir_impl::remove_dir_all;
mod remove_dir_impl {
use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir};
use crate::ffi::CStr;
use crate::io;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use crate::os::unix::prelude::{OwnedFd, RawFd};
use crate::path::{Path, PathBuf};
use crate::sync::Arc;
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::{cvt_ocall, cvt_ocall_r};
use libc::{fdopendir, openat, unlinkat};
pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
let fd = cvt_ocall_r(|| unsafe {
openat(
parent_fd.unwrap_or(libc::AT_FDCWD),
p,
libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
)
})?;
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
let ptr = cvt_ocall(unsafe { fdopendir(dir_fd.as_raw_fd()) })?;
if ptr.is_null() {
return Err(io::Error::last_os_error());
}
let dirp = Dir(ptr);
let new_parent_fd = dir_fd.into_raw_fd();
let dummy_root = PathBuf::new();
Ok((
ReadDir {
inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
end_of_stream: false,
},
new_parent_fd,
))
}
fn is_dir(ent: &DirEntry) -> Option<bool> {
match ent.entry.d_type {
libc::DT_UNKNOWN => None,
libc::DT_DIR => Some(true),
_ => Some(false),
}
}
fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
let fd = match openat_nofollow_dironly(parent_fd, path) {
Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
return match parent_fd {
Some(parent_fd) => {
cvt_ocall(unsafe { unlinkat(parent_fd, path, 0) }).map(drop)
}
None => Err(err),
};
}
result => result?,
};
let (dir, fd) = fdreaddir(fd)?;
for child in dir {
let child = child?;
let child_name = child.name_cstr();
match is_dir(&child) {
Some(true) => {
remove_dir_all_recursive(Some(fd), child_name)?;
}
Some(false) => {
cvt_ocall(unsafe { unlinkat(fd, child_name, 0) })?;
}
None => {
remove_dir_all_recursive(Some(fd), child_name)?;
}
}
}
cvt_ocall(unsafe {
unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path, libc::AT_REMOVEDIR)
})?;
Ok(())
}
fn remove_dir_all_modern(p: &Path) -> io::Result<()> {
let attr = lstat(p)?;
if attr.file_type().is_symlink() {
crate::fs::remove_file(p)
} else {
run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &p))
}
}
pub fn remove_dir_all(p: &Path) -> io::Result<()> {
remove_dir_all_modern(p)
}
mod libc {
pub use sgx_oc::ocall::{fdopendir, openat, unlinkat};
pub use sgx_oc::*;
}
}