Skip to content

Commit e0ccae6

Browse files
committed
New: clone_dirfd() may fix #34, ADD: FdType machinery, libc_ok()
* With FdType/fd_type() one can determine the kind of an underlying file descriptor. Lite descriptors are implemented only in Linux (for now). When O_DIRECTORY is supported it uses fcntl() in favo over stat() * clone_dirfd() tries to do 'the right thing' for duplicating FD's * libc_ok() is a simple wraper for libc calls that return -1 on error, I will refactor the code in the next commits to make use of that more. Please test this! So far it works for me on Linux.
1 parent d567fb1 commit e0ccae6

File tree

1 file changed

+70
-4
lines changed

1 file changed

+70
-4
lines changed

src/dir.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,78 @@ impl Dir {
511511

512512
/// Creates a new independently owned handle to the underlying directory.
513513
pub fn try_clone(&self) -> io::Result<Self> {
514-
let fd = unsafe { libc::dup(self.0) };
515-
if fd == -1 {
516-
Err(io::Error::last_os_error())
514+
Ok(Dir(clone_dirfd(self.0)?))
515+
}
516+
517+
//TODO(cehteh): clone/conversion full<->lite
518+
}
519+
520+
const CURRENT_DIRECTORY: [libc::c_char; 2] = [b'.' as libc::c_char, 0];
521+
522+
fn clone_dirfd(fd: libc::c_int) -> io::Result<libc::c_int> {
523+
unsafe {
524+
match fd_type(fd)? {
525+
FdType::NormalDir => libc_ok(libc::openat(
526+
fd,
527+
&CURRENT_DIRECTORY as *const libc::c_char,
528+
BASE_OPEN_FLAGS,
529+
)),
530+
#[cfg(target_os = "linux")]
531+
FdType::LiteDir => libc_ok(libc::dup(fd)),
532+
_ => Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
533+
}
534+
}
535+
}
536+
537+
enum FdType {
538+
NormalDir,
539+
LiteDir,
540+
Other,
541+
}
542+
543+
// OSes with O_DIRECTORY can use fcntl()
544+
// Linux hash O_PATH
545+
#[cfg(target_os = "linux")]
546+
fn fd_type(fd: libc::c_int) -> io::Result<FdType> {
547+
let flags = unsafe { libc_ok(libc::fcntl(fd, libc::F_GETFL))? };
548+
if flags & libc::O_DIRECTORY != 0 {
549+
if flags & libc::O_PATH != 0 {
550+
Ok(FdType::LiteDir)
517551
} else {
518-
unsafe { Self::from_raw_fd_checked(fd) }
552+
Ok(FdType::NormalDir)
519553
}
554+
} else {
555+
Ok(FdType::Other)
556+
}
557+
}
558+
559+
#[cfg(target_os = "freebsd")]
560+
fn fd_type(fd: libc::c_int) -> io::Result<FdType> {
561+
let flags = unsafe { libc_ok(libc::fcntl(fd, libc::F_GETFL))? };
562+
if flags & libc::O_DIRECTORY != 0 {
563+
Ok(FdType::NormalDir)
564+
} else {
565+
Ok(FdType::Other)
566+
}
567+
}
568+
569+
// OSes without O_DIRECTORY use stat()
570+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
571+
fn fd_type(fd: libc::c_int) -> io::Result<DirType> {
572+
let mut stat = mem::zeroed(); // TODO(cehteh): uninit
573+
unsafe { libc_ok(libc::fstat(fd, &mut stat))? };
574+
match stat.st_mode & libc::S_IFMT {
575+
libc::S_IFDIR => Ok(FdType::NormalDir),
576+
_ => Ok(FdType::Other),
577+
}
578+
}
579+
580+
#[inline]
581+
fn libc_ok(ret: libc::c_int) -> io::Result<libc::c_int> {
582+
if ret != -1 {
583+
Ok(ret)
584+
} else {
585+
Err(io::Error::last_os_error())
520586
}
521587
}
522588

0 commit comments

Comments
 (0)