mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
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 commit55196ee2a0
. This reverts commit4992f88966
. This reverts commit46c8ba2c9f
. This reverts commit3a9b4149da
. This reverts commit5f9e9cbe74
. This reverts commit338579b78c
. This reverts commitd19e5508d7
. This reverts commitb64045dc18
. Closes #10634
This commit is contained in:
parent
46c1f0e338
commit
7b7d16da48
13 changed files with 122 additions and 373 deletions
|
@ -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
|
||||
|
|
|
@ -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(), ×[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
124
src/libc.c
124
src/libc.c
|
@ -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, ×[0]) == 0;
|
||||
}
|
||||
|
|
166
src/libc.rs
166
src/libc.rs
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
30
src/timer.rs
30
src/timer.rs
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue