2025-04-14 18:13:26 -07:00
|
|
|
use crate::cxx_extern::readlinkat;
|
2025-03-07 02:36:08 -08:00
|
|
|
use crate::{
|
2025-09-08 11:24:33 -07:00
|
|
|
FsPathBuilder, LibcReturn, LoggedResult, OsError, OsResult, Utf8CStr, Utf8CStrBuf, cstr, errno,
|
|
|
|
|
fd_path, fd_set_attr,
|
2025-03-07 02:36:08 -08:00
|
|
|
};
|
2025-09-08 23:59:29 -07:00
|
|
|
use libc::{dirent, mode_t};
|
|
|
|
|
use nix::{errno::Errno, fcntl::AtFlags, fcntl::OFlag, sys::stat::Mode, unistd::UnlinkatFlags};
|
2025-03-07 02:36:08 -08:00
|
|
|
use std::fs::File;
|
2025-09-08 23:59:29 -07:00
|
|
|
use std::ops::Deref;
|
|
|
|
|
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd};
|
2025-04-14 18:13:26 -07:00
|
|
|
use std::ptr::NonNull;
|
2025-09-08 23:59:29 -07:00
|
|
|
use std::slice;
|
2025-03-07 02:36:08 -08:00
|
|
|
|
|
|
|
|
pub struct DirEntry<'a> {
|
2025-09-08 23:59:29 -07:00
|
|
|
dir: &'a Directory,
|
2025-04-14 18:13:26 -07:00
|
|
|
entry: NonNull<dirent>,
|
2025-03-07 02:36:08 -08:00
|
|
|
d_name_len: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DirEntry<'_> {
|
2025-04-14 18:13:26 -07:00
|
|
|
pub fn as_ptr(&self) -> *mut dirent {
|
|
|
|
|
self.entry.as_ptr()
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-15 00:19:28 -07:00
|
|
|
pub fn name(&self) -> &Utf8CStr {
|
2025-09-08 23:59:29 -07:00
|
|
|
// SAFETY: Utf8CStr is already validated in Directory::read
|
2025-03-07 02:36:08 -08:00
|
|
|
unsafe {
|
2025-04-15 00:19:28 -07:00
|
|
|
Utf8CStr::from_bytes_unchecked(slice::from_raw_parts(
|
2025-03-07 02:36:08 -08:00
|
|
|
self.d_name.as_ptr().cast(),
|
|
|
|
|
self.d_name_len,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
2025-04-14 18:00:19 -07:00
|
|
|
self.dir.path_at(self.name(), buf)
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_dir(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_DIR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_file(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_REG
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_symlink(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_LNK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_block_device(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_BLK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_char_device(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_CHR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_fifo(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_FIFO
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_socket(&self) -> bool {
|
|
|
|
|
self.d_type == libc::DT_SOCK
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:15:59 -07:00
|
|
|
pub fn unlink(&self) -> OsResult<'_, ()> {
|
2025-09-08 23:59:29 -07:00
|
|
|
let flag = if self.is_dir() {
|
|
|
|
|
UnlinkatFlags::RemoveDir
|
|
|
|
|
} else {
|
|
|
|
|
UnlinkatFlags::NoRemoveDir
|
|
|
|
|
};
|
2025-04-24 15:48:11 -07:00
|
|
|
self.dir.unlink_at(self.name(), flag)
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:15:59 -07:00
|
|
|
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
self.dir.read_link_at(self.name(), buf)
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:15:59 -07:00
|
|
|
pub fn open_as_dir(&self) -> OsResult<'_, Directory> {
|
2025-03-07 02:36:08 -08:00
|
|
|
if !self.is_dir() {
|
2025-09-08 10:55:57 -07:00
|
|
|
return Err(OsError::new(
|
2025-09-08 23:59:29 -07:00
|
|
|
Errno::ENOTDIR,
|
2025-04-14 18:05:14 -07:00
|
|
|
"fdopendir",
|
2025-04-15 00:19:28 -07:00
|
|
|
Some(self.name()),
|
2025-04-14 18:05:14 -07:00
|
|
|
None,
|
|
|
|
|
));
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
2025-04-24 15:48:11 -07:00
|
|
|
self.dir.open_as_dir_at(self.name())
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-09-08 23:59:29 -07:00
|
|
|
pub fn open_as_file(&self, flags: OFlag) -> OsResult<'_, File> {
|
2025-03-07 02:36:08 -08:00
|
|
|
if self.is_dir() {
|
2025-09-08 10:55:57 -07:00
|
|
|
return Err(OsError::new(
|
2025-09-08 23:59:29 -07:00
|
|
|
Errno::EISDIR,
|
2025-04-14 18:05:14 -07:00
|
|
|
"open_as_file",
|
2025-04-15 00:19:28 -07:00
|
|
|
Some(self.name()),
|
2025-04-14 18:05:14 -07:00
|
|
|
None,
|
|
|
|
|
));
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
2025-04-24 15:48:11 -07:00
|
|
|
self.dir.open_as_file_at(self.name(), flags, 0)
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
2025-09-08 23:59:29 -07:00
|
|
|
|
|
|
|
|
pub fn rename_to<'a, 'entry: 'a>(
|
|
|
|
|
&'entry self,
|
|
|
|
|
new_dir: impl AsFd,
|
|
|
|
|
path: &'a Utf8CStr,
|
|
|
|
|
) -> OsResult<'a, ()> {
|
|
|
|
|
self.dir.rename_at(self.name(), new_dir, path)
|
|
|
|
|
}
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Deref for DirEntry<'_> {
|
|
|
|
|
type Target = dirent;
|
|
|
|
|
|
|
|
|
|
fn deref(&self) -> &dirent {
|
2025-04-14 18:13:26 -07:00
|
|
|
unsafe { self.entry.as_ref() }
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-14 18:13:26 -07:00
|
|
|
#[repr(transparent)]
|
2025-03-07 02:36:08 -08:00
|
|
|
pub struct Directory {
|
2025-04-14 18:13:26 -07:00
|
|
|
inner: NonNull<libc::DIR>,
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub enum WalkResult {
|
|
|
|
|
Continue,
|
|
|
|
|
Abort,
|
|
|
|
|
Skip,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Directory {
|
2025-09-08 23:59:29 -07:00
|
|
|
fn open_at<'a>(&self, name: &'a Utf8CStr, flags: OFlag, mode: mode_t) -> OsResult<'a, OwnedFd> {
|
|
|
|
|
nix::fcntl::openat(
|
|
|
|
|
self,
|
|
|
|
|
name,
|
|
|
|
|
flags | OFlag::O_CLOEXEC,
|
|
|
|
|
Mode::from_bits_truncate(mode),
|
|
|
|
|
)
|
|
|
|
|
.into_os_result("openat", Some(name), None)
|
2025-04-14 18:00:19 -07:00
|
|
|
}
|
|
|
|
|
|
2025-04-15 00:19:28 -07:00
|
|
|
fn path_at(&self, name: &Utf8CStr, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
self.resolve_path(buf)?;
|
2025-04-21 21:11:13 -07:00
|
|
|
buf.append_path(name);
|
2025-04-14 18:00:19 -07:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
// Low-level methods, we should track the caller when error occurs, so return OsResult.
|
2025-04-14 18:00:19 -07:00
|
|
|
impl Directory {
|
2025-08-28 16:15:59 -07:00
|
|
|
pub fn open(path: &Utf8CStr) -> OsResult<'_, Directory> {
|
2025-04-14 18:05:14 -07:00
|
|
|
let dirp = unsafe { libc::opendir(path.as_ptr()) };
|
2025-09-08 10:55:57 -07:00
|
|
|
let dirp = dirp.into_os_result("opendir", Some(path), None)?;
|
2025-04-14 13:31:52 -07:00
|
|
|
Ok(Directory { inner: dirp })
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:15:59 -07:00
|
|
|
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry<'_>>> {
|
2025-03-07 02:36:08 -08:00
|
|
|
*errno() = 0;
|
2025-04-14 18:13:26 -07:00
|
|
|
let e = unsafe { libc::readdir(self.inner.as_ptr()) };
|
2025-03-07 02:36:08 -08:00
|
|
|
if e.is_null() {
|
|
|
|
|
return if *errno() != 0 {
|
2025-04-14 18:05:14 -07:00
|
|
|
Err(OsError::last_os_error("readdir", None, None))
|
2025-03-07 02:36:08 -08:00
|
|
|
} else {
|
|
|
|
|
Ok(None)
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-04-15 00:19:28 -07:00
|
|
|
// Skip non UTF-8 entries, ".", and ".."
|
2025-03-07 02:36:08 -08:00
|
|
|
unsafe {
|
|
|
|
|
let entry = &*e;
|
2025-04-15 00:19:28 -07:00
|
|
|
|
|
|
|
|
let Ok(name) = Utf8CStr::from_ptr(entry.d_name.as_ptr()) else {
|
|
|
|
|
return self.read();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if name == "." || name == ".." {
|
2025-03-07 02:36:08 -08:00
|
|
|
self.read()
|
|
|
|
|
} else {
|
|
|
|
|
let e = DirEntry {
|
2025-09-08 23:59:29 -07:00
|
|
|
dir: self,
|
2025-04-14 18:13:26 -07:00
|
|
|
entry: NonNull::from(entry),
|
2025-04-15 00:19:28 -07:00
|
|
|
d_name_len: name.as_bytes_with_nul().len(),
|
2025-03-07 02:36:08 -08:00
|
|
|
};
|
|
|
|
|
Ok(Some(e))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn rewind(&mut self) {
|
2025-04-14 18:13:26 -07:00
|
|
|
unsafe { libc::rewinddir(self.inner.as_ptr()) };
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
pub fn open_as_dir_at<'a>(&self, name: &'a Utf8CStr) -> OsResult<'a, Directory> {
|
2025-09-08 23:59:29 -07:00
|
|
|
let fd = self.open_at(name, OFlag::O_RDONLY, 0)?;
|
2025-04-15 00:19:28 -07:00
|
|
|
Directory::try_from(fd).map_err(|e| e.set_args(Some(name), None))
|
2025-04-14 18:05:14 -07:00
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
pub fn open_as_file_at<'a>(
|
2025-04-15 00:19:28 -07:00
|
|
|
&self,
|
|
|
|
|
name: &'a Utf8CStr,
|
2025-09-08 23:59:29 -07:00
|
|
|
flags: OFlag,
|
|
|
|
|
mode: mode_t,
|
2025-04-15 00:19:28 -07:00
|
|
|
) -> OsResult<'a, File> {
|
2025-09-08 23:59:29 -07:00
|
|
|
let fd = self.open_at(name, flags, mode)?;
|
2025-04-14 18:05:14 -07:00
|
|
|
Ok(File::from(fd))
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
pub fn read_link_at<'a>(
|
2025-04-14 18:00:19 -07:00
|
|
|
&self,
|
2025-04-15 00:19:28 -07:00
|
|
|
name: &'a Utf8CStr,
|
2025-04-24 15:48:11 -07:00
|
|
|
buf: &mut dyn Utf8CStrBuf,
|
2025-04-14 18:00:19 -07:00
|
|
|
) -> OsResult<'a, ()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
buf.clear();
|
|
|
|
|
unsafe {
|
2025-09-09 22:19:05 -07:00
|
|
|
readlinkat(
|
2025-04-24 15:48:11 -07:00
|
|
|
self.as_raw_fd(),
|
|
|
|
|
name.as_ptr(),
|
|
|
|
|
buf.as_mut_ptr().cast(),
|
|
|
|
|
buf.capacity(),
|
|
|
|
|
)
|
2025-09-09 22:19:05 -07:00
|
|
|
.check_os_err("readlinkat", Some(name), None)?;
|
2025-04-24 15:48:11 -07:00
|
|
|
}
|
2025-09-09 22:19:05 -07:00
|
|
|
buf.rebuild().ok();
|
2025-04-24 15:48:11 -07:00
|
|
|
Ok(())
|
2025-04-14 18:00:19 -07:00
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
pub fn mkdir_at<'a>(&self, name: &'a Utf8CStr, mode: mode_t) -> OsResult<'a, ()> {
|
2025-09-08 23:59:29 -07:00
|
|
|
match nix::sys::stat::mkdirat(self, name, Mode::from_bits_truncate(mode)) {
|
|
|
|
|
Ok(_) | Err(Errno::EEXIST) => Ok(()),
|
|
|
|
|
Err(e) => Err(OsError::new(e, "mkdirat", Some(name), None)),
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
// ln -s target self/name
|
|
|
|
|
pub fn create_symlink_at<'a>(
|
|
|
|
|
&self,
|
|
|
|
|
name: &'a Utf8CStr,
|
|
|
|
|
target: &'a Utf8CStr,
|
|
|
|
|
) -> OsResult<'a, ()> {
|
2025-09-08 23:59:29 -07:00
|
|
|
nix::unistd::symlinkat(target, self, name).check_os_err(
|
|
|
|
|
"symlinkat",
|
|
|
|
|
Some(target),
|
|
|
|
|
Some(name),
|
|
|
|
|
)
|
2025-04-24 15:48:11 -07:00
|
|
|
}
|
|
|
|
|
|
2025-09-08 23:59:29 -07:00
|
|
|
pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: UnlinkatFlags) -> OsResult<'a, ()> {
|
|
|
|
|
nix::unistd::unlinkat(self, name, flag).check_os_err("unlinkat", Some(name), None)
|
2025-04-24 15:48:11 -07:00
|
|
|
}
|
|
|
|
|
|
2025-04-15 00:19:28 -07:00
|
|
|
pub fn contains_path(&self, path: &Utf8CStr) -> bool {
|
2025-03-07 02:36:08 -08:00
|
|
|
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
|
|
|
|
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
|
|
|
|
// Use fstatat to check the existence of a file instead.
|
2025-09-08 23:59:29 -07:00
|
|
|
nix::sys::stat::fstatat(self, path, AtFlags::AT_SYMLINK_NOFOLLOW).is_ok()
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:48:11 -07:00
|
|
|
pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
2025-03-07 02:36:08 -08:00
|
|
|
fd_path(self.as_raw_fd(), buf)
|
|
|
|
|
}
|
2025-09-08 23:59:29 -07:00
|
|
|
|
|
|
|
|
pub fn rename_at<'a>(
|
|
|
|
|
&self,
|
|
|
|
|
old: &'a Utf8CStr,
|
|
|
|
|
new_dir: impl AsFd,
|
|
|
|
|
new: &'a Utf8CStr,
|
|
|
|
|
) -> OsResult<'a, ()> {
|
|
|
|
|
nix::fcntl::renameat(self, old, new_dir, new).check_os_err("renameat", Some(old), Some(new))
|
|
|
|
|
}
|
2025-09-08 11:24:33 -07:00
|
|
|
}
|
2025-03-07 02:36:08 -08:00
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
// High-level helper methods, composed of multiple operations.
|
|
|
|
|
// We should treat these as application logic and log ASAP, so return LoggedResult.
|
|
|
|
|
impl Directory {
|
|
|
|
|
pub fn post_order_walk<F: FnMut(&DirEntry) -> LoggedResult<WalkResult>>(
|
2025-03-07 02:36:08 -08:00
|
|
|
&mut self,
|
|
|
|
|
mut f: F,
|
2025-09-08 11:24:33 -07:00
|
|
|
) -> LoggedResult<WalkResult> {
|
2025-03-07 02:36:08 -08:00
|
|
|
self.post_order_walk_impl(&mut f)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
pub fn pre_order_walk<F: FnMut(&DirEntry) -> LoggedResult<WalkResult>>(
|
2025-03-07 02:36:08 -08:00
|
|
|
&mut self,
|
|
|
|
|
mut f: F,
|
2025-09-08 11:24:33 -07:00
|
|
|
) -> LoggedResult<WalkResult> {
|
2025-03-07 02:36:08 -08:00
|
|
|
self.pre_order_walk_impl(&mut f)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
pub fn remove_all(mut self) -> LoggedResult<()> {
|
2025-03-07 02:36:08 -08:00
|
|
|
self.post_order_walk(|e| {
|
|
|
|
|
e.unlink()?;
|
|
|
|
|
Ok(WalkResult::Continue)
|
|
|
|
|
})?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
pub fn copy_into(&mut self, dir: &Directory) -> LoggedResult<()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
let mut buf = cstr::buf::default();
|
|
|
|
|
self.copy_into_impl(dir, &mut buf)
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
pub fn move_into(&mut self, dir: &Directory) -> LoggedResult<()> {
|
2025-03-07 02:36:08 -08:00
|
|
|
while let Some(ref e) = self.read()? {
|
|
|
|
|
if e.is_dir() && dir.contains_path(e.name()) {
|
|
|
|
|
// Destination folder exists, needs recursive move
|
|
|
|
|
let mut src = e.open_as_dir()?;
|
2025-04-24 15:48:11 -07:00
|
|
|
let dest = dir.open_as_dir_at(e.name())?;
|
2025-03-07 02:36:08 -08:00
|
|
|
src.move_into(&dest)?;
|
2025-04-14 18:05:14 -07:00
|
|
|
return Ok(e.unlink()?);
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
2025-09-08 23:59:29 -07:00
|
|
|
e.rename_to(dir, e.name())?;
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
pub fn link_into(&mut self, dir: &Directory) -> LoggedResult<()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
let mut buf = cstr::buf::default();
|
|
|
|
|
self.link_into_impl(dir, &mut buf)
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Directory {
|
2025-09-08 11:24:33 -07:00
|
|
|
fn post_order_walk_impl<F: FnMut(&DirEntry) -> LoggedResult<WalkResult>>(
|
2025-03-07 02:36:08 -08:00
|
|
|
&mut self,
|
|
|
|
|
f: &mut F,
|
2025-09-08 11:24:33 -07:00
|
|
|
) -> LoggedResult<WalkResult> {
|
2025-03-07 02:36:08 -08:00
|
|
|
use WalkResult::*;
|
|
|
|
|
loop {
|
|
|
|
|
match self.read()? {
|
|
|
|
|
None => return Ok(Continue),
|
|
|
|
|
Some(ref e) => {
|
|
|
|
|
if e.is_dir() {
|
|
|
|
|
let mut dir = e.open_as_dir()?;
|
|
|
|
|
if let Abort = dir.post_order_walk_impl(f)? {
|
|
|
|
|
return Ok(Abort);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
match f(e)? {
|
|
|
|
|
Abort => return Ok(Abort),
|
|
|
|
|
Skip => return Ok(Continue),
|
|
|
|
|
Continue => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 11:24:33 -07:00
|
|
|
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> LoggedResult<WalkResult>>(
|
2025-03-07 02:36:08 -08:00
|
|
|
&mut self,
|
|
|
|
|
f: &mut F,
|
2025-09-08 11:24:33 -07:00
|
|
|
) -> LoggedResult<WalkResult> {
|
2025-03-07 02:36:08 -08:00
|
|
|
use WalkResult::*;
|
|
|
|
|
loop {
|
|
|
|
|
match self.read()? {
|
|
|
|
|
None => return Ok(Continue),
|
|
|
|
|
Some(ref e) => match f(e)? {
|
|
|
|
|
Abort => return Ok(Abort),
|
|
|
|
|
Skip => continue,
|
|
|
|
|
Continue => {
|
|
|
|
|
if e.is_dir() {
|
|
|
|
|
let mut dir = e.open_as_dir()?;
|
|
|
|
|
if let Abort = dir.pre_order_walk_impl(f)? {
|
|
|
|
|
return Ok(Abort);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-24 15:48:11 -07:00
|
|
|
|
|
|
|
|
fn copy_into_impl(
|
|
|
|
|
&mut self,
|
|
|
|
|
dest_dir: &Directory,
|
|
|
|
|
buf: &mut dyn Utf8CStrBuf,
|
2025-09-08 11:24:33 -07:00
|
|
|
) -> LoggedResult<()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
while let Some(ref e) = self.read()? {
|
|
|
|
|
e.resolve_path(buf)?;
|
|
|
|
|
let attr = buf.get_attr()?;
|
|
|
|
|
if e.is_dir() {
|
|
|
|
|
dest_dir.mkdir_at(e.name(), 0o777)?;
|
|
|
|
|
let mut src = e.open_as_dir()?;
|
|
|
|
|
let dest = dest_dir.open_as_dir_at(e.name())?;
|
|
|
|
|
src.copy_into_impl(&dest, buf)?;
|
|
|
|
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
|
|
|
} else if e.is_file() {
|
2025-09-08 23:59:29 -07:00
|
|
|
let mut src = e.open_as_file(OFlag::O_RDONLY)?;
|
|
|
|
|
let mut dest = dest_dir.open_as_file_at(
|
|
|
|
|
e.name(),
|
|
|
|
|
OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC,
|
|
|
|
|
0o777,
|
|
|
|
|
)?;
|
2025-04-24 15:48:11 -07:00
|
|
|
std::io::copy(&mut src, &mut dest)?;
|
|
|
|
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
|
|
|
} else if e.is_symlink() {
|
|
|
|
|
e.read_link(buf)?;
|
|
|
|
|
dest_dir.create_symlink_at(e.name(), buf)?;
|
|
|
|
|
dest_dir.path_at(e.name(), buf)?;
|
|
|
|
|
buf.set_attr(&attr)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn link_into_impl(
|
|
|
|
|
&mut self,
|
|
|
|
|
dest_dir: &Directory,
|
|
|
|
|
buf: &mut dyn Utf8CStrBuf,
|
2025-09-08 11:24:33 -07:00
|
|
|
) -> LoggedResult<()> {
|
2025-04-24 15:48:11 -07:00
|
|
|
while let Some(ref e) = self.read()? {
|
|
|
|
|
if e.is_dir() {
|
|
|
|
|
dest_dir.mkdir_at(e.name(), 0o777)?;
|
|
|
|
|
e.resolve_path(buf)?;
|
|
|
|
|
let attr = buf.get_attr()?;
|
|
|
|
|
let mut src = e.open_as_dir()?;
|
|
|
|
|
let dest = dest_dir.open_as_dir_at(e.name())?;
|
|
|
|
|
src.link_into_impl(&dest, buf)?;
|
|
|
|
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
|
|
|
} else {
|
2025-09-08 23:59:29 -07:00
|
|
|
nix::unistd::linkat(e.dir, e.name(), dest_dir, e.name(), AtFlags::empty())
|
2025-04-24 15:48:11 -07:00
|
|
|
.check_os_err("linkat", Some(e.name()), None)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<OwnedFd> for Directory {
|
2025-04-14 18:05:14 -07:00
|
|
|
type Error = OsError<'static>;
|
2025-03-07 02:36:08 -08:00
|
|
|
|
2025-04-14 18:05:14 -07:00
|
|
|
fn try_from(fd: OwnedFd) -> OsResult<'static, Self> {
|
|
|
|
|
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) };
|
2025-09-08 10:55:57 -07:00
|
|
|
let dirp = dirp.into_os_result("fdopendir", None, None)?;
|
2025-04-14 13:31:52 -07:00
|
|
|
Ok(Directory { inner: dirp })
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AsRawFd for Directory {
|
|
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
2025-04-14 18:13:26 -07:00
|
|
|
unsafe { libc::dirfd(self.inner.as_ptr()) }
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AsFd for Directory {
|
2025-08-28 16:15:59 -07:00
|
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
2025-03-07 02:36:08 -08:00
|
|
|
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Directory {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2025-04-14 18:13:26 -07:00
|
|
|
libc::closedir(self.inner.as_ptr());
|
2025-03-07 02:36:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|