1 #![deny(unsafe_op_in_unsafe_fn)]
4 use crate::ffi
::{CStr, OsStr, OsString}
;
6 use crate::io
::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}
;
8 use crate::mem
::{self, ManuallyDrop}
;
9 use crate::os
::raw
::c_int
;
10 use crate::os
::wasi
::ffi
::{OsStrExt, OsStringExt}
;
11 use crate::os
::wasi
::io
::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}
;
12 use crate::path
::{Path, PathBuf}
;
15 use crate::sys
::common
::small_c_string
::run_path_with_cstr
;
16 use crate::sys
::time
::SystemTime
;
17 use crate::sys
::unsupported
;
18 use crate::sys_common
::{AsInner, FromInner, IntoInner}
;
20 pub use crate::sys_common
::fs
::try_exists
;
32 inner
: Arc
<ReadDirInner
>,
33 cookie
: Option
<wasi
::Dircookie
>,
47 inner
: Arc
<ReadDirInner
>,
50 #[derive(Clone, Debug, Default)]
51 pub struct OpenOptions
{
55 dirflags
: wasi
::Lookupflags
,
56 fdflags
: wasi
::Fdflags
,
58 rights_base
: Option
<wasi
::Rights
>,
59 rights_inheriting
: Option
<wasi
::Rights
>,
62 #[derive(Clone, PartialEq, Eq, Debug)]
63 pub struct FilePermissions
{
67 #[derive(Copy, Clone, Debug, Default)]
68 pub struct FileTimes
{
69 accessed
: Option
<SystemTime
>,
70 modified
: Option
<SystemTime
>,
73 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
79 pub struct DirBuilder {}
82 pub fn size(&self) -> u64 {
86 pub fn perm(&self) -> FilePermissions
{
87 // not currently implemented in wasi yet
88 FilePermissions { readonly: false }
91 pub fn file_type(&self) -> FileType
{
92 FileType { bits: self.meta.filetype }
95 pub fn modified(&self) -> io
::Result
<SystemTime
> {
96 Ok(SystemTime
::from_wasi_timestamp(self.meta
.mtim
))
99 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
100 Ok(SystemTime
::from_wasi_timestamp(self.meta
.atim
))
103 pub fn created(&self) -> io
::Result
<SystemTime
> {
104 Ok(SystemTime
::from_wasi_timestamp(self.meta
.ctim
))
107 pub fn as_wasi(&self) -> &wasi
::Filestat
{
112 impl FilePermissions
{
113 pub fn readonly(&self) -> bool
{
117 pub fn set_readonly(&mut self, readonly
: bool
) {
118 self.readonly
= readonly
;
123 pub fn set_accessed(&mut self, t
: SystemTime
) {
124 self.accessed
= Some(t
);
127 pub fn set_modified(&mut self, t
: SystemTime
) {
128 self.modified
= Some(t
);
133 pub fn is_dir(&self) -> bool
{
134 self.bits
== wasi
::FILETYPE_DIRECTORY
137 pub fn is_file(&self) -> bool
{
138 self.bits
== wasi
::FILETYPE_REGULAR_FILE
141 pub fn is_symlink(&self) -> bool
{
142 self.bits
== wasi
::FILETYPE_SYMBOLIC_LINK
145 pub fn bits(&self) -> wasi
::Filetype
{
151 fn new(dir
: File
, root
: PathBuf
) -> ReadDir
{
157 inner
: Arc
::new(ReadDirInner { dir, root }
),
162 impl fmt
::Debug
for ReadDir
{
163 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
164 f
.debug_struct("ReadDir").finish_non_exhaustive()
168 impl Iterator
for ReadDir
{
169 type Item
= io
::Result
<DirEntry
>;
171 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
173 // If we've reached the capacity of our buffer then we need to read
174 // some more from the OS, otherwise we pick up at our old offset.
175 let offset
= if self.offset
== self.cap
{
176 let cookie
= self.cookie
.take()?
;
177 match self.inner
.dir
.fd
.readdir(&mut self.buf
, cookie
) {
178 Ok(bytes
) => self.cap
= bytes
,
179 Err(e
) => return Some(Err(e
)),
182 self.cookie
= Some(cookie
);
184 // If we didn't actually read anything, this is in theory the
185 // end of the directory.
195 let data
= &self.buf
[offset
..self.cap
];
197 // If we're not able to read a directory entry then that means it
198 // must have been truncated at the end of the buffer, so reset our
199 // offset so we can go back and reread into the buffer, picking up
200 // where we last left off.
201 let dirent_size
= mem
::size_of
::<wasi
::Dirent
>();
202 if data
.len() < dirent_size
{
203 assert
!(self.cookie
.is_some());
204 assert
!(self.buf
.len() >= dirent_size
);
205 self.offset
= self.cap
;
208 let (dirent
, data
) = data
.split_at(dirent_size
);
209 let dirent
= unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }
;
211 // If the file name was truncated, then we need to reinvoke
212 // `readdir` so we truncate our buffer to start over and reread this
213 // descriptor. Note that if our offset is 0 that means the file name
214 // is massive and we need a bigger buffer.
215 if data
.len() < dirent
.d_namlen
as usize {
217 let amt_to_add
= self.buf
.capacity();
218 self.buf
.extend(iter
::repeat(0).take(amt_to_add
));
220 assert
!(self.cookie
.is_some());
221 self.offset
= self.cap
;
224 self.cookie
= Some(dirent
.d_next
);
225 self.offset
= offset
+ dirent_size
+ dirent
.d_namlen
as usize;
227 let name
= &data
[..(dirent
.d_namlen
as usize)];
229 // These names are skipped on all other platforms, so let's skip
231 if name
== b
"." || name
== b
".." {
235 return Some(Ok(DirEntry
{
238 inner
: self.inner
.clone(),
245 pub fn path(&self) -> PathBuf
{
246 let name
= OsStr
::from_bytes(&self.name
);
247 self.inner
.root
.join(name
)
250 pub fn file_name(&self) -> OsString
{
251 OsString
::from_vec(self.name
.clone())
254 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
255 metadata_at(&self.inner
.dir
.fd
, 0, OsStr
::from_bytes(&self.name
).as_ref())
258 pub fn file_type(&self) -> io
::Result
<FileType
> {
259 Ok(FileType { bits: self.meta.d_type }
)
262 pub fn ino(&self) -> wasi
::Inode
{
268 pub fn new() -> OpenOptions
{
269 let mut base
= OpenOptions
::default();
270 base
.dirflags
= wasi
::LOOKUPFLAGS_SYMLINK_FOLLOW
;
274 pub fn read(&mut self, read
: bool
) {
278 pub fn write(&mut self, write
: bool
) {
282 pub fn truncate(&mut self, truncate
: bool
) {
283 self.oflag(wasi
::OFLAGS_TRUNC
, truncate
);
286 pub fn create(&mut self, create
: bool
) {
287 self.oflag(wasi
::OFLAGS_CREAT
, create
);
290 pub fn create_new(&mut self, create_new
: bool
) {
291 self.oflag(wasi
::OFLAGS_EXCL
, create_new
);
292 self.oflag(wasi
::OFLAGS_CREAT
, create_new
);
295 pub fn directory(&mut self, directory
: bool
) {
296 self.oflag(wasi
::OFLAGS_DIRECTORY
, directory
);
299 fn oflag(&mut self, bit
: wasi
::Oflags
, set
: bool
) {
307 pub fn append(&mut self, append
: bool
) {
308 self.append
= append
;
309 self.fdflag(wasi
::FDFLAGS_APPEND
, append
);
312 pub fn dsync(&mut self, set
: bool
) {
313 self.fdflag(wasi
::FDFLAGS_DSYNC
, set
);
316 pub fn nonblock(&mut self, set
: bool
) {
317 self.fdflag(wasi
::FDFLAGS_NONBLOCK
, set
);
320 pub fn rsync(&mut self, set
: bool
) {
321 self.fdflag(wasi
::FDFLAGS_RSYNC
, set
);
324 pub fn sync(&mut self, set
: bool
) {
325 self.fdflag(wasi
::FDFLAGS_SYNC
, set
);
328 fn fdflag(&mut self, bit
: wasi
::Fdflags
, set
: bool
) {
332 self.fdflags
&= !bit
;
336 pub fn fs_rights_base(&mut self, rights
: wasi
::Rights
) {
337 self.rights_base
= Some(rights
);
340 pub fn fs_rights_inheriting(&mut self, rights
: wasi
::Rights
) {
341 self.rights_inheriting
= Some(rights
);
344 fn rights_base(&self) -> wasi
::Rights
{
345 if let Some(rights
) = self.rights_base
{
349 // If rights haven't otherwise been specified try to pick a reasonable
350 // set. This can always be overridden by users via extension traits, and
351 // implementations may give us fewer rights silently than we ask for. So
352 // given that, just look at `read` and `write` and bucket permissions
356 base
|= wasi
::RIGHTS_FD_READ
;
357 base
|= wasi
::RIGHTS_FD_READDIR
;
359 if self.write
|| self.append
{
360 base
|= wasi
::RIGHTS_FD_WRITE
;
361 base
|= wasi
::RIGHTS_FD_DATASYNC
;
362 base
|= wasi
::RIGHTS_FD_ALLOCATE
;
363 base
|= wasi
::RIGHTS_FD_FILESTAT_SET_SIZE
;
366 // FIXME: some of these should probably be read-only or write-only...
367 base
|= wasi
::RIGHTS_FD_ADVISE
;
368 base
|= wasi
::RIGHTS_FD_FDSTAT_SET_FLAGS
;
369 base
|= wasi
::RIGHTS_FD_FILESTAT_GET
;
370 base
|= wasi
::RIGHTS_FD_FILESTAT_SET_TIMES
;
371 base
|= wasi
::RIGHTS_FD_SEEK
;
372 base
|= wasi
::RIGHTS_FD_SYNC
;
373 base
|= wasi
::RIGHTS_FD_TELL
;
374 base
|= wasi
::RIGHTS_PATH_CREATE_DIRECTORY
;
375 base
|= wasi
::RIGHTS_PATH_CREATE_FILE
;
376 base
|= wasi
::RIGHTS_PATH_FILESTAT_GET
;
377 base
|= wasi
::RIGHTS_PATH_LINK_SOURCE
;
378 base
|= wasi
::RIGHTS_PATH_LINK_TARGET
;
379 base
|= wasi
::RIGHTS_PATH_OPEN
;
380 base
|= wasi
::RIGHTS_PATH_READLINK
;
381 base
|= wasi
::RIGHTS_PATH_REMOVE_DIRECTORY
;
382 base
|= wasi
::RIGHTS_PATH_RENAME_SOURCE
;
383 base
|= wasi
::RIGHTS_PATH_RENAME_TARGET
;
384 base
|= wasi
::RIGHTS_PATH_SYMLINK
;
385 base
|= wasi
::RIGHTS_PATH_UNLINK_FILE
;
386 base
|= wasi
::RIGHTS_POLL_FD_READWRITE
;
391 fn rights_inheriting(&self) -> wasi
::Rights
{
392 self.rights_inheriting
.unwrap_or_else(|| self.rights_base())
395 pub fn lookup_flags(&mut self, flags
: wasi
::Lookupflags
) {
396 self.dirflags
= flags
;
401 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
402 let (dir
, file
) = open_parent(path
)?
;
403 open_at(&dir
, &file
, opts
)
406 pub fn open_at(&self, path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
407 open_at(&self.fd
, path
, opts
)
410 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
411 self.fd
.filestat_get().map(|meta
| FileAttr { meta }
)
414 pub fn metadata_at(&self, flags
: wasi
::Lookupflags
, path
: &Path
) -> io
::Result
<FileAttr
> {
415 metadata_at(&self.fd
, flags
, path
)
418 pub fn fsync(&self) -> io
::Result
<()> {
422 pub fn datasync(&self) -> io
::Result
<()> {
426 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
427 self.fd
.filestat_set_size(size
)
430 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
431 self.read_vectored(&mut [IoSliceMut
::new(buf
)])
434 pub fn read_vectored(&self, bufs
: &mut [IoSliceMut
<'_
>]) -> io
::Result
<usize> {
439 pub fn is_read_vectored(&self) -> bool
{
443 pub fn read_buf(&self, cursor
: BorrowedCursor
<'_
>) -> io
::Result
<()> {
444 crate::io
::default_read_buf(|buf
| self.read(buf
), cursor
)
447 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
448 self.write_vectored(&[IoSlice
::new(buf
)])
451 pub fn write_vectored(&self, bufs
: &[IoSlice
<'_
>]) -> io
::Result
<usize> {
456 pub fn is_write_vectored(&self) -> bool
{
460 pub fn flush(&self) -> io
::Result
<()> {
464 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
468 pub fn duplicate(&self) -> io
::Result
<File
> {
469 // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
473 pub fn set_permissions(&self, _perm
: FilePermissions
) -> io
::Result
<()> {
474 // Permissions haven't been fully figured out in wasi yet, so this is
479 pub fn set_times(&self, times
: FileTimes
) -> io
::Result
<()> {
480 let to_timestamp
= |time
: Option
<SystemTime
>| {
482 Some(time
) if let Some(ts
) = time
.to_wasi_timestamp() => Ok(ts
),
483 Some(_
) => Err(io
::const_io_error
!(io
::ErrorKind
::InvalidInput
, "timestamp is too large to set as a file time")),
487 self.fd
.filestat_set_times(
488 to_timestamp(times
.accessed
)?
,
489 to_timestamp(times
.modified
)?
,
490 times
.accessed
.map_or(0, |_
| wasi
::FSTFLAGS_ATIM
)
491 | times
.modified
.map_or(0, |_
| wasi
::FSTFLAGS_MTIM
),
495 pub fn read_link(&self, file
: &Path
) -> io
::Result
<PathBuf
> {
496 read_link(&self.fd
, file
)
500 impl AsInner
<WasiFd
> for File
{
501 fn as_inner(&self) -> &WasiFd
{
506 impl IntoInner
<WasiFd
> for File
{
507 fn into_inner(self) -> WasiFd
{
512 impl FromInner
<WasiFd
> for File
{
513 fn from_inner(fd
: WasiFd
) -> File
{
519 fn as_fd(&self) -> BorrowedFd
<'_
> {
524 impl AsRawFd
for File
{
525 fn as_raw_fd(&self) -> RawFd
{
530 impl IntoRawFd
for File
{
531 fn into_raw_fd(self) -> RawFd
{
532 self.fd
.into_raw_fd()
536 impl FromRawFd
for File
{
537 unsafe fn from_raw_fd(raw_fd
: RawFd
) -> Self {
538 unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) }
}
543 pub fn new() -> DirBuilder
{
547 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
548 let (dir
, file
) = open_parent(p
)?
;
549 dir
.create_directory(osstr2str(file
.as_ref())?
)
553 impl fmt
::Debug
for File
{
554 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
555 f
.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
559 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
560 let mut opts
= OpenOptions
::new();
561 opts
.directory(true);
563 let dir
= File
::open(p
, &opts
)?
;
564 Ok(ReadDir
::new(dir
, p
.to_path_buf()))
567 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
568 let (dir
, file
) = open_parent(p
)?
;
569 dir
.unlink_file(osstr2str(file
.as_ref())?
)
572 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
573 let (old
, old_file
) = open_parent(old
)?
;
574 let (new
, new_file
) = open_parent(new
)?
;
575 old
.rename(osstr2str(old_file
.as_ref())?
, &new
, osstr2str(new_file
.as_ref())?
)
578 pub fn set_perm(_p
: &Path
, _perm
: FilePermissions
) -> io
::Result
<()> {
579 // Permissions haven't been fully figured out in wasi yet, so this is
584 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
585 let (dir
, file
) = open_parent(p
)?
;
586 dir
.remove_directory(osstr2str(file
.as_ref())?
)
589 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
590 let (dir
, file
) = open_parent(p
)?
;
591 read_link(&dir
, &file
)
594 fn read_link(fd
: &WasiFd
, file
: &Path
) -> io
::Result
<PathBuf
> {
595 // Try to get a best effort initial capacity for the vector we're going to
596 // fill. Note that if it's not a symlink we don't use a file to avoid
597 // allocating gigabytes if you read_link a huge movie file by accident.
598 // Additionally we add 1 to the initial size so if it doesn't change until
599 // when we call `readlink` the returned length will be less than the
600 // capacity, guaranteeing that we got all the data.
601 let meta
= metadata_at(fd
, 0, file
)?
;
602 let initial_size
= if meta
.file_type().is_symlink() {
603 (meta
.size() as usize).saturating_add(1)
605 1 // this'll fail in just a moment
608 // Now that we have an initial guess of how big to make our buffer, call
609 // `readlink` in a loop until it fails or reports it filled fewer bytes than
610 // we asked for, indicating we got everything.
611 let file
= osstr2str(file
.as_ref())?
;
612 let mut destination
= vec
![0u8; initial_size
];
614 let len
= fd
.readlink(file
, &mut destination
)?
;
615 if len
< destination
.len() {
616 destination
.truncate(len
);
617 destination
.shrink_to_fit();
618 return Ok(PathBuf
::from(OsString
::from_vec(destination
)));
620 let amt_to_add
= destination
.len();
621 destination
.extend(iter
::repeat(0).take(amt_to_add
));
625 pub fn symlink(original
: &Path
, link
: &Path
) -> io
::Result
<()> {
626 let (link
, link_file
) = open_parent(link
)?
;
627 link
.symlink(osstr2str(original
.as_ref())?
, osstr2str(link_file
.as_ref())?
)
630 pub fn link(original
: &Path
, link
: &Path
) -> io
::Result
<()> {
631 let (original
, original_file
) = open_parent(original
)?
;
632 let (link
, link_file
) = open_parent(link
)?
;
633 // Pass 0 as the flags argument, meaning don't follow symlinks.
634 original
.link(0, osstr2str(original_file
.as_ref())?
, &link
, osstr2str(link_file
.as_ref())?
)
637 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
638 let (dir
, file
) = open_parent(p
)?
;
639 metadata_at(&dir
, wasi
::LOOKUPFLAGS_SYMLINK_FOLLOW
, &file
)
642 pub fn lstat(p
: &Path
) -> io
::Result
<FileAttr
> {
643 let (dir
, file
) = open_parent(p
)?
;
644 metadata_at(&dir
, 0, &file
)
647 fn metadata_at(fd
: &WasiFd
, flags
: wasi
::Lookupflags
, path
: &Path
) -> io
::Result
<FileAttr
> {
648 let meta
= fd
.path_filestat_get(flags
, osstr2str(path
.as_ref())?
)?
;
649 Ok(FileAttr { meta }
)
652 pub fn canonicalize(_p
: &Path
) -> io
::Result
<PathBuf
> {
653 // This seems to not be in wasi's API yet, and we may need to end up
654 // emulating it ourselves. For now just return an error.
658 fn open_at(fd
: &WasiFd
, path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
661 osstr2str(path
.as_ref())?
,
664 opts
.rights_inheriting(),
670 /// Attempts to open a bare path `p`.
672 /// WASI has no fundamental capability to do this. All syscalls and operations
673 /// are relative to already-open file descriptors. The C library, however,
674 /// manages a map of pre-opened file descriptors to their path, and then the C
675 /// library provides an API to look at this. In other words, when you want to
676 /// open a path `p`, you have to find a previously opened file descriptor in a
677 /// global table and then see if `p` is relative to that file descriptor.
679 /// This function, if successful, will return two items:
681 /// * The first is a `ManuallyDrop<WasiFd>`. This represents a pre-opened file
682 /// descriptor which we don't have ownership of, but we can use. You shouldn't
683 /// actually drop the `fd`.
685 /// * The second is a path that should be a part of `p` and represents a
686 /// relative traversal from the file descriptor specified to the desired
689 /// If successful you can use the returned file descriptor to perform
690 /// file-descriptor-relative operations on the path returned as well. The
691 /// `rights` argument indicates what operations are desired on the returned file
692 /// descriptor, and if successful the returned file descriptor should have the
693 /// appropriate rights for performing `rights` actions.
695 /// Note that this can fail if `p` doesn't look like it can be opened relative
696 /// to any pre-opened file descriptor.
697 fn open_parent(p
: &Path
) -> io
::Result
<(ManuallyDrop
<WasiFd
>, PathBuf
)> {
698 run_path_with_cstr(p
, |p
| {
699 let mut buf
= Vec
::<u8>::with_capacity(512);
702 let mut relative_path
= buf
.as_ptr().cast();
703 let mut abs_prefix
= ptr
::null();
704 let fd
= __wasilibc_find_relpath(
711 if io
::Error
::last_os_error().raw_os_error() == Some(libc
::ENOMEM
) {
712 // Trigger the internal buffer resizing logic of `Vec` by requiring
713 // more space than the current capacity.
714 let cap
= buf
.capacity();
720 "failed to find a pre-opened file descriptor \
721 through which {:?} could be opened",
724 return Err(io
::Error
::new(io
::ErrorKind
::Uncategorized
, msg
));
726 let relative
= CStr
::from_ptr(relative_path
).to_bytes().to_vec();
729 ManuallyDrop
::new(WasiFd
::from_raw_fd(fd
as c_int
)),
730 PathBuf
::from(OsString
::from_vec(relative
)),
736 pub fn __wasilibc_find_relpath(
737 path
: *const libc
::c_char
,
738 abs_prefix
: *mut *const libc
::c_char
,
739 relative_path
: *mut *const libc
::c_char
,
740 relative_path_len
: libc
::size_t
,
746 pub fn osstr2str(f
: &OsStr
) -> io
::Result
<&str> {
748 .ok_or_else(|| io
::const_io_error
!(io
::ErrorKind
::Uncategorized
, "input must be utf-8"))
751 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
754 let mut reader
= File
::open(from
)?
;
755 let mut writer
= File
::create(to
)?
;
757 io
::copy(&mut reader
, &mut writer
)
760 pub fn remove_dir_all(path
: &Path
) -> io
::Result
<()> {
761 let (parent
, path
) = open_parent(path
)?
;
762 remove_dir_all_recursive(&parent
, &path
)
765 fn remove_dir_all_recursive(parent
: &WasiFd
, path
: &Path
) -> io
::Result
<()> {
766 // Open up a file descriptor for the directory itself. Note that we don't
767 // follow symlinks here and we specifically open directories.
769 // At the root invocation of this function this will correctly handle
770 // symlinks passed to the top-level `remove_dir_all`. At the recursive
771 // level this will double-check that after the `readdir` call deduced this
772 // was a directory it's still a directory by the time we open it up.
774 // If the opened file was actually a symlink then the symlink is deleted,
775 // not the directory recursively.
776 let mut opts
= OpenOptions
::new();
777 opts
.lookup_flags(0);
778 opts
.directory(true);
780 let fd
= open_at(parent
, path
, &opts
)?
;
781 if fd
.file_attr()?
.file_type().is_symlink() {
782 return parent
.unlink_file(osstr2str(path
.as_ref())?
);
785 // this "root" is only used by `DirEntry::path` which we don't use below so
786 // it's ok for this to be a bogus value
787 let dummy_root
= PathBuf
::new();
789 // Iterate over all the entries in this directory, and travel recursively if
791 for entry
in ReadDir
::new(fd
, dummy_root
) {
793 let path
= crate::str::from_utf8(&entry
.name
).map_err(|_
| {
794 io
::const_io_error
!(io
::ErrorKind
::Uncategorized
, "invalid utf-8 file name found")
797 if entry
.file_type()?
.is_dir() {
798 remove_dir_all_recursive(&entry
.inner
.dir
.fd
, path
.as_ref())?
;
800 entry
.inner
.dir
.fd
.unlink_file(path
)?
;
804 // Once all this directory's contents are deleted it should be safe to
805 // delete the directory tiself.
806 parent
.remove_directory(osstr2str(path
.as_ref())?
)