Use uninit instead of zeroed

This commit is contained in:
王宇逸
2025-05-19 20:52:55 +08:00
parent 223b98f2ff
commit 7c2c7f5874
15 changed files with 107 additions and 74 deletions

View File

@@ -1563,9 +1563,10 @@ pub fn is_windows_subsystem_for_linux(v: WSL) -> bool {
}
let wsl = RESULT.get_or_init(|| {
let mut info: libc::utsname = unsafe { mem::zeroed() };
let mut info = mem::MaybeUninit::uninit();
let release: &[u8] = unsafe {
libc::uname(&mut info);
libc::uname(info.as_mut_ptr());
let info = info.assume_init();
std::mem::transmute(&info.release[..])
};
@@ -1635,7 +1636,7 @@ pub fn fish_reserved_codepoint(c: char) -> bool {
pub fn redirect_tty_output(in_signal_handler: bool) {
unsafe {
let mut t: libc::termios = mem::zeroed();
let mut t = mem::MaybeUninit::uninit();
let s = CStr::from_bytes_with_nul(b"/dev/null\0").unwrap();
let fd = libc::open(s.as_ptr(), O_WRONLY);
if in_signal_handler && fd == -1 {
@@ -1644,7 +1645,7 @@ pub fn redirect_tty_output(in_signal_handler: bool) {
assert!(fd != -1, "Could not open /dev/null!");
}
for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] {
if libc::tcgetattr(stdfd, &mut t) == -1 && errno::errno().0 == EIO {
if libc::tcgetattr(stdfd, t.as_mut_ptr()) == -1 && errno::errno().0 == EIO {
libc::dup2(fd, stdfd);
}
}

View File

@@ -59,6 +59,7 @@
use nix::sys::stat;
use std::ffi::CStr;
use std::io::{Read, Write};
use std::mem::MaybeUninit;
use std::num::NonZeroU32;
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
use std::slice;
@@ -481,8 +482,11 @@ fn internal_exec(vars: &EnvStack, j: &Job, block_io: IoChain) {
return;
}
let mut blocked_signals: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut blocked_signals) };
let mut blocked_signals = MaybeUninit::uninit();
let mut blocked_signals = unsafe {
libc::sigemptyset(blocked_signals.as_mut_ptr());
blocked_signals.assume_init()
};
let blocked_signals = if blocked_signals_for_job(j, &mut blocked_signals) {
Some(&blocked_signals)
} else {
@@ -685,8 +689,11 @@ fn fork_child_for_process(
};
// Decide if the job wants to set a custom sigmask.
let mut blocked_signals: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut blocked_signals) };
let mut blocked_signals = MaybeUninit::uninit();
let mut blocked_signals = unsafe {
libc::sigemptyset(blocked_signals.as_mut_ptr());
blocked_signals.assume_init()
};
let blocked_signals = if blocked_signals_for_job(job, &mut blocked_signals) {
Some(&blocked_signals)
} else {

View File

@@ -30,6 +30,7 @@
use crate::wildcard::{WildcardResult, ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE};
use crate::wutil::{normalize_path, wcstoi_partial, Options};
use bitflags::bitflags;
use std::mem::MaybeUninit;
bitflags! {
/// Set of flags controlling expansions.
@@ -1157,19 +1158,20 @@ fn expand_home_directory(input: &mut WString, vars: &dyn Environment) {
} else {
// Some other user's home directory.
let name_cstr = wcs2zstring(username);
let mut userinfo: libc::passwd = unsafe { std::mem::zeroed() };
let mut userinfo = MaybeUninit::uninit();
let mut result: *mut libc::passwd = std::ptr::null_mut();
let mut buf = [0 as libc::c_char; 8192];
let retval = unsafe {
libc::getpwnam_r(
name_cstr.as_ptr(),
&mut userinfo,
userinfo.as_mut_ptr(),
&mut buf[0],
std::mem::size_of_val(&buf),
&mut result,
)
};
if retval == 0 && !result.is_null() {
let userinfo = unsafe { userinfo.assume_init() };
home = Some(charptr2wcstring(userinfo.pw_dir));
}
}

View File

@@ -53,10 +53,12 @@ pub struct FdReadableSet {
impl FdReadableSet {
/// Construct an empty set.
pub fn new() -> FdReadableSet {
FdReadableSet {
fdset_: unsafe { std::mem::zeroed() },
nfds_: 0,
}
let mut fdset_ = std::mem::MaybeUninit::uninit();
let fdset_ = unsafe {
libc::FD_ZERO(fdset_.as_mut_ptr());
fdset_.assume_init()
};
FdReadableSet { fdset_, nfds_: 0 }
}
/// Reset back to an empty set.

View File

@@ -8,6 +8,7 @@
use errno::Errno;
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
use std::ffi::{CStr, CString};
use std::mem::MaybeUninit;
use std::sync::atomic::Ordering;
// The posix_spawn family of functions is unusual in that it returns errno codes directly in the return value, not via errno.
@@ -25,9 +26,9 @@ fn check_fail(res: i32) -> Result<(), Errno> {
impl Attr {
fn new() -> Result<Self, Errno> {
unsafe {
let mut attr: posix_spawnattr_t = std::mem::zeroed();
check_fail(libc::posix_spawnattr_init(&mut attr))?;
Ok(Self(attr))
let mut attr = MaybeUninit::uninit();
check_fail(libc::posix_spawnattr_init(attr.as_mut_ptr()))?;
Ok(Self(attr.assume_init()))
}
}
@@ -62,9 +63,9 @@ fn drop(&mut self) {
impl FileActions {
fn new() -> Result<Self, Errno> {
unsafe {
let mut actions: posix_spawn_file_actions_t = std::mem::zeroed();
check_fail(libc::posix_spawn_file_actions_init(&mut actions))?;
Ok(Self(actions))
let mut actions = MaybeUninit::uninit();
check_fail(libc::posix_spawn_file_actions_init(actions.as_mut_ptr()))?;
Ok(Self(actions.assume_init()))
}
}
@@ -129,8 +130,9 @@ pub fn new(j: &Job, dup2s: &Dup2List) -> Result<PosixSpawner, Errno> {
attr.set_sigdefault(&signals_to_default)?;
// Reset the sigmask.
let mut sigmask = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut sigmask) };
let mut sigmask = MaybeUninit::uninit();
unsafe { libc::sigemptyset(sigmask.as_mut_ptr()) };
let mut sigmask = unsafe { sigmask.assume_init() };
blocked_signals_for_job(j, &mut sigmask);
attr.set_sigmask(&sigmask)?;

View File

@@ -33,6 +33,7 @@
ffi::CString,
fs::File,
io::{BufRead, Read, Seek, SeekFrom, Write},
mem::MaybeUninit,
num::NonZeroUsize,
ops::ControlFlow,
os::{fd::AsRawFd, unix::fs::MetadataExt},
@@ -1443,9 +1444,9 @@ fn format_history_record(
// This warns for musl, but the warning is useless to us - there is nothing we can or should do.
#[allow(deprecated)]
let seconds = seconds as libc::time_t;
let mut timestamp: libc::tm = unsafe { std::mem::zeroed() };
let mut timestamp = MaybeUninit::uninit();
if let Some(show_time_format) = show_time_format.and_then(|s| CString::new(s).ok()) {
if !unsafe { libc::localtime_r(&seconds, &mut timestamp).is_null() } {
if !unsafe { libc::localtime_r(&seconds, timestamp.as_mut_ptr()).is_null() } {
const max_tstamp_length: usize = 100;
let mut timestamp_str = [0_u8; max_tstamp_length];
use libc::strftime;
@@ -1454,7 +1455,7 @@ fn format_history_record(
&mut timestamp_str[0] as *mut u8 as *mut libc::c_char,
max_tstamp_length,
show_time_format.as_ptr(),
&timestamp,
timestamp.as_ptr(),
)
} != 0
{

View File

@@ -29,6 +29,7 @@
use crate::wutil::fish_wcstol;
use std::cell::{RefCell, RefMut};
use std::collections::VecDeque;
use std::mem::MaybeUninit;
use std::os::fd::RawFd;
use std::os::unix::ffi::OsStrExt;
use std::ptr;
@@ -1463,8 +1464,8 @@ fn readch_timed(&mut self, wait_time_ms: usize) -> Option<CharEvent> {
// We are not prepared to handle a signal immediately; we only want to know if we get input on
// our fd before the timeout. Use pselect to block all signals; we will handle signals
// before the next call to readch().
let mut sigs: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe { libc::sigfillset(&mut sigs) };
let mut sigs = MaybeUninit::uninit();
unsafe { libc::sigfillset(sigs.as_mut_ptr()) };
// pselect expects timeouts in nanoseconds.
const NSEC_PER_MSEC: u64 = 1000 * 1000;
@@ -1476,27 +1477,27 @@ fn readch_timed(&mut self, wait_time_ms: usize) -> Option<CharEvent> {
};
// We have one fd of interest.
let mut fdset: libc::fd_set = unsafe { std::mem::zeroed() };
let mut fdset = MaybeUninit::uninit();
let in_fd = self.get_in_fd();
unsafe {
libc::FD_ZERO(&mut fdset);
libc::FD_SET(in_fd, &mut fdset);
libc::FD_ZERO(fdset.as_mut_ptr());
libc::FD_SET(in_fd, fdset.as_mut_ptr());
};
let res = unsafe {
libc::pselect(
in_fd + 1,
&mut fdset,
fdset.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
&timeout,
&sigs,
sigs.as_ptr(),
)
};
// Prevent signal starvation on WSL causing the `torn_escapes.py` test to fail
if is_windows_subsystem_for_linux(WSL::V1) {
// Merely querying the current thread's sigmask is sufficient to deliver a pending signal
let _ = unsafe { libc::pthread_sigmask(0, ptr::null(), &mut sigs) };
let _ = unsafe { libc::pthread_sigmask(0, ptr::null(), sigs.as_mut_ptr()) };
}
if res > 0 {
return Some(self.readch());

View File

@@ -15,6 +15,7 @@
use once_cell::sync::Lazy;
use std::ffi::OsStr;
use std::io::ErrorKind;
use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
/// Returns the user configuration directory for fish. If the directory or one of its parents
@@ -670,10 +671,11 @@ fn path_remoteness(path: &wstr) -> DirRemoteness {
let narrow = wcs2zstring(path);
#[cfg(target_os = "linux")]
{
let mut buf: libc::statfs = unsafe { std::mem::zeroed() };
if unsafe { libc::statfs(narrow.as_ptr(), &mut buf) } < 0 {
let mut buf = MaybeUninit::uninit();
if unsafe { libc::statfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
return DirRemoteness::unknown;
}
let buf = unsafe { buf.assume_init() };
// Linux has constants for these like NFS_SUPER_MAGIC, SMB_SUPER_MAGIC, CIFS_MAGIC_NUMBER but
// these are in varying headers. Simply hard code them.
// Note that we treat FUSE filesystems as remote, which means we lock less on such filesystems.
@@ -707,10 +709,11 @@ fn path_remoteness(path: &wstr) -> DirRemoteness {
// In practice the only system to define it is NetBSD.
let local_flag = ST_LOCAL() | MNT_LOCAL();
if local_flag != 0 {
let mut buf: libc::statvfs = unsafe { std::mem::zeroed() };
if unsafe { libc::statvfs(narrow.as_ptr(), &mut buf) } < 0 {
let mut buf = MaybeUninit::uninit();
if unsafe { libc::statvfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
return DirRemoteness::unknown;
}
let buf = unsafe { buf.assume_init() };
// statfs::f_flag is hard-coded as 64-bits on 32/64-bit FreeBSD but it's a (4-byte)
// long on 32-bit NetBSD.. and always 4-bytes on macOS (even on 64-bit builds).
#[allow(clippy::useless_conversion)]

View File

@@ -36,6 +36,7 @@
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::fs;
use std::io::{Read, Write};
use std::mem::MaybeUninit;
use std::num::NonZeroU32;
use std::os::fd::RawFd;
use std::rc::Rc;
@@ -343,9 +344,9 @@ pub fn reclaim(&mut self) {
/// Save the current tty modes into the owning job group, if we are transferred.
pub fn save_tty_modes(&mut self) {
if let Some(ref mut owner) = self.owner {
let mut tmodes: libc::termios = unsafe { std::mem::zeroed() };
if unsafe { libc::tcgetattr(STDIN_FILENO, &mut tmodes) } == 0 {
owner.tmodes.replace(Some(tmodes));
let mut tmodes = MaybeUninit::uninit();
if unsafe { libc::tcgetattr(STDIN_FILENO, tmodes.as_mut_ptr()) } == 0 {
owner.tmodes.replace(Some(unsafe { tmodes.assume_init() }));
} else if errno::errno().0 != ENOTTY {
perror("tcgetattr");
}

View File

@@ -29,6 +29,7 @@
use std::cell::UnsafeCell;
use std::cmp;
use std::io::BufReader;
use std::mem::MaybeUninit;
use std::num::NonZeroUsize;
use std::ops::ControlFlow;
use std::ops::Range;
@@ -656,10 +657,11 @@ pub fn reader_read(parser: &Parser, fd: RawFd, io: &IoChain) -> Result<(), Error
// See also, commit 396bf12. Without the need for this workaround we would just write:
// int inter = ((fd == STDIN_FILENO) && isatty(STDIN_FILENO));
if fd == STDIN_FILENO {
let mut t: libc::termios = unsafe { std::mem::zeroed() };
let mut t = MaybeUninit::uninit();
if isatty(STDIN_FILENO) {
interactive = true;
} else if unsafe { libc::tcgetattr(STDIN_FILENO, &mut t) } == -1 && errno().0 == EIO {
} else if unsafe { libc::tcgetattr(STDIN_FILENO, t.as_mut_ptr()) } == -1 && errno().0 == EIO
{
redirect_tty_output(false);
interactive = true;
}
@@ -2180,8 +2182,10 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
unsafe { libc::tcsetpgrp(self.conf.inputfd, libc::getpgrp()) };
// Get the current terminal modes. These will be restored when the function returns.
let mut old_modes: libc::termios = unsafe { std::mem::zeroed() };
if unsafe { libc::tcgetattr(self.conf.inputfd, &mut old_modes) } == -1 && errno().0 == EIO {
let mut old_modes = MaybeUninit::uninit();
if unsafe { libc::tcgetattr(self.conf.inputfd, old_modes.as_mut_ptr()) } == -1
&& errno().0 == EIO
{
redirect_tty_output(false);
}
@@ -2275,7 +2279,7 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
if EXIT_STATE.load(Ordering::Relaxed) != ExitState::FinishedHandlers as _ {
// The order of the two conditions below is important. Try to restore the mode
// in all cases, but only complain if interactive.
if unsafe { libc::tcsetattr(self.conf.inputfd, TCSANOW, &old_modes) } == -1
if unsafe { libc::tcsetattr(self.conf.inputfd, TCSANOW, old_modes.as_ptr()) } == -1
&& is_interactive_session()
{
if errno().0 == EIO {
@@ -4259,10 +4263,10 @@ fn term_donate(quiet: bool /* = false */) {
/// Copy the (potentially changed) terminal modes and use them from now on.
pub fn term_copy_modes() {
let mut modes: libc::termios = unsafe { std::mem::zeroed() };
unsafe { libc::tcgetattr(STDIN_FILENO, &mut modes) };
let mut modes = MaybeUninit::uninit();
unsafe { libc::tcgetattr(STDIN_FILENO, modes.as_mut_ptr()) };
let mut tty_modes_for_external_cmds = TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap();
*tty_modes_for_external_cmds = modes;
*tty_modes_for_external_cmds = unsafe { modes.assume_init() };
// We still want to fix most egregious breakage.
// E.g. OPOST is *not* something that should be set globally,
// and 99% triggered by a crashed program.

View File

@@ -1,3 +1,4 @@
use std::mem::MaybeUninit;
use std::num::NonZeroI32;
use crate::common::exit_without_destructors;
@@ -131,8 +132,9 @@ pub fn signal_reset_handlers() {
for data in SIGNAL_TABLE.iter() {
if data.signal == libc::SIGHUP {
let mut oact: libc::sigaction = unsafe { std::mem::zeroed() };
unsafe { libc::sigaction(libc::SIGHUP, std::ptr::null(), &mut oact) };
let mut oact = MaybeUninit::uninit();
unsafe { libc::sigaction(libc::SIGHUP, std::ptr::null(), oact.as_mut_ptr()) };
let oact = unsafe { oact.assume_init() };
if oact.sa_sigaction == libc::SIG_IGN {
continue;
}
@@ -277,30 +279,31 @@ pub fn signal_handle(sig: Signal) {
}
pub static signals_to_default: Lazy<libc::sigset_t> = Lazy::new(|| {
let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut set) };
let mut set = MaybeUninit::uninit();
unsafe { libc::sigemptyset(set.as_mut_ptr()) };
for data in SIGNAL_TABLE.iter() {
// If SIGHUP is being ignored (e.g., because were were run via `nohup`) don't reset it.
// We don't special case other signals because if they're being ignored that shouldn't
// affect processes we spawn. They should get the default behavior for those signals.
if data.signal == libc::SIGHUP {
let mut act: libc::sigaction = unsafe { std::mem::zeroed() };
unsafe { libc::sigaction(data.signal.code(), std::ptr::null(), &mut act) };
let mut act = MaybeUninit::uninit();
unsafe { libc::sigaction(data.signal.code(), std::ptr::null(), act.as_mut_ptr()) };
let act = unsafe { act.assume_init() };
if act.sa_sigaction == libc::SIG_IGN {
continue;
}
}
unsafe { libc::sigaddset(&mut set, data.signal.code()) };
unsafe { libc::sigaddset(set.as_mut_ptr(), data.signal.code()) };
}
return set;
unsafe { set.assume_init() }
});
/// Ensure we did not inherit any blocked signals. See issue #3964.
pub fn signal_unblock_all() {
unsafe {
let mut iset: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&mut iset);
libc::sigprocmask(libc::SIG_SETMASK, &iset, std::ptr::null_mut());
let mut iset = MaybeUninit::uninit();
libc::sigemptyset(iset.as_mut_ptr());
libc::sigprocmask(libc::SIG_SETMASK, iset.as_ptr(), std::ptr::null_mut());
}
}

View File

@@ -5,6 +5,7 @@
use crate::parser::Parser;
use crate::wchar::prelude::*;
use crate::wutil::fish_wcstoi;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Mutex;
@@ -40,8 +41,9 @@ fn var_to_int_or(var: Option<EnvVar>, default: isize) -> isize {
fn read_termsize_from_tty() -> Option<Termsize> {
let mut ret: Option<Termsize> = None;
// Note: historically we've supported libc::winsize not existing.
let mut winsize: libc::winsize = unsafe { std::mem::zeroed() };
if unsafe { libc::ioctl(0, libc::TIOCGWINSZ, &mut winsize as *mut libc::winsize) } >= 0 {
let mut winsize = MaybeUninit::<libc::winsize>::uninit();
if unsafe { libc::ioctl(0, libc::TIOCGWINSZ, winsize.as_mut_ptr()) } >= 0 {
let mut winsize = unsafe { winsize.assume_init() };
// 0 values are unusable, fall back to the default instead.
if winsize.ws_col == 0 {
FLOG!(

View File

@@ -3,6 +3,7 @@
use crate::wchar::prelude::*;
use crate::wutil::wgetcwd;
use std::collections::HashMap;
use std::mem::MaybeUninit;
use std::time::{SystemTime, UNIX_EPOCH};
/// An environment built around an std::map.
@@ -69,9 +70,9 @@ fn return_timezone_hour(tstamp: SystemTime, timezone: &wstr) -> libc::c_int {
.as_secs()
.try_into()
.unwrap();
let mut local_time: libc::tm = unsafe { std::mem::zeroed() };
unsafe { libc::localtime_r(&tstamp, &mut local_time) };
let mut local_time = MaybeUninit::uninit();
unsafe { libc::localtime_r(&tstamp, local_time.as_mut_ptr()) };
let local_time = unsafe { local_time.assume_init() };
local_time.tm_hour
}

View File

@@ -4,6 +4,7 @@
use crate::flog::{FloggableDebug, FLOG};
use crate::reader::Reader;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::num::NonZeroU64;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
@@ -175,8 +176,8 @@ pub fn spawn<F: FnOnce() + Send + 'static>(callback: F) -> bool {
// (#7837). Conservatively don't try to mask SIGKILL or SIGSTOP either; that's ignored on Linux
// but maybe has an effect elsewhere.
let saved_set = unsafe {
let mut new_set: libc::sigset_t = std::mem::zeroed();
let new_set = &mut new_set as *mut _;
let mut new_set = MaybeUninit::uninit();
let new_set = new_set.as_mut_ptr();
libc::sigfillset(new_set);
libc::sigdelset(new_set, libc::SIGILL); // bad jump
libc::sigdelset(new_set, libc::SIGFPE); // divide-by-zero
@@ -682,8 +683,8 @@ fn perform_inner(&self, work_item: WorkItem) -> NonZeroU64 {
fn std_thread_inherits_sigmask() {
// First change our own thread mask
let (saved_set, t1_set) = unsafe {
let mut new_set: libc::sigset_t = std::mem::zeroed();
let new_set = &mut new_set as *mut _;
let mut new_set = MaybeUninit::uninit();
let new_set = new_set.as_mut_ptr();
libc::sigemptyset(new_set);
libc::sigaddset(new_set, libc::SIGILL); // mask bad jump
@@ -693,8 +694,8 @@ fn std_thread_inherits_sigmask() {
// Now get the current set that includes the masked SIGILL
let mut t1_set: libc::sigset_t = std::mem::zeroed();
let mut empty_set = std::mem::zeroed();
let empty_set = &mut empty_set as *mut _;
let mut empty_set = MaybeUninit::uninit();
let empty_set = empty_set.as_mut_ptr();
libc::sigemptyset(empty_set);
let result = libc::pthread_sigmask(libc::SIG_UNBLOCK, empty_set, &mut t1_set as *mut _);
assert_eq!(result, 0, "Failed to get own altered thread mask!");
@@ -706,8 +707,8 @@ fn std_thread_inherits_sigmask() {
let t2_set = std::thread::scope(|_| {
unsafe {
// Set a new thread sigmask and verify that the old one is what we expect it to be
let mut new_set: libc::sigset_t = std::mem::zeroed();
let new_set = &mut new_set as *mut _;
let mut new_set = MaybeUninit::uninit();
let new_set = new_set.as_mut_ptr();
libc::sigemptyset(new_set);
let mut saved_set2: libc::sigset_t = std::mem::zeroed();
let result = libc::pthread_sigmask(libc::SIG_BLOCK, new_set, &mut saved_set2 as *mut _);

View File

@@ -9,6 +9,7 @@
};
use std::cell::Cell;
use std::io;
use std::mem::MaybeUninit;
use std::os::fd::RawFd;
use std::ptr::{addr_of, NonNull};
use std::rc::Rc;
@@ -102,8 +103,9 @@ fn do_stat(&self) {
return;
}
let narrow = wcs2zstring(&self.name);
let mut s: libc::stat = unsafe { std::mem::zeroed() };
if unsafe { libc::fstatat(fd, narrow.as_ptr(), &mut s, 0) } == 0 {
let mut s = MaybeUninit::uninit();
if unsafe { libc::fstatat(fd, narrow.as_ptr(), s.as_mut_ptr(), 0) } == 0 {
let s = unsafe { s.assume_init() };
// st_dev is a dev_t, which is i32 on OpenBSD/Haiku and u32 in FreeBSD 11
#[allow(clippy::unnecessary_cast)]
let dev_inode = DevInode {