#![cfg_attr(target_os = "linux", allow(dead_code))]
use std::{io, os::fd::RawFd};
use crate::{SetattrValid, stat64};
const LINUX_EPERM: i32 = 1;
const LINUX_ENOENT: i32 = 2;
const LINUX_ESRCH: i32 = 3;
const LINUX_EINTR: i32 = 4;
const LINUX_EIO: i32 = 5;
const LINUX_ENXIO: i32 = 6;
const LINUX_ENOEXEC: i32 = 8;
const LINUX_EBADF: i32 = 9;
const LINUX_ECHILD: i32 = 10;
const LINUX_EAGAIN: i32 = 11;
const LINUX_ENOMEM: i32 = 12;
const LINUX_EACCES: i32 = 13;
const LINUX_EFAULT: i32 = 14;
const LINUX_ENOTBLK: i32 = 15;
const LINUX_EBUSY: i32 = 16;
const LINUX_EEXIST: i32 = 17;
const LINUX_EXDEV: i32 = 18;
const LINUX_ENODEV: i32 = 19;
const LINUX_ENOTDIR: i32 = 20;
const LINUX_EISDIR: i32 = 21;
const LINUX_EINVAL: i32 = 22;
const LINUX_ENFILE: i32 = 23;
const LINUX_EMFILE: i32 = 24;
const LINUX_ENOTTY: i32 = 25;
const LINUX_ETXTBSY: i32 = 26;
const LINUX_EFBIG: i32 = 27;
const LINUX_ENOSPC: i32 = 28;
const LINUX_ESPIPE: i32 = 29;
const LINUX_EROFS: i32 = 30;
const LINUX_EMLINK: i32 = 31;
const LINUX_EPIPE: i32 = 32;
const LINUX_EDOM: i32 = 33;
const LINUX_ERANGE: i32 = 34;
const LINUX_EDEADLK: i32 = 35;
const LINUX_ENAMETOOLONG: i32 = 36;
const LINUX_ENOLCK: i32 = 37;
pub(crate) const LINUX_ENOSYS: i32 = 38;
const LINUX_ENOTEMPTY: i32 = 39;
const LINUX_ELOOP: i32 = 40;
const LINUX_ENOMSG: i32 = 42;
const LINUX_EIDRM: i32 = 43;
const LINUX_ENOSTR: i32 = 60;
pub(crate) const LINUX_ENODATA: i32 = 61;
const LINUX_ETIME: i32 = 62;
const LINUX_ENOSR: i32 = 63;
const LINUX_EREMOTE: i32 = 66;
const LINUX_ENOLINK: i32 = 67;
const LINUX_EPROTO: i32 = 71;
const LINUX_EMULTIHOP: i32 = 72;
const LINUX_EBADMSG: i32 = 74;
const LINUX_EOVERFLOW: i32 = 75;
const LINUX_EILSEQ: i32 = 84;
const LINUX_EUSERS: i32 = 87;
const LINUX_ENOTSOCK: i32 = 88;
const LINUX_EDESTADDRREQ: i32 = 89;
const LINUX_EMSGSIZE: i32 = 90;
const LINUX_EPROTOTYPE: i32 = 91;
const LINUX_ENOPROTOOPT: i32 = 92;
const LINUX_EPROTONOSUPPORT: i32 = 93;
const LINUX_ESOCKTNOSUPPORT: i32 = 94;
const LINUX_EOPNOTSUPP: i32 = 95;
const LINUX_EPFNOSUPPORT: i32 = 96;
const LINUX_EAFNOSUPPORT: i32 = 97;
const LINUX_EADDRINUSE: i32 = 98;
const LINUX_EADDRNOTAVAIL: i32 = 99;
const LINUX_ENETDOWN: i32 = 100;
const LINUX_ENETUNREACH: i32 = 101;
const LINUX_ENETRESET: i32 = 102;
const LINUX_ECONNABORTED: i32 = 103;
const LINUX_ECONNRESET: i32 = 104;
const LINUX_ENOBUFS: i32 = 105;
const LINUX_EISCONN: i32 = 106;
const LINUX_ENOTCONN: i32 = 107;
const LINUX_ESHUTDOWN: i32 = 108;
const LINUX_ETOOMANYREFS: i32 = 109;
const LINUX_ETIMEDOUT: i32 = 110;
const LINUX_ECONNREFUSED: i32 = 111;
const LINUX_EHOSTDOWN: i32 = 112;
const LINUX_EHOSTUNREACH: i32 = 113;
const LINUX_EALREADY: i32 = 114;
const LINUX_EINPROGRESS: i32 = 115;
const LINUX_ESTALE: i32 = 116;
const LINUX_EDQUOT: i32 = 122;
const LINUX_ECANCELED: i32 = 125;
const LINUX_EOWNERDEAD: i32 = 130;
const LINUX_ENOTRECOVERABLE: i32 = 131;
#[cfg(target_os = "linux")]
pub(crate) const MODE_TYPE_MASK: u32 = libc::S_IFMT;
#[cfg(target_os = "macos")]
pub(crate) const MODE_TYPE_MASK: u32 = libc::S_IFMT as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_REG: u32 = libc::S_IFREG;
#[cfg(target_os = "macos")]
pub(crate) const MODE_REG: u32 = libc::S_IFREG as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_DIR: u32 = libc::S_IFDIR;
#[cfg(target_os = "macos")]
pub(crate) const MODE_DIR: u32 = libc::S_IFDIR as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_LNK: u32 = libc::S_IFLNK;
#[cfg(target_os = "macos")]
pub(crate) const MODE_LNK: u32 = libc::S_IFLNK as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_CHR: u32 = libc::S_IFCHR;
#[cfg(target_os = "macos")]
pub(crate) const MODE_CHR: u32 = libc::S_IFCHR as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_BLK: u32 = libc::S_IFBLK;
#[cfg(target_os = "macos")]
pub(crate) const MODE_BLK: u32 = libc::S_IFBLK as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_FIFO: u32 = libc::S_IFIFO;
#[cfg(target_os = "macos")]
pub(crate) const MODE_FIFO: u32 = libc::S_IFIFO as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_SOCK: u32 = libc::S_IFSOCK;
#[cfg(target_os = "macos")]
pub(crate) const MODE_SOCK: u32 = libc::S_IFSOCK as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_SETUID: u32 = libc::S_ISUID;
#[cfg(target_os = "macos")]
pub(crate) const MODE_SETUID: u32 = libc::S_ISUID as u32;
#[cfg(target_os = "linux")]
pub(crate) const MODE_SETGID: u32 = libc::S_ISGID;
#[cfg(target_os = "macos")]
pub(crate) const MODE_SETGID: u32 = libc::S_ISGID as u32;
pub(crate) const DIRENT_REG: u32 = libc::DT_REG as u32;
pub(crate) const DIRENT_DIR: u32 = libc::DT_DIR as u32;
pub(crate) const DIRENT_LNK: u32 = libc::DT_LNK as u32;
pub(crate) const DIRENT_CHR: u32 = libc::DT_CHR as u32;
pub(crate) const DIRENT_BLK: u32 = libc::DT_BLK as u32;
pub(crate) const DIRENT_FIFO: u32 = libc::DT_FIFO as u32;
pub(crate) const DIRENT_SOCK: u32 = libc::DT_SOCK as u32;
pub(crate) const ACCESS_F_OK: u32 = libc::F_OK as u32;
pub(crate) const ACCESS_R_OK: u32 = libc::R_OK as u32;
pub(crate) const ACCESS_W_OK: u32 = libc::W_OK as u32;
pub(crate) const ACCESS_X_OK: u32 = libc::X_OK as u32;
pub(crate) fn build_timespecs(attr: stat64, valid: SetattrValid) -> [libc::timespec; 2] {
let mut times = [libc::timespec {
tv_sec: 0,
tv_nsec: libc::UTIME_OMIT,
}; 2];
if valid.contains(SetattrValid::ATIME) {
if valid.contains(SetattrValid::ATIME_NOW) {
times[0].tv_nsec = libc::UTIME_NOW;
} else {
times[0].tv_sec = attr.st_atime;
times[0].tv_nsec = attr.st_atime_nsec;
}
}
if valid.contains(SetattrValid::MTIME) {
if valid.contains(SetattrValid::MTIME_NOW) {
times[1].tv_nsec = libc::UTIME_NOW;
} else {
times[1].tv_sec = attr.st_mtime;
times[1].tv_nsec = attr.st_mtime_nsec;
}
}
times
}
#[cfg(target_os = "linux")]
pub(crate) fn linux_error(error: io::Error) -> io::Error {
error
}
#[cfg(target_os = "macos")]
pub(crate) fn linux_error(error: io::Error) -> io::Error {
io::Error::from_raw_os_error(linux_errno_raw(error.raw_os_error().unwrap_or(libc::EIO)))
}
#[cfg(target_os = "macos")]
fn linux_errno_raw(errno: i32) -> i32 {
match errno {
libc::EPERM => LINUX_EPERM,
libc::ENOENT => LINUX_ENOENT,
libc::ESRCH => LINUX_ESRCH,
libc::EINTR => LINUX_EINTR,
libc::EIO => LINUX_EIO,
libc::ENXIO => LINUX_ENXIO,
libc::ENOEXEC => LINUX_ENOEXEC,
libc::EBADF => LINUX_EBADF,
libc::ECHILD => LINUX_ECHILD,
libc::EDEADLK => LINUX_EDEADLK,
libc::ENOMEM => LINUX_ENOMEM,
libc::EACCES => LINUX_EACCES,
libc::EFAULT => LINUX_EFAULT,
libc::ENOTBLK => LINUX_ENOTBLK,
libc::EBUSY => LINUX_EBUSY,
libc::EEXIST => LINUX_EEXIST,
libc::EXDEV => LINUX_EXDEV,
libc::ENODEV => LINUX_ENODEV,
libc::ENOTDIR => LINUX_ENOTDIR,
libc::EISDIR => LINUX_EISDIR,
libc::EINVAL => LINUX_EINVAL,
libc::ENFILE => LINUX_ENFILE,
libc::EMFILE => LINUX_EMFILE,
libc::ENOTTY => LINUX_ENOTTY,
libc::ETXTBSY => LINUX_ETXTBSY,
libc::EFBIG => LINUX_EFBIG,
libc::ENOSPC => LINUX_ENOSPC,
libc::ESPIPE => LINUX_ESPIPE,
libc::EROFS => LINUX_EROFS,
libc::EMLINK => LINUX_EMLINK,
libc::EPIPE => LINUX_EPIPE,
libc::EDOM => LINUX_EDOM,
libc::EAGAIN => LINUX_EAGAIN,
libc::EINPROGRESS => LINUX_EINPROGRESS,
libc::EALREADY => LINUX_EALREADY,
libc::ENOTSOCK => LINUX_ENOTSOCK,
libc::EDESTADDRREQ => LINUX_EDESTADDRREQ,
libc::EMSGSIZE => LINUX_EMSGSIZE,
libc::EPROTOTYPE => LINUX_EPROTOTYPE,
libc::ENOPROTOOPT => LINUX_ENOPROTOOPT,
libc::EPROTONOSUPPORT => LINUX_EPROTONOSUPPORT,
libc::ESOCKTNOSUPPORT => LINUX_ESOCKTNOSUPPORT,
libc::EPFNOSUPPORT => LINUX_EPFNOSUPPORT,
libc::EAFNOSUPPORT => LINUX_EAFNOSUPPORT,
libc::EADDRINUSE => LINUX_EADDRINUSE,
libc::EADDRNOTAVAIL => LINUX_EADDRNOTAVAIL,
libc::ENETDOWN => LINUX_ENETDOWN,
libc::ENETUNREACH => LINUX_ENETUNREACH,
libc::ENETRESET => LINUX_ENETRESET,
libc::ECONNABORTED => LINUX_ECONNABORTED,
libc::ECONNRESET => LINUX_ECONNRESET,
libc::ENOBUFS => LINUX_ENOBUFS,
libc::EISCONN => LINUX_EISCONN,
libc::ENOTCONN => LINUX_ENOTCONN,
libc::ESHUTDOWN => LINUX_ESHUTDOWN,
libc::ETOOMANYREFS => LINUX_ETOOMANYREFS,
libc::ETIMEDOUT => LINUX_ETIMEDOUT,
libc::ECONNREFUSED => LINUX_ECONNREFUSED,
libc::ELOOP => LINUX_ELOOP,
libc::ENAMETOOLONG => LINUX_ENAMETOOLONG,
libc::EHOSTDOWN => LINUX_EHOSTDOWN,
libc::EHOSTUNREACH => LINUX_EHOSTUNREACH,
libc::ENOTEMPTY => LINUX_ENOTEMPTY,
libc::EUSERS => LINUX_EUSERS,
libc::EDQUOT => LINUX_EDQUOT,
libc::ESTALE => LINUX_ESTALE,
libc::EREMOTE => LINUX_EREMOTE,
libc::ENOLCK => LINUX_ENOLCK,
libc::ENOSYS => LINUX_ENOSYS,
libc::EOVERFLOW => LINUX_EOVERFLOW,
libc::ECANCELED => LINUX_ECANCELED,
libc::EIDRM => LINUX_EIDRM,
libc::ENOMSG => LINUX_ENOMSG,
libc::EILSEQ => LINUX_EILSEQ,
libc::ENOATTR => LINUX_ENODATA,
libc::EBADMSG => LINUX_EBADMSG,
libc::EMULTIHOP => LINUX_EMULTIHOP,
libc::ENODATA => LINUX_ENODATA,
libc::ENOLINK => LINUX_ENOLINK,
libc::ENOSR => LINUX_ENOSR,
libc::ENOSTR => LINUX_ENOSTR,
libc::EPROTO => LINUX_EPROTO,
libc::ETIME => LINUX_ETIME,
libc::EOPNOTSUPP => LINUX_EOPNOTSUPP,
libc::ENOTRECOVERABLE => LINUX_ENOTRECOVERABLE,
libc::EOWNERDEAD => LINUX_EOWNERDEAD,
_ => LINUX_EIO,
}
}
pub(crate) fn eio() -> io::Error {
io::Error::from_raw_os_error(LINUX_EIO)
}
pub(crate) fn ebadf() -> io::Error {
io::Error::from_raw_os_error(LINUX_EBADF)
}
pub(crate) fn einval() -> io::Error {
io::Error::from_raw_os_error(LINUX_EINVAL)
}
pub(crate) fn eacces() -> io::Error {
io::Error::from_raw_os_error(LINUX_EACCES)
}
pub(crate) fn eperm() -> io::Error {
io::Error::from_raw_os_error(LINUX_EPERM)
}
pub(crate) fn enosys() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENOSYS)
}
pub(crate) fn enoent() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENOENT)
}
pub(crate) fn enodata() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENODATA)
}
pub(crate) fn eisdir() -> io::Error {
io::Error::from_raw_os_error(LINUX_EISDIR)
}
pub(crate) fn enotdir() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENOTDIR)
}
pub(crate) fn enotempty() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENOTEMPTY)
}
pub(crate) fn eloop() -> io::Error {
io::Error::from_raw_os_error(LINUX_ELOOP)
}
pub(crate) fn enametoolong() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENAMETOOLONG)
}
pub(crate) fn eexist() -> io::Error {
io::Error::from_raw_os_error(LINUX_EEXIST)
}
pub(crate) fn enospc() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENOSPC)
}
pub(crate) fn efbig() -> io::Error {
io::Error::from_raw_os_error(LINUX_EFBIG)
}
pub(crate) fn eopnotsupp() -> io::Error {
io::Error::from_raw_os_error(LINUX_EOPNOTSUPP)
}
pub(crate) fn enodev() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENODEV)
}
pub(crate) fn enxio() -> io::Error {
io::Error::from_raw_os_error(LINUX_ENXIO)
}
pub(crate) fn erange() -> io::Error {
io::Error::from_raw_os_error(LINUX_ERANGE)
}
pub(crate) fn erofs() -> io::Error {
io::Error::from_raw_os_error(LINUX_EROFS)
}
pub(crate) fn is_enoent(err: &io::Error) -> bool {
err.raw_os_error() == Some(2)
}
pub(crate) fn fstat(fd: RawFd) -> io::Result<stat64> {
let mut st = unsafe { std::mem::zeroed::<stat64>() };
#[cfg(target_os = "linux")]
let ret = unsafe { libc::fstat64(fd, &mut st) };
#[cfg(target_os = "macos")]
let ret = unsafe { libc::fstat(fd, &mut st) };
if ret < 0 {
Err(linux_error(io::Error::last_os_error()))
} else {
Ok(st)
}
}
#[cfg(target_os = "linux")]
pub(crate) fn mode_u32(mode: libc::mode_t) -> u32 {
mode
}
#[cfg(target_os = "macos")]
pub(crate) fn mode_u32(mode: libc::mode_t) -> u32 {
mode as u32
}
pub(crate) fn mode_file_type(mode: libc::mode_t) -> u32 {
mode_u32(mode) & MODE_TYPE_MASK
}
pub(crate) fn dirent_type_from_mode(file_type: u32) -> u32 {
match file_type {
MODE_LNK => DIRENT_LNK,
MODE_DIR => DIRENT_DIR,
MODE_CHR => DIRENT_CHR,
MODE_BLK => DIRENT_BLK,
MODE_FIFO => DIRENT_FIFO,
MODE_SOCK => DIRENT_SOCK,
_ => DIRENT_REG,
}
}
pub(crate) fn stat_ino(st: &stat64) -> u64 {
st.st_ino
}
#[cfg(target_os = "linux")]
pub(crate) fn stat_dev(st: &stat64) -> u64 {
st.st_dev
}
#[cfg(target_os = "macos")]
pub(crate) fn stat_dev(st: &stat64) -> u64 {
st.st_dev as u64
}
#[cfg(target_os = "linux")]
pub(crate) fn readlink_fd(fd: RawFd) -> io::Result<Vec<u8>> {
let mut buf = vec![0u8; libc::PATH_MAX as usize];
let len = unsafe {
libc::readlinkat(
fd,
c"".as_ptr(),
buf.as_mut_ptr() as *mut libc::c_char,
buf.len(),
)
};
if len < 0 {
Err(linux_error(io::Error::last_os_error()))
} else {
buf.truncate(len as usize);
Ok(buf)
}
}
#[cfg(target_os = "linux")]
#[repr(C)]
pub(crate) struct OpenHow {
flags: u64,
mode: u64,
resolve: u64,
}
#[cfg(target_os = "linux")]
pub(crate) const RESOLVE_BENEATH: u64 = 0x08;
#[cfg(target_os = "linux")]
pub(crate) const RESOLVE_NO_SYMLINKS: u64 = 0x04;
#[cfg(target_os = "linux")]
pub(crate) const RESOLVE_NO_MAGICLINKS: u64 = 0x02;
#[cfg(target_os = "linux")]
const OPENAT2_RESOLVE_FLAGS: u64 = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS;
#[cfg(target_os = "linux")]
const SYS_OPENAT2: libc::c_long = 437;
#[cfg(target_os = "linux")]
pub(crate) fn probe_openat2() -> bool {
let how = OpenHow {
flags: libc::O_CLOEXEC as u64 | libc::O_PATH as u64,
mode: 0,
resolve: OPENAT2_RESOLVE_FLAGS,
};
let ret = unsafe {
libc::syscall(
SYS_OPENAT2,
libc::AT_FDCWD,
c".".as_ptr(),
&how as *const OpenHow,
std::mem::size_of::<OpenHow>(),
)
};
if ret >= 0 {
unsafe { libc::close(ret as i32) };
true
} else {
!matches!(
io::Error::last_os_error().raw_os_error(),
Some(libc::ENOSYS | libc::EINVAL)
)
}
}
#[cfg(target_os = "linux")]
pub(crate) fn open_beneath(
dirfd: RawFd,
name: *const libc::c_char,
flags: i32,
use_openat2: bool,
) -> RawFd {
if use_openat2 {
let how = OpenHow {
flags: (flags | libc::O_CLOEXEC) as u64,
mode: 0,
resolve: OPENAT2_RESOLVE_FLAGS,
};
let ret = unsafe {
libc::syscall(
SYS_OPENAT2,
dirfd,
name,
&how as *const OpenHow,
std::mem::size_of::<OpenHow>(),
) as i32
};
if ret >= 0 || io::Error::last_os_error().raw_os_error() != Some(libc::ENOSYS) {
return ret;
}
}
unsafe { libc::openat(dirfd, name, flags | libc::O_CLOEXEC) }
}
#[cfg(target_os = "linux")]
pub(crate) fn statx_to_stat64(stx: &libc::statx) -> stat64 {
let mut st: stat64 = unsafe { std::mem::zeroed() };
st.st_dev = makedev(stx.stx_dev_major, stx.stx_dev_minor);
st.st_ino = stx.stx_ino;
st.st_nlink = stx.stx_nlink as _;
st.st_mode = stx.stx_mode as _;
st.st_uid = stx.stx_uid;
st.st_gid = stx.stx_gid;
st.st_rdev = makedev(stx.stx_rdev_major, stx.stx_rdev_minor);
st.st_size = stx.stx_size as _;
st.st_blksize = stx.stx_blksize as _;
st.st_blocks = stx.stx_blocks as _;
st.st_atime = stx.stx_atime.tv_sec;
st.st_atime_nsec = stx.stx_atime.tv_nsec as _;
st.st_mtime = stx.stx_mtime.tv_sec;
st.st_mtime_nsec = stx.stx_mtime.tv_nsec as _;
st.st_ctime = stx.stx_ctime.tv_sec;
st.st_ctime_nsec = stx.stx_ctime.tv_nsec as _;
st
}
#[cfg(target_os = "linux")]
fn makedev(major: u32, minor: u32) -> u64 {
((major as u64 & 0xfffff000) << 32)
| ((major as u64 & 0x00000fff) << 8)
| ((minor as u64 & 0xffffff00) << 12)
| (minor as u64 & 0x000000ff)
}
pub(crate) fn fstatat_nofollow(dirfd: RawFd, name: &std::ffi::CStr) -> io::Result<stat64> {
let mut st = unsafe { std::mem::zeroed::<stat64>() };
#[cfg(target_os = "linux")]
let ret = unsafe { libc::fstatat64(dirfd, name.as_ptr(), &mut st, libc::AT_SYMLINK_NOFOLLOW) };
#[cfg(target_os = "macos")]
let ret = unsafe { libc::fstatat(dirfd, name.as_ptr(), &mut st, libc::AT_SYMLINK_NOFOLLOW) };
if ret < 0 {
Err(linux_error(io::Error::last_os_error()))
} else {
Ok(st)
}
}