Revert libc time_t changes

This was based on a misunderstanding.

On musl, 64-bit time_t on 32-bit architectures was introduced in version 1.2.0,
by introducing new symbols. The old symbols still exist, to allow programs compiled against older versions
to keep running on 1.2.0+, preserving ABI-compatibility. (see musl commit 38143339646a4ccce8afe298c34467767c899f51)

Programs compiled against 1.2.0+ will get the new symbols, and will therefore think time_t is 64-bit.

Unfortunately, rust's libc crate uses its own definition of these types, and does not check for musl version.
Currently, it includes the pre-1.2.0 32-bit type.

That means:

- If you run on a 32-bit system like i686
- ... and compile against a C-library other than libc
- ... and pass it a time_t-containing struct like timespec or stat

... you need to arrange for that library to be built against musl <1.2.0.

Or, as https://github.com/ericonr/rust-time64 says:

> Therefore, for "old" 32-bit targets (riscv32 is supposed to default to time64),
> any Rust code that interacts with C code built on musl after 1.2.0,
> using types based on time_t (arguably, the main ones are struct timespec and struct stat) in their interface,
> will be completely miscompiled.

However, while fish runs on i686 and compiles against pcre2, we do not pass pcre2 a time_t.
Our only uses of time_t are confined to interactions with libc, in which case with musl we would simply use the legacy ABI.

I have compiled an i686 fish against musl to confirm and can find no issue.

This reverts commit 55196ee2a0.
This reverts commit 4992f88966.
This reverts commit 46c8ba2c9f.
This reverts commit 3a9b4149da.
This reverts commit 5f9e9cbe74.
This reverts commit 338579b78c.
This reverts commit d19e5508d7.
This reverts commit b64045dc18.

Closes #10634
This commit is contained in:
Fabian Boehm 2024-08-27 11:15:27 +02:00
parent 46c1f0e338
commit 7b7d16da48
13 changed files with 122 additions and 373 deletions

View file

@ -41,7 +41,6 @@ use fish::{
fprintf, function, future_feature_flags as features,
history::{self, start_private_mode},
io::IoChain,
libc::{getrusage64, timeval64},
nix::{getpid, isatty},
panic::panic_handler,
parse_constants::{ParseErrorList, ParseTreeFlags},
@ -56,12 +55,14 @@ use fish::{
},
reader::{reader_init, reader_read, term_copy_modes},
signal::{signal_clear_cancel, signal_unblock_all},
threads, topic_monitor,
threads::{self},
topic_monitor,
wchar::prelude::*,
wutil::waccess,
};
use std::ffi::{CString, OsStr, OsString};
use std::fs::File;
use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use std::rc::Rc;
@ -103,20 +104,23 @@ struct FishCmdOpts {
}
/// Return a timeval converted to milliseconds.
fn tv_to_msec(tv: &timeval64) -> i64 {
#[allow(clippy::unnecessary_cast)]
fn tv_to_msec(tv: &libc::timeval) -> i64 {
// milliseconds per second
let mut msec = tv.tv_sec * 1000;
let mut msec = tv.tv_sec as i64 * 1000;
// microseconds per millisecond
msec += tv.tv_usec / 1000;
msec += tv.tv_usec as i64 / 1000;
msec
}
fn print_rusage_self() {
let Some(rs) = getrusage64(libc::RUSAGE_SELF) else {
let mut rs = MaybeUninit::uninit();
if unsafe { libc::getrusage(libc::RUSAGE_SELF, rs.as_mut_ptr()) } != 0 {
let s = CString::new("getrusage").unwrap();
unsafe { libc::perror(s.as_ptr()) }
return;
};
}
let rs: libc::rusage = unsafe { rs.assume_init() };
let rss_kb = if cfg!(target_os = "macos") {
// mac use bytes.
rs.ru_maxrss / 1024

View file

@ -799,14 +799,12 @@ impl EnvUniversal {
// unlikely to affect users.
#[cfg(any(target_os = "linux", target_os = "android"))]
{
use crate::libc::{clock_gettime64, futimens64, timespec64};
#[allow(clippy::useless_conversion)]
let t0 = timespec64 {
tv_sec: 0,
tv_nsec: libc::UTIME_OMIT.try_into().unwrap(), // don't change ctime
};
if let Some(t1) = clock_gettime64(libc::CLOCK_REALTIME) {
futimens64(private_fd.as_raw_fd(), t0, t1);
let mut times: [libc::timespec; 2] = unsafe { std::mem::zeroed() };
times[0].tv_nsec = libc::UTIME_OMIT; // don't change ctime
if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut times[1]) } != 0 {
unsafe {
libc::futimens(private_fd.as_raw_fd(), &times[0]);
}
}
}

View file

@ -11,7 +11,6 @@ use crate::common::exit_without_destructors;
use crate::fd_readable_set::FdReadableSet;
use crate::fds::AutoCloseFd;
use crate::flog::FLOG;
use crate::libc::{select64, timeval64};
use crate::threads::assert_is_background_thread;
use crate::wutil::perror;
use errno::errno;
@ -147,24 +146,26 @@ impl FdEventSignaller {
/// but guarantees that the next call to wait() will not block.
/// Return true if readable, false if not readable, or not interrupted by a signal.
pub fn poll(&self, wait: bool /* = false */) -> bool {
let mut timeout = timeval64 {
let mut timeout = libc::timeval {
tv_sec: 0,
tv_usec: 0,
};
let mut fds: libc::fd_set = unsafe { std::mem::zeroed() };
unsafe { libc::FD_ZERO(&mut fds) };
unsafe { libc::FD_SET(self.read_fd(), &mut fds) };
let res = select64(
self.read_fd() + 1,
&mut fds,
std::ptr::null_mut(),
std::ptr::null_mut(),
if wait {
std::ptr::null_mut()
} else {
&mut timeout
},
);
let res = unsafe {
libc::select(
self.read_fd() + 1,
&mut fds,
std::ptr::null_mut(),
std::ptr::null_mut(),
if wait {
std::ptr::null_mut()
} else {
&mut timeout
},
)
};
res > 0
}

View file

@ -68,21 +68,24 @@ impl FdReadableSet {
/// destructively modifies the set. Returns the result of `select()` or `poll()`.
pub fn check_readable(&mut self, timeout_usec: u64) -> c_int {
let null = std::ptr::null_mut();
use crate::libc::{select64, timeval64};
if timeout_usec == Self::kNoTimeout {
return select64(
self.nfds_,
&mut self.fdset_,
null,
null,
std::ptr::null_mut(),
);
unsafe {
return libc::select(
self.nfds_,
&mut self.fdset_,
null,
null,
std::ptr::null_mut(),
);
}
} else {
let mut tvs = timeval64 {
tv_sec: (timeout_usec / kUsecPerSec).try_into().unwrap(),
tv_usec: (timeout_usec % kUsecPerSec).try_into().unwrap(),
let mut tvs = libc::timeval {
tv_sec: (timeout_usec / kUsecPerSec) as libc::time_t,
tv_usec: (timeout_usec % kUsecPerSec) as libc::suseconds_t,
};
return select64(self.nfds_, &mut self.fdset_, null, null, &mut tvs);
unsafe {
return libc::select(self.nfds_, &mut self.fdset_, null, null, &mut tvs);
}
}
}

View file

@ -16,7 +16,7 @@
//! 5. The chaos_mode boolean can be set to true to do things like lower buffer sizes which can
//! trigger race conditions. This is useful for testing.
use crate::{common::cstr2wcstring, env::EnvVar, libc::localtime64_r, wcstringutil::trim};
use crate::{common::cstr2wcstring, env::EnvVar, wcstringutil::trim};
use std::{
borrow::Cow,
collections::{BTreeMap, HashMap, HashSet, VecDeque},
@ -1409,8 +1409,10 @@ fn format_history_record(
) -> WString {
let mut result = WString::new();
let seconds = time_to_seconds(item.timestamp());
let seconds = seconds as libc::time_t;
let mut timestamp: libc::tm = unsafe { std::mem::zeroed() };
if let Some(show_time_format) = show_time_format.and_then(|s| CString::new(s).ok()) {
if let Some(timestamp) = localtime64_r(seconds) {
if !unsafe { libc::localtime_r(&seconds, &mut timestamp).is_null() } {
const max_tstamp_length: usize = 100;
let mut timestamp_str = [0_u8; max_tstamp_length];
// The libc crate fails to declare strftime on BSD.

View file

@ -11,7 +11,6 @@ use crate::key::{
self, alt, canonicalize_control_char, canonicalize_keyed_control_char, function_key, shift,
Key, Modifiers,
};
use crate::libc::{pselect64, timespec64};
use crate::reader::{reader_current_data, reader_test_and_clear_interrupted};
use crate::threads::{iothread_port, MainThread};
use crate::universal_notifier::default_notifier;
@ -1094,7 +1093,7 @@ pub trait InputEventQueuer {
const NSEC_PER_MSEC: u64 = 1000 * 1000;
const NSEC_PER_SEC: u64 = NSEC_PER_MSEC * 1000;
let wait_nsec: u64 = (wait_time_ms as u64) * NSEC_PER_MSEC;
let timeout = timespec64 {
let timeout = libc::timespec {
tv_sec: (wait_nsec / NSEC_PER_SEC).try_into().unwrap(),
tv_nsec: (wait_nsec % NSEC_PER_SEC).try_into().unwrap(),
};
@ -1106,14 +1105,16 @@ pub trait InputEventQueuer {
libc::FD_ZERO(&mut fdset);
libc::FD_SET(in_fd, &mut fdset);
};
let res = pselect64(
in_fd + 1,
&mut fdset,
ptr::null_mut(),
ptr::null_mut(),
&timeout,
&sigs,
);
let res = unsafe {
libc::pselect(
in_fd + 1,
&mut fdset,
ptr::null_mut(),
ptr::null_mut(),
&timeout,
&sigs,
)
};
// Prevent signal starvation on WSL causing the `torn_escapes.py` test to fail
if is_windows_subsystem_for_linux(WSL::V1) {

View file

@ -1,6 +1,3 @@
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <locale.h>
#include <paths.h>
#include <stdbool.h>
@ -8,20 +5,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#define UNUSED(x) (void)(x)
#define assert_or_die(x) \
do { \
assert((x)); \
if (!(x)) abort(); \
} while (false)
size_t C_MB_CUR_MAX() { return MB_CUR_MAX; }
uint64_t C_ST_LOCAL() {
@ -193,114 +180,3 @@ int C_RLIMIT_NTHR() {
return -1;
#endif
}
bool C_readdir64(DIR* dirp, const char** d_name, size_t* d_name_len, uint64_t* d_ino,
unsigned char* d_type) {
struct dirent* dent = readdir(dirp);
if (!dent) {
return false;
}
*d_name = dent->d_name;
*d_name_len = sizeof dent->d_name / sizeof **d_name;
#if defined(__BSD__)
*d_ino = dent->d_fileno;
#else
*d_ino = dent->d_ino;
#endif
*d_type = dent->d_type;
return true;
}
bool C_fstatat64(int dirfd, const char* file, int flag, uint64_t* st_dev, uint64_t* st_ino,
mode_t* st_mode) {
struct stat buf;
if (fstatat(dirfd, file, &buf, flag) == -1) {
return false;
}
*st_dev = buf.st_dev;
*st_ino = buf.st_ino;
*st_mode = buf.st_mode;
return true;
}
bool C_localtime64_r(int64_t timep, struct tm* result) {
time_t timep_ = timep;
return localtime_r(&timep_, result);
}
struct timeval64 {
int64_t tv_sec;
int64_t tv_usec;
};
int C_select64(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds,
struct timeval64* timeout64) {
struct timeval timeout;
if (timeout64) {
timeout.tv_sec = timeout64->tv_sec;
timeout.tv_usec = timeout64->tv_usec;
assert_or_die(timeout.tv_sec == timeout64->tv_sec);
assert_or_die(timeout.tv_usec == timeout64->tv_usec);
}
int result = select(nfds, readfds, writefds, errorfds, timeout64 ? &timeout : NULL);
if (timeout64) {
timeout64->tv_sec = timeout.tv_sec;
timeout64->tv_usec = timeout.tv_usec;
}
return result;
}
struct timespec64 {
int64_t tv_sec;
int64_t tv_nsec;
};
int C_pselect64(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds,
struct timespec64* timeout64, const sigset_t* sigmask) {
struct timespec timeout;
if (timeout64) {
timeout.tv_sec = timeout64->tv_sec;
timeout.tv_nsec = timeout64->tv_nsec;
assert_or_die(timeout.tv_sec == timeout64->tv_sec);
assert_or_die(timeout.tv_nsec == timeout64->tv_nsec);
}
return pselect(nfds, readfds, writefds, errorfds, timeout64 ? &timeout : NULL, sigmask);
}
struct rusage64 {
struct timeval64 ru_utime;
struct timeval64 ru_stime;
int64_t ru_maxrss;
int64_t ru_nsignals;
};
int C_getrusage64(int resource, struct rusage64* usage) {
struct rusage tmp;
int result = getrusage(resource, &tmp);
usage->ru_utime.tv_sec = tmp.ru_utime.tv_sec;
usage->ru_utime.tv_usec = tmp.ru_utime.tv_usec;
usage->ru_stime.tv_sec = tmp.ru_stime.tv_sec;
usage->ru_stime.tv_usec = tmp.ru_stime.tv_usec;
usage->ru_maxrss = tmp.ru_maxrss;
usage->ru_nsignals = tmp.ru_nsignals;
return result;
}
bool C_clock_gettime64(clockid_t clock_id, struct timespec64* tp) {
struct timespec tp_;
if (clock_gettime(clock_id, &tp_) == -1) {
return false;
}
tp->tv_sec = tp_.tv_sec;
tp->tv_nsec = tp_.tv_nsec;
return true;
}
bool C_futimens64(int fd, struct timespec64 times0, struct timespec64 times1) {
struct timespec times[2];
times[0].tv_sec = times0.tv_sec;
times[0].tv_nsec = times0.tv_nsec;
times[1].tv_sec = times1.tv_sec;
times[1].tv_nsec = times1.tv_nsec;
return futimens(fd, &times[0]) == 0;
}

View file

@ -1,6 +1,6 @@
use std::{ffi::CStr, sync::atomic::AtomicPtr};
use std::sync::atomic::AtomicPtr;
use libc::{c_char, c_int, fd_set};
use libc::{c_char, c_int};
use once_cell::sync::Lazy;
pub static _PATH_BSHELL: AtomicPtr<c_char> = AtomicPtr::new(std::ptr::null_mut());
@ -59,165 +59,3 @@ CVAR!(C_RLIMIT_RTTIME, RLIMIT_RTTIME, i32);
CVAR!(C_RLIMIT_KQUEUES, RLIMIT_KQUEUES, i32);
CVAR!(C_RLIMIT_NPTS, RLIMIT_NPTS, i32);
CVAR!(C_RLIMIT_NTHR, RLIMIT_NTHR, i32);
pub(crate) fn readdir64(dirp: *mut libc::DIR) -> Option<(*const c_char, usize, u64, u8)> {
let mut d_name = unsafe { std::mem::zeroed() };
let mut d_name_len = unsafe { std::mem::zeroed() };
let mut d_ino = unsafe { std::mem::zeroed() };
let mut d_type = unsafe { std::mem::zeroed() };
if !unsafe { C_readdir64(dirp, &mut d_name, &mut d_name_len, &mut d_ino, &mut d_type) } {
return None;
}
Some((d_name, d_name_len, d_ino, d_type))
}
extern "C" {
fn C_readdir64(
dirp: *mut libc::DIR,
d_name: *mut *const c_char,
d_name_len: *mut usize,
d_ino: *mut u64,
d_type: *mut u8,
) -> bool;
}
pub(crate) fn fstatat64(
dirfd: c_int,
file: &CStr,
flag: c_int,
) -> Option<(u64, u64, libc::mode_t)> {
let mut st_dev = unsafe { std::mem::zeroed() };
let mut st_ino = unsafe { std::mem::zeroed() };
let mut st_mode = unsafe { std::mem::zeroed() };
if !unsafe {
C_fstatat64(
dirfd,
file.as_ptr(),
flag,
&mut st_dev,
&mut st_ino,
&mut st_mode,
)
} {
return None;
}
Some((st_dev, st_ino, st_mode))
}
extern "C" {
fn C_fstatat64(
dirfd: c_int,
file: *const c_char,
flag: c_int,
st_dev: *mut u64,
st_ino: *mut u64,
st_mode: *mut libc::mode_t,
) -> bool;
}
pub(crate) fn localtime64_r(timep: i64) -> Option<libc::tm> {
let mut timestamp = unsafe { std::mem::zeroed() };
if !unsafe { C_localtime64_r(timep, &mut timestamp) } {
return None;
}
Some(timestamp)
}
extern "C" {
fn C_localtime64_r(timep: i64, result: *mut libc::tm) -> bool;
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct timeval64 {
pub tv_sec: i64,
pub tv_usec: i64,
}
pub(crate) fn select64(
nfds: c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *mut timeval64,
) -> c_int {
unsafe { C_select64(nfds, readfds, writefds, errorfds, timeout) }
}
extern "C" {
fn C_select64(
nfds: c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *mut timeval64,
) -> c_int;
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct timespec64 {
pub tv_sec: i64,
pub tv_nsec: i64,
}
pub(crate) fn pselect64(
nfds: c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *const timespec64,
sigmask: *const libc::sigset_t,
) -> c_int {
unsafe { C_pselect64(nfds, readfds, writefds, errorfds, timeout, sigmask) }
}
extern "C" {
fn C_pselect64(
nfds: c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *const timespec64,
sigmask: *const libc::sigset_t,
) -> c_int;
}
#[repr(C)]
pub struct rusage64 {
pub ru_utime: timeval64,
pub ru_stime: timeval64,
pub ru_maxrss: u64,
pub ru_nsignals: u64,
}
pub fn getrusage64(resource: c_int) -> Option<rusage64> {
let mut rusage = std::mem::MaybeUninit::uninit();
let result = unsafe { C_getrusage64(resource, rusage.as_mut_ptr()) };
// getrusage(2) says the syscall can only fail if the dest address is invalid (EFAULT) or if the
// requested resource type is invalid. Since we're in control of both, we can assume it won't
// fail. In case it does anyway (e.g. OS where the syscall isn't implemented), we can just
// return an empty value.
match result {
0 => unsafe { Some(rusage.assume_init()) },
_ => None,
}
}
extern "C" {
fn C_getrusage64(resource: c_int, usage: *mut rusage64) -> c_int;
}
#[allow(unused)]
pub(crate) fn clock_gettime64(clk_id: libc::clockid_t) -> Option<timespec64> {
let mut tp = unsafe { std::mem::zeroed() };
if !unsafe { C_clock_gettime64(clk_id, &mut tp) } {
return None;
}
Some(tp)
}
extern "C" {
fn C_clock_gettime64(clk_id: libc::clockid_t, tp: *mut timespec64) -> bool;
}
#[allow(unused)]
pub(crate) fn futimens64(fd: c_int, time0: timespec64, time1: timespec64) -> bool {
unsafe { C_futimens64(fd, time0, time1) }
}
extern "C" {
fn C_futimens64(fd: c_int, time0: timespec64, time1: timespec64) -> bool;
}

View file

@ -2,10 +2,9 @@
use std::time::Duration;
use crate::libc::timeval64;
pub(crate) const fn timeval_to_duration(val: &timeval64) -> Duration {
let micros = val.tv_sec * (1E6 as i64) + val.tv_usec;
#[allow(clippy::unnecessary_cast)]
pub const fn timeval_to_duration(val: &libc::timeval) -> Duration {
let micros = val.tv_sec as i64 * (1E6 as i64) + val.tv_usec as i64;
Duration::from_micros(micros as u64)
}
@ -14,7 +13,7 @@ pub trait TimevalExt {
fn as_duration(&self) -> Duration;
}
impl TimevalExt for timeval64 {
impl TimevalExt for libc::timeval {
fn as_micros(&self) -> i64 {
timeval_to_duration(self).as_micros() as i64
}

View file

@ -1,5 +1,4 @@
use crate::env::{EnvMode, EnvStack, EnvVar, EnvVarFlags, Environment};
use crate::libc::localtime64_r;
use crate::tests::prelude::*;
use crate::wchar::prelude::*;
use crate::wutil::wgetcwd;
@ -64,8 +63,16 @@ fn return_timezone_hour(tstamp: SystemTime, timezone: &wstr) -> libc::c_int {
let _var = vars.get(L!("TZ"));
let tstamp = tstamp.duration_since(UNIX_EPOCH).unwrap().as_secs();
localtime64_r(tstamp.try_into().unwrap()).unwrap().tm_hour
let tstamp: libc::time_t = tstamp
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
.try_into()
.unwrap();
let mut local_time: libc::tm = unsafe { std::mem::zeroed() };
unsafe { libc::localtime_r(&tstamp, &mut local_time) };
local_time.tm_hour
}
/// Verify that setting TZ calls tzset() in the current shell process.

View file

@ -17,8 +17,6 @@
use std::io::Write;
use std::time::{Duration, Instant};
use crate::libc::{getrusage64, rusage64};
enum Unit {
Minutes,
Seconds,
@ -28,8 +26,8 @@ enum Unit {
struct TimerSnapshot {
wall_time: Instant,
cpu_fish: rusage64,
cpu_children: rusage64,
cpu_fish: libc::rusage,
cpu_children: libc::rusage,
}
/// Create a `TimerSnapshot` and return a `PrintElapsedOnDrop` object that will print upon
@ -47,12 +45,24 @@ enum RUsage {
RChildren,
}
fn getrusage(resource: RUsage) -> rusage64 {
getrusage64(match resource {
RUsage::RSelf => libc::RUSAGE_SELF,
RUsage::RChildren => libc::RUSAGE_CHILDREN,
})
.unwrap_or(unsafe { std::mem::zeroed() })
/// A safe wrapper around `libc::getrusage()`
fn getrusage(resource: RUsage) -> libc::rusage {
let mut rusage = std::mem::MaybeUninit::uninit();
let result = unsafe {
match resource {
RUsage::RSelf => libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr()),
RUsage::RChildren => libc::getrusage(libc::RUSAGE_CHILDREN, rusage.as_mut_ptr()),
}
};
// getrusage(2) says the syscall can only fail if the dest address is invalid (EFAULT) or if the
// requested resource type is invalid. Since we're in control of both, we can assume it won't
// fail. In case it does anyway (e.g. OS where the syscall isn't implemented), we can just
// return an empty value.
match result {
0 => unsafe { rusage.assume_init() },
_ => unsafe { std::mem::zeroed() },
}
}
impl TimerSnapshot {

View file

@ -901,7 +901,7 @@ mod expander {
// Ensure we don't fall into a symlink loop.
// Ideally we would compare both devices and inodes, but devices require a stat call, so we
// use inodes exclusively.
let mut visited_inodes: HashSet<u64> = HashSet::new();
let mut visited_inodes: HashSet<libc::ino_t> = HashSet::new();
loop {
let mut unique_entry = WString::new();

View file

@ -1,6 +1,5 @@
use super::wopendir;
use crate::common::{cstr2wcstring, wcs2zstring};
use crate::libc::{fstatat64, readdir64};
use crate::wchar::{wstr, WString};
use crate::wutil::DevInode;
use libc::{
@ -35,7 +34,7 @@ pub struct DirEntry {
pub name: WString,
/// inode of this entry.
pub inode: u64,
pub inode: libc::ino_t,
// Device, inode pair for this entry, or none if not yet computed.
dev_inode: Cell<Option<DevInode>>,
@ -104,13 +103,14 @@ impl DirEntry {
return;
}
let narrow = wcs2zstring(&self.name);
if let Some((st_dev, st_ino, st_mode)) = fstatat64(fd, &narrow, 0) {
let mut s: libc::stat = unsafe { std::mem::zeroed() };
if unsafe { libc::fstatat(fd, narrow.as_ptr(), &mut s, 0) } == 0 {
let dev_inode = DevInode {
device: st_dev,
inode: st_ino,
device: s.st_dev as u64,
inode: s.st_ino as u64,
};
self.dev_inode.set(Some(dev_inode));
self.typ.set(stat_mode_to_entry_type(st_mode));
self.typ.set(stat_mode_to_entry_type(s.st_mode));
} else {
match errno::errno().0 {
ELOOP => {
@ -252,7 +252,8 @@ impl DirIter {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<io::Result<&DirEntry>> {
errno::set_errno(errno::Errno(0));
let Some((d_name, d_name_len, d_ino, d_type)) = readdir64(self.dir.dir()) else {
let dent = unsafe { libc::readdir(self.dir.dir()).as_ref() };
let Some(dent) = dent else {
// readdir distinguishes between EOF and error via errno.
let err = errno::errno().0;
if err == 0 {
@ -262,10 +263,9 @@ impl DirIter {
}
};
let d_name = unsafe { slice::from_raw_parts(d_name, d_name_len) };
// dent.d_name is c_char; pretend it's u8.
assert!(std::mem::size_of::<libc::c_char>() == std::mem::size_of::<u8>());
let d_name_cchar = &d_name;
let d_name_cchar = &dent.d_name;
let d_name = unsafe {
slice::from_raw_parts(d_name_cchar.as_ptr() as *const u8, d_name_cchar.len())
};
@ -276,12 +276,22 @@ impl DirIter {
return self.next();
}
let nul_pos = d_name.iter().position(|b| *b == 0).unwrap();
let d_name = &d_name[..nul_pos + 1];
let nul_pos = dent.d_name.iter().position(|b| *b == 0).unwrap();
let d_name: Vec<u8> = dent.d_name[..nul_pos + 1]
.iter()
.map(|b| *b as u8)
.collect();
self.entry.reset();
self.entry.name = cstr2wcstring(d_name);
self.entry.inode = d_ino;
let typ = dirent_type_to_entry_type(d_type);
self.entry.name = cstr2wcstring(&d_name);
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
{
self.entry.inode = dent.d_fileno;
}
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
{
self.entry.inode = dent.d_ino;
}
let typ = dirent_type_to_entry_type(dent.d_type);
// Do not store symlinks as we will need to resolve them.
if typ != Some(DirEntryType::lnk) {
self.entry.typ.set(typ);