From b7f7fae9e124c6dd7b4232983c636df43ffb4ed6 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Fri, 19 Jul 2024 13:03:11 -0600 Subject: [PATCH 1/8] Updated stat interposing. --- xetldfs/Cargo.lock | 10 -- xetldfs/Cargo.toml | 1 - xetldfs/src/interposing_linux.rs | 79 +++++++++ xetldfs/src/interposing_osx.rs | 47 ++++++ xetldfs/src/lib.rs | 173 ++----------------- xetldfs/src/path_utils.rs | 25 --- xetldfs/src/reporting.rs | 4 +- xetldfs/src/stat_interposing.rs | 281 +++++++++++++++++++++++++++++++ 8 files changed, 421 insertions(+), 199 deletions(-) create mode 100644 xetldfs/src/interposing_linux.rs create mode 100644 xetldfs/src/interposing_osx.rs create mode 100644 xetldfs/src/stat_interposing.rs diff --git a/xetldfs/Cargo.lock b/xetldfs/Cargo.lock index 6263f6a0..1367a598 100644 --- a/xetldfs/Cargo.lock +++ b/xetldfs/Cargo.lock @@ -3117,15 +3117,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "redhook" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e109b8469dbbe6d7cbe18e5ebd65d4a134e2f4b91304ad9213984cad1d70a6" -dependencies = [ - "libc", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -5000,7 +4991,6 @@ dependencies = [ "libxet", "openssl-probe", "path-absolutize", - "redhook", "regex", "tempdir", "tempfile", diff --git a/xetldfs/Cargo.toml b/xetldfs/Cargo.toml index bcb78024..ef1e063e 100644 --- a/xetldfs/Cargo.toml +++ b/xetldfs/Cargo.toml @@ -27,7 +27,6 @@ anyhow = "1" tokio = { version = "1", features = ["full"] } errno = "0.3.9" ctor = "0.1" -redhook = "*" openssl-probe = "0.1.5" clap = { version = "3.1.6", features = ["derive"] } path-absolutize = "3.1.1" diff --git a/xetldfs/src/interposing_linux.rs b/xetldfs/src/interposing_linux.rs new file mode 100644 index 00000000..171d45e4 --- /dev/null +++ b/xetldfs/src/interposing_linux.rs @@ -0,0 +1,79 @@ +// From redhook, https://github.com/geofft/redhook, modified to not crash on non-existent interposed. + +use libc::{c_char, c_void}; + +#[link(name = "dl")] +extern "C" { + fn dlsym(handle: *const c_void, symbol: *const c_char) -> *const c_void; +} + +const RTLD_NEXT: *const c_void = -1isize as *const c_void; + +// NOTE: May be the null pointer. +pub unsafe fn dlsym_next(symbol: &'static str) -> *const u8 { + let ptr = dlsym(RTLD_NEXT, symbol.as_ptr() as *const c_char); + ptr as *const u8 +} + +#[macro_export] +macro_rules! hook { + (unsafe fn $real_fn:ident ( $($v:ident : $t:ty),* ) -> $r:ty => $hook_fn:ident $body:block) => { + #[allow(non_camel_case_types)] + pub struct $real_fn {__private_field: ()} + #[allow(non_upper_case_globals)] + static $real_fn: $real_fn = $real_fn {__private_field: ()}; + + impl $real_fn { + fn get(&self) -> unsafe extern fn ( $($v : $t),* ) -> $r { + use ::std::sync::Once; + + static mut REAL: *const u8 = 0 as *const u8; + static mut ONCE: Once = Once::new(); + + unsafe { + ONCE.call_once(|| { + REAL = $crate::interposing_linux::dlsym_next(concat!(stringify!($real_fn), "\0")); + if REAL == null() { + panic!("XetLDFS: Attempting to call hook to non-existant function {}.", stringify!($real_fn)); + } + }); + ::std::mem::transmute(REAL) + } + } + + #[no_mangle] + pub unsafe extern fn $real_fn ( $($v : $t),* ) -> $r { + ::std::panic::catch_unwind(|| $hook_fn ( $($v),* )).unwrap_or_else(|_| $real_fn.get() ( $($v),* )) + } + } + + pub unsafe fn $hook_fn ( $($v : $t),* ) -> $r { + $body + } + }; + + (unsafe fn $real_fn:ident ( $($v:ident : $t:ty),* ) => $hook_fn:ident $body:block) => { + $crate::hook! { unsafe fn $real_fn ( $($v : $t),* ) -> () => $hook_fn $body } + }; +} + +#[macro_export] +macro_rules! real { + ($real_fn:ident) => { + let fn_ptr = $real_fn.get(); + if fn_ptr == null { + panic!( + "XetLDFS: AAttempting to call non-existant function {}.", + stringify!($real_fn) + ); + } + fn_ptr + }; +} + +#[macro_export] +macro_rules! fn_is_valid { + ($real_fn:ident) => { + $real_fn.get() != null() + }; +} diff --git a/xetldfs/src/interposing_osx.rs b/xetldfs/src/interposing_osx.rs new file mode 100644 index 00000000..298f334c --- /dev/null +++ b/xetldfs/src/interposing_osx.rs @@ -0,0 +1,47 @@ +#[macro_export] +macro_rules! hook { + (unsafe fn $real_fn:ident ( $($v:ident : $t:ty),* ) -> $r:ty => $hook_fn:ident $body:block) => { + pub mod $real_fn { + #[allow(non_camel_case_types)] + pub struct $real_fn { + _new: *const (), + _old: *const (), + } + + #[allow(dead_code)] + #[allow(non_upper_case_globals)] + #[link_section="__DATA,__interpose"] + pub static mut $real_fn: $real_fn = $real_fn { + _new: super::$hook_fn as *const (), + _old: super::$real_fn as *const (), + }; + } + + extern { + pub fn $real_fn ( $($v : $t),* ) -> $r; + } + + pub unsafe extern fn $hook_fn ( $($v : $t),* ) -> $r { + ::std::panic::catch_unwind(|| $body ).unwrap_or_else(|_| $real_fn ( $($v),* )) + } + }; + + (unsafe fn $real_fn:ident ( $($v:ident : $t:ty),* ) => $hook_fn:ident $body:block) => { + $crate::hook! { unsafe fn $real_fn ( $($v : $t),* ) -> () => $hook_fn $body } + }; +} + +#[macro_export] +macro_rules! real { + ($real_fn:ident) => { + $real_fn + }; +} + +// Don't have to worry about this. +#[macro_export] +macro_rules! fn_is_valid { + ($real_fn:ident) => { + true + }; +} diff --git a/xetldfs/src/lib.rs b/xetldfs/src/lib.rs index 8b777342..8ecc80d2 100644 --- a/xetldfs/src/lib.rs +++ b/xetldfs/src/lib.rs @@ -1,14 +1,23 @@ use libc::*; +use stat_interposing::{is_regular_fd, is_regular_file}; use std::ffi::CStr; use std::ptr::null_mut; -#[macro_use] -extern crate redhook; extern crate libc; #[macro_use] mod reporting; +#[cfg(target_os = "macos")] +#[macro_use] +pub mod interposing_osx; + +#[cfg(target_os = "linux")] +#[macro_use] +pub mod interposing_linux; + +pub mod stat_interposing; + mod path_utils; mod repo_path_utils; mod runtime; @@ -17,7 +26,7 @@ mod xet_interface; mod xet_repo_wrapper; mod xet_rfile; -use crate::path_utils::{is_regular_fd, is_regular_file, resolve_path_from_fd}; +use crate::path_utils::resolve_path_from_fd; use crate::runtime::{ activate_fd_runtime, interposing_disabled, process_in_interposable_state, with_interposing_disabled, @@ -27,8 +36,6 @@ use crate::runtime::{ use crate::utils::{c_to_str, open_flags_from_mode_string, C_EMPTY_STR}; use xet_interface as xet; -// 0666, copied from sys/stat.h -const DEFFILEMODE: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; #[inline] unsafe fn fopen_impl( @@ -256,162 +263,6 @@ hook! { } } } - -unsafe fn stat_impl(fd: c_int, buf: *mut libc::stat) -> c_int { - let r = real!(fstat)(fd, buf); - - if r == EOF { - return EOF; - } - - if let Some(fd_info) = xet::maybe_fd_read_managed(fd) { - ld_trace!("XetLDFS: fstat called on {fd} is managed"); - fd_info.update_stat(buf); - } - - r -} - -hook! { - unsafe fn fstat(fd: c_int, buf: *mut libc::stat) -> c_int => my_fstat { - ld_func_trace!("fstat", fd); - if interposing_disabled() { return real!(fstat)(fd, buf); } - let _ig = with_interposing_disabled(); - - stat_impl(fd, buf) - } -} - -unsafe fn real_fstat(fd: c_int, buf: *mut libc::stat) -> c_int { - real!(fstat)(fd, buf) -} - -#[cfg(target_os = "linux")] -hook! { - unsafe fn fstat64(fd: c_int, buf: *mut libc::stat) -> c_int => my_fstat64 { - ld_func_trace!("fstat64", fd); - if interposing_disabled() { return real!(fstat64)(fd, buf); } - let _ig = with_interposing_disabled(); - - stat_impl(fd, buf) - } -} - -hook! { - unsafe fn stat(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => my_stat { - ld_func_trace!("stat", pathname); - if !process_in_interposable_state() { return real!(stat)(pathname, buf); } - - let fd = my_open(pathname, O_RDONLY, DEFFILEMODE); - if fd == -1 { - return real!(stat)(pathname, buf); - } - - stat_impl(fd, buf) - } -} - -#[allow(unused)] -unsafe fn real_stat(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int { - real!(stat)(pathname, buf) -} - -#[cfg(target_os = "linux")] -hook! { - unsafe fn stat64(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => my_stat64 { - ld_func_trace!("stat64", pathname); - if !process_in_interposable_state() { return real!(stat64)(pathname, buf); } - - let fd = my_open(pathname, O_RDONLY, DEFFILEMODE); - if fd == -1 { - return real!(stat64)(pathname, buf); - } - - stat_impl(fd, buf) - } -} - -hook! { - unsafe fn fstatat(dirfd: libc::c_int, pathname: *const libc::c_char, buf: *mut libc::stat, flags: libc::c_int) -> libc::c_int => my_fstatat { - ld_func_trace!("fstatat", dirfd, pathname); - if !process_in_interposable_state() { return real!(fstatat)(dirfd, pathname, buf, flags); } - - let fd = { - if pathname.is_null() || *pathname == (0 as c_char) { - dirfd - } else { - my_openat(dirfd, pathname, flags, DEFFILEMODE) - } - }; - if fd == -1 { - return real!(fstatat)(dirfd, pathname, buf, flags); - } - - stat_impl(fd, buf) - } -} - -#[cfg(target_os = "linux")] -hook! { - unsafe fn fstatat64(dirfd: libc::c_int, pathname: *const libc::c_char, buf: *mut libc::stat, flags: libc::c_int) -> libc::c_int => my_fstatat64 { - ld_func_trace!("fstatat64", dirfd, pathname); - if !process_in_interposable_state() { return real!(fstatat)(dirfd, pathname, buf, flags); } - - let fd = { - if pathname.is_null() || *pathname == (0 as c_char) { - dirfd - } else { - my_openat(dirfd, pathname, flags, DEFFILEMODE) - } - }; - if fd == -1 { - return real!(fstatat64)(dirfd, pathname, buf, flags); - } - - stat_impl(fd, buf) - } -} - -#[cfg(target_os = "linux")] -hook! { - unsafe fn statx(dirfd: libc::c_int, pathname: *const libc::c_char, flags: libc::c_int, mask: libc::c_uint, statxbuf: *mut libc::statx) -> libc::c_int => my_statx { - ld_func_trace!("statx", dirfd, pathname, flags, mask); - if !process_in_interposable_state() { return real!(statx)(dirfd, pathname, flags, mask, statxbuf); } - - let fd = { - if pathname.is_null() || *pathname == (0 as c_char) { - dirfd - } else { - ld_trace!("statx: attempting to open path on {dirfd}, pathname = {:?}", c_to_str(pathname)); - my_openat(dirfd, pathname, flags, DEFFILEMODE) - } - }; - - if fd == -1 { - ld_trace!("statx: openat returned -1, passing through."); - return real!(statx)(dirfd, pathname, flags, mask, statxbuf); - } - - // If pathname is an empty string and the AT_EMPTY_PATH flag - // is specified in flags (see below), then the target file is - // the one referred to by the file descriptor dirfd. - let ret = real!(statx)(fd, C_EMPTY_STR, AT_EMPTY_PATH | flags, mask, statxbuf); - if ret == EOF { - return real!(statx)(dirfd, pathname, flags, mask, statxbuf); - } - - if let Some(fd_info) = xet::maybe_fd_read_managed(fd) { - ld_trace!("statx: update_statx called on {fd}, is managed"); - fd_info.update_statx(statxbuf); - } else { - ld_trace!("statx called on {fd}; passed through."); - } - - - ret - } -} - hook! { unsafe fn lseek(fd: libc::c_int, offset: libc::off_t, whence: libc::c_int) -> libc::off_t => my_lseek { ld_func_trace!("lseek", fd, offset, whence); diff --git a/xetldfs/src/path_utils.rs b/xetldfs/src/path_utils.rs index bfe4cb44..a755146c 100644 --- a/xetldfs/src/path_utils.rs +++ b/xetldfs/src/path_utils.rs @@ -1,7 +1,6 @@ use path_absolutize::Absolutize; use crate::utils::{c_chars_to_cstring, c_to_str}; -use crate::{real_fstat, real_stat}; use std::borrow::Cow; use std::ffi::{CStr, CString}; use std::os::raw::c_char; @@ -129,27 +128,3 @@ pub fn resolve_path_from_fd(dirfd: libc::c_int, path: *const libc::c_char) -> Op Some(c_chars_to_cstring(dest_path)) } } - -pub fn is_regular_file(pathname: *const libc::c_char) -> bool { - let mut buf: libc::stat = unsafe { std::mem::zeroed() }; - let buf_ptr = &mut buf as *mut libc::stat; - unsafe { - let ret = real_stat(pathname, buf_ptr); - if ret == -1 { - return false; - } - (*buf_ptr).st_mode & libc::S_IFMT == libc::S_IFREG - } -} - -pub fn is_regular_fd(fd: libc::c_int) -> bool { - let mut buf: libc::stat = unsafe { std::mem::zeroed() }; - let buf_ptr = &mut buf as *mut libc::stat; - unsafe { - let ret = real_fstat(fd, buf_ptr); - if ret == -1 { - return false; - } - (*buf_ptr).st_mode & libc::S_IFMT == libc::S_IFREG - } -} diff --git a/xetldfs/src/reporting.rs b/xetldfs/src/reporting.rs index 12e8bf66..fb0fd56e 100644 --- a/xetldfs/src/reporting.rs +++ b/xetldfs/src/reporting.rs @@ -1,5 +1,5 @@ -pub const ENABLE_CALL_TRACING: bool = false; -pub const ENABLE_CALL_TRACING_FULL: bool = false; +pub const ENABLE_CALL_TRACING: bool = true; +pub const ENABLE_CALL_TRACING_FULL: bool = true; #[macro_export] macro_rules! ld_trace { diff --git a/xetldfs/src/stat_interposing.rs b/xetldfs/src/stat_interposing.rs new file mode 100644 index 00000000..a99a8d91 --- /dev/null +++ b/xetldfs/src/stat_interposing.rs @@ -0,0 +1,281 @@ +use crate::runtime::{ + interposing_disabled, process_in_interposable_state, with_interposing_disabled, +}; +use crate::{hook, my_open, my_openat, real}; +use libc::*; + +// 0666, copied from sys/stat.h +const DEFFILEMODE: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + +#[derive(Debug)] +enum PathMode { + PathName(*const libc::c_char), + Fd(c_int), + PathFromFd(c_int, *const libc::c_char), +} + +enum StatStructBuf { + Stat(*mut libc::stat), + + #[cfg(target_os = "linux")] + Stat64(*mut libc::stat), + + #[cfg(target_os = "linux")] + StatX(*mut libc::statx), +} + +#[inline] +unsafe fn stat_impl( + name: &str, + file_info: PathMode, + stat_buf: StatStructBuf, + base: impl Fn() -> c_int, +) -> c_int { + ld_func_trace!("{name}", file_info); + + let r = base(); + + if !process_in_interposable_state() || r < 0 { + return r; + } + + let (fd, respect_interposing) = match file_info { + PathMode::PathName(pathname) => (my_open(pathname, O_RDONLY, DEFFILEMODE), false), + PathMode::Fd(fd) => (fd, true), + PathMode::PathFromFd(dirfd, pathname) => { + if pathname.is_null() || *pathname == (0 as c_char) { + (dirfd, true) + } else { + (my_openat(dirfd, pathname, O_RDONLY, DEFFILEMODE), false) + } + } + }; + + let _maybe_lg = { + if respect_interposing { + if interposing_disabled() { + return r; + } + Some(with_interposing_disabled()) + } else { + None + } + }; + + // Ignore stdin, stdout, and stderr + if fd >= 3 { + if let Some(fd_info) = crate::xet_interface::maybe_fd_read_managed(fd) { + ld_trace!("XetLDFS: {name} called on {fd} is managed"); + + match stat_buf { + StatStructBuf::Stat(buf) => { + fd_info.update_stat(buf); + } + #[cfg(target_os = "linux")] + StatStructBuf::Stat64(buf) => { + fd_info.update_stat64(buf); + } + #[cfg(target_os = "linux")] + StatStructBuf::StatX(buf) => { + fd_info.update_statx(buf); + } + }; + } + } + + r +} + +// int stat(const char *pathname, struct stat *buf); +hook! { + unsafe fn stat(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => stat_hook { + stat_impl("stat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || real!(stat)(pathname, buf)) + } +} + +// int __xstat(int ver, const char *pathname, struct stat *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __xstat(ver : c_int, pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => __xstat_hook { + stat_impl("__xstat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || real!(__xstat)(ver, pathname, buf)) + } +} + +// int stat64(const char *pathname, struct stat64 *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn stat64(pathname: *const libc::c_char, buf: *mut libc::stat64) -> c_int => stat64_hook { + stat_impl("stat64", PathMode::PathName(pathname), StatStructBuf::Stat64(buf), || real!(stat64)(pathname, buf)) + } +} + +// int __xstat64(int ver, const char *pathname, struct stat64 *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __xstat64(ver : c_int, pathname: *const libc::c_char, buf: *mut libc::stat64) -> c_int => __xstat64_hook { + stat_impl("__xstat64", PathMode::PathName(pathname), StatStructBuf::Stat64(buf), || real!(__xstat64)(ver, pathname, buf)) + } +} + +// int lstat(const char *pathname, struct stat *buf); +hook! { + unsafe fn lstat(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => lstat_hook { + stat_impl("lstat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || real!(lstat)(pathname, buf)) + } +} + +// int __lxstat(int ver, const char *pathname, struct stat *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __lxstat(ver : c_int, pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => __lxstat_hook { + stat_impl("__lxstat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || real!(__lxstat)(ver, pathname, buf)) + } +} + +// int lstat64(const char *pathname, struct stat64 *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn lstat64(pathname: *const libc::c_char, buf: *mut libc::stat64) -> c_int => lstat64_hook { + stat_impl("lstat64", PathMode::PathName(pathname), StatStructBuf::Stat64(buf), || real!(lstat64)(pathname, buf)) + } +} + +// int __lxstat64(int ver, const char *pathname, struct stat64 *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __lxstat64(ver : c_int, pathname: *const libc::c_char, buf: *mut libc::stat64) -> c_int => __lxstat64_hook { + stat_impl("__lxstat64", PathMode::PathName(pathname), StatStructBuf::Stat64(buf), || real!(__lxstat64)(ver, pathname, buf)) + } +} + +// int fstat(int fd, struct stat *buf); +hook! { + unsafe fn fstat(fd: c_int, buf: *mut libc::stat) -> c_int => fstat_hook { + stat_impl("fstat", PathMode::Fd(fd), StatStructBuf::Stat(buf), || real!(fstat)(fd, buf)) + } +} + +// int __fxstat(int ver, int fd, struct stat *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __fxstat(ver : c_int, fd: c_int, buf: *mut libc::stat) -> c_int => __fxstat_hook { + stat_impl("__fxstat", PathMode::Fd(fd), StatStructBuf::Stat(buf), || real!(__fxstat)(ver, fd, buf)) + } +} + +// int fstat64(int fd, struct stat64 *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn fstat64(fd: c_int, buf: *mut libc::stat64) -> c_int => fstat_hook { + stat_impl("fstat64", PathMode::Fd(fd), StatStructBuf::Stat64(buf), || real!(fstat64)(fd, buf)) + } +} + +// int __fxstat64(int ver, int fd, struct stat64 *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __fxstat64(ver : c_int, fd: c_int, buf: *mut libc::stat64) -> c_int => __fxstat_hook { + stat_impl("__fxstat64", PathMode::Fd(fd), StatStructBuf::Stat64(buf), || real!(__fxstat64)(ver, fd, buf)) + } +} + +// int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags); +hook! { + unsafe fn fstatat(dirfd: libc::c_int, pathname: *const libc::c_char, buf: *mut libc::stat, flags: libc::c_int) -> libc::c_int => fstatat_hook { + stat_impl("fstatat", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::Stat(buf), + || real!(fstatat)(dirfd, pathname, buf, flags)) + } +} + +// int __fxstatat(int ver, int dirfd, const char *pathname, struct stat *buf, int flags); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __fxstatat(ver : c_int, dirfd: libc::c_int, pathname: *const libc::c_char, buf: *mut libc::stat, flags: libc::c_int) -> libc::c_int => __fxstatat_hook { + stat_impl("__fxstatat", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::Stat(buf), + || real!(__fxstatat)(ver, dirfd, pathname, buf, flags)) + } +} + +// int fstatat64(int dirfd, const char *pathname, struct stat64 *buf, int flags); +#[cfg(target_os = "linux")] +hook! { + unsafe fn fstatat64(dirfd: libc::c_int, pathname: *const libc::c_char, buf: *mut libc::stat64, flags: libc::c_int) -> libc::c_int => fstatat64_hook { + stat_impl("fstatat64", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::Stat64(buf), + || real!(fstatat64)(dirfd, pathname, buf, flags)) + } +} + +// int __fxstatat64(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags); +#[cfg(target_os = "linux")] +hook! { + unsafe fn __fxstatat64(ver : c_int, dirfd: libc::c_int, pathname: *const libc::c_char, buf: *mut libc::stat64, flags: libc::c_int) -> libc::c_int => __fxstatat64_hook { + stat_impl("__fxstatat64", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::Stat64(buf), + || real!(__fxstatat64)(ver, dirfd, pathname, buf, flags)) + } +} + +// int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *buf); +#[cfg(target_os = "linux")] +hook! { + unsafe fn statx(dirfd: libc::c_int, pathname: *const libc::c_char, flags: libc::c_int, mask: libc::c_uint, statxbuf: *mut libc::statx) -> libc::c_int => statx_hook { + stat_impl("statx", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::Stat64(buf), + || real!(statx)(dirfd, pathname, flags, mask, statxbuf)) + } +} + +pub fn is_regular_file(pathname: *const libc::c_char) -> bool { + #[cfg(target_os = "macos")] + unsafe { + let mut buf: libc::stat = std::mem::zeroed(); + let buf_ptr = &mut buf as *mut libc::stat; + + let ret = real!(stat)(pathname, buf_ptr); + ret == 0 && (*buf_ptr).st_mode & libc::S_IFMT == libc::S_IFREG + } + + #[cfg(target_os = "linux")] + unsafe { + use libc::STATX_BASIC_STATS; + // Create a statx buffer to hold the results + let mut statx_buf: statx = std::mem::zeroed(); + + // Call statx with AT_FDCWD (current working directory) and 0 flags + let ret = real!(statx)(AT_FDCWD, pathname, 0, STATX_BASIC_STATS, &mut statx_buf); + ret == 0 && (statx_buf.stx_mode & S_IFMT) == S_IFREG + } +} + +pub fn is_regular_fd(fd: libc::c_int) -> bool { + #[cfg(target_os = "macos")] + unsafe { + let mut buf: libc::stat = std::mem::zeroed(); + let buf_ptr = &mut buf as *mut libc::stat; + + let ret = real!(fstat)(fd, buf_ptr); + (ret == 0) && (*buf_ptr).st_mode & libc::S_IFMT == libc::S_IFREG + } + + // Create a statx buffer to hold the results + #[cfg(target_os = "linux")] + unsafe { + use crate::path_utils::path_of_fd; + use libc::STATX_BASIC_STATS; + + // Convert the file descriptor to a path representation: "/proc/self/fd/" + let Some(path) = path_of_fd(fd) else { + return false; + }; + + let mut statx_buf: statx = std::mem::zeroed(); + + // Call statx with AT_FDCWD (current working directory) and 0 flags + let ret = real!(statx)( + AT_FDCWD, + path.as_ptr() as *const c_char, + 0, + STATX_BASIC_STATS, + &mut statx_buf, + ); + ret == 0 && (statx_buf.stx_mode & S_IFMT) == S_IFREG + } +} From 6067bc488445740f2bd028c0d0fe45d48960edd9 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Fri, 19 Jul 2024 12:28:36 -0700 Subject: [PATCH 2/8] Fixes for linux. --- xetldfs/src/interposing_linux.rs | 15 ++++----------- xetldfs/src/reporting.rs | 4 ++-- xetldfs/src/stat_interposing.rs | 25 ++++++++++++++----------- xetldfs/src/xet_rfile.rs | 12 ++++++++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/xetldfs/src/interposing_linux.rs b/xetldfs/src/interposing_linux.rs index 171d45e4..9e512762 100644 --- a/xetldfs/src/interposing_linux.rs +++ b/xetldfs/src/interposing_linux.rs @@ -33,7 +33,7 @@ macro_rules! hook { unsafe { ONCE.call_once(|| { REAL = $crate::interposing_linux::dlsym_next(concat!(stringify!($real_fn), "\0")); - if REAL == null() { + if REAL == (0 as *const u8) { panic!("XetLDFS: Attempting to call hook to non-existant function {}.", stringify!($real_fn)); } }); @@ -59,16 +59,9 @@ macro_rules! hook { #[macro_export] macro_rules! real { - ($real_fn:ident) => { - let fn_ptr = $real_fn.get(); - if fn_ptr == null { - panic!( - "XetLDFS: AAttempting to call non-existant function {}.", - stringify!($real_fn) - ); - } - fn_ptr - }; + ($real_fn:ident) => {{ + $real_fn.get() + }}; } #[macro_export] diff --git a/xetldfs/src/reporting.rs b/xetldfs/src/reporting.rs index fb0fd56e..12e8bf66 100644 --- a/xetldfs/src/reporting.rs +++ b/xetldfs/src/reporting.rs @@ -1,5 +1,5 @@ -pub const ENABLE_CALL_TRACING: bool = true; -pub const ENABLE_CALL_TRACING_FULL: bool = true; +pub const ENABLE_CALL_TRACING: bool = false; +pub const ENABLE_CALL_TRACING_FULL: bool = false; #[macro_export] macro_rules! ld_trace { diff --git a/xetldfs/src/stat_interposing.rs b/xetldfs/src/stat_interposing.rs index a99a8d91..d447719a 100644 --- a/xetldfs/src/stat_interposing.rs +++ b/xetldfs/src/stat_interposing.rs @@ -18,7 +18,7 @@ enum StatStructBuf { Stat(*mut libc::stat), #[cfg(target_os = "linux")] - Stat64(*mut libc::stat), + Stat64(*mut libc::stat64), #[cfg(target_os = "linux")] StatX(*mut libc::statx), @@ -166,7 +166,7 @@ hook! { // int fstat64(int fd, struct stat64 *buf); #[cfg(target_os = "linux")] hook! { - unsafe fn fstat64(fd: c_int, buf: *mut libc::stat64) -> c_int => fstat_hook { + unsafe fn fstat64(fd: c_int, buf: *mut libc::stat64) -> c_int => fstat64_hook { stat_impl("fstat64", PathMode::Fd(fd), StatStructBuf::Stat64(buf), || real!(fstat64)(fd, buf)) } } @@ -174,7 +174,7 @@ hook! { // int __fxstat64(int ver, int fd, struct stat64 *buf); #[cfg(target_os = "linux")] hook! { - unsafe fn __fxstat64(ver : c_int, fd: c_int, buf: *mut libc::stat64) -> c_int => __fxstat_hook { + unsafe fn __fxstat64(ver : c_int, fd: c_int, buf: *mut libc::stat64) -> c_int => __fxst64_hook { stat_impl("__fxstat64", PathMode::Fd(fd), StatStructBuf::Stat64(buf), || real!(__fxstat64)(ver, fd, buf)) } } @@ -218,7 +218,7 @@ hook! { #[cfg(target_os = "linux")] hook! { unsafe fn statx(dirfd: libc::c_int, pathname: *const libc::c_char, flags: libc::c_int, mask: libc::c_uint, statxbuf: *mut libc::statx) -> libc::c_int => statx_hook { - stat_impl("statx", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::Stat64(buf), + stat_impl("statx", PathMode::PathFromFd(dirfd, pathname), StatStructBuf::StatX(statxbuf), || real!(statx)(dirfd, pathname, flags, mask, statxbuf)) } } @@ -230,18 +230,20 @@ pub fn is_regular_file(pathname: *const libc::c_char) -> bool { let buf_ptr = &mut buf as *mut libc::stat; let ret = real!(stat)(pathname, buf_ptr); - ret == 0 && (*buf_ptr).st_mode & libc::S_IFMT == libc::S_IFREG + ret == 0 && ((*buf_ptr).st_mode & libc::S_IFMT) == libc::S_IFREG } #[cfg(target_os = "linux")] unsafe { use libc::STATX_BASIC_STATS; // Create a statx buffer to hold the results - let mut statx_buf: statx = std::mem::zeroed(); + let mut statx_buf: libc::statx = std::mem::zeroed(); + + let statx_buf_ptr = &mut statx_buf as *mut libc::statx; // Call statx with AT_FDCWD (current working directory) and 0 flags - let ret = real!(statx)(AT_FDCWD, pathname, 0, STATX_BASIC_STATS, &mut statx_buf); - ret == 0 && (statx_buf.stx_mode & S_IFMT) == S_IFREG + let ret = real!(statx)(AT_FDCWD, pathname, 0, STATX_BASIC_STATS, statx_buf_ptr); + ret == 0 && (statx_buf.stx_mode & (libc::S_IFMT as u16)) == libc::S_IFREG as u16 } } @@ -266,7 +268,8 @@ pub fn is_regular_fd(fd: libc::c_int) -> bool { return false; }; - let mut statx_buf: statx = std::mem::zeroed(); + let mut statx_buf: libc::statx = std::mem::zeroed(); + let statx_buf_ptr = &mut statx_buf as *mut libc::statx; // Call statx with AT_FDCWD (current working directory) and 0 flags let ret = real!(statx)( @@ -274,8 +277,8 @@ pub fn is_regular_fd(fd: libc::c_int) -> bool { path.as_ptr() as *const c_char, 0, STATX_BASIC_STATS, - &mut statx_buf, + statx_buf_ptr ); - ret == 0 && (statx_buf.stx_mode & S_IFMT) == S_IFREG + ret == 0 && (statx_buf.stx_mode & (libc::S_IFMT as u16)) == libc::S_IFREG as u16 } } diff --git a/xetldfs/src/xet_rfile.rs b/xetldfs/src/xet_rfile.rs index daa922a2..ff9d9729 100644 --- a/xetldfs/src/xet_rfile.rs +++ b/xetldfs/src/xet_rfile.rs @@ -155,6 +155,18 @@ impl XetFdReadHandle { } } + #[cfg(target_os = "linux")] + pub fn update_stat64(self: &Arc, buf: *mut libc::stat64) { + unsafe { + (*buf).st_size = self.filesize() as i64; /* file size, in bytes */ + (*buf).st_blocks = 0; // todo!() /* blocks allocated for file */ + (*buf).st_blksize = libxet::merkledb::constants::IDEAL_CAS_BLOCK_SIZE + .try_into() + .unwrap() + /* optimal blocksize for I/O */ + } + } + pub fn lseek(self: &Arc, offset: libc::off_t, whence: libc::c_int) -> libc::off_t { let s = self.clone(); From 49c936ec9b63089bd883e418bd052616ae4def62 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Fri, 19 Jul 2024 12:59:56 -0700 Subject: [PATCH 3/8] Fixed clippy warnings. --- xetldfs/src/interposing_linux.rs | 16 +++++++++------- xetldfs/src/interposing_osx.rs | 1 + xetldfs/src/stat_interposing.rs | 6 ++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/xetldfs/src/interposing_linux.rs b/xetldfs/src/interposing_linux.rs index 9e512762..90c4878a 100644 --- a/xetldfs/src/interposing_linux.rs +++ b/xetldfs/src/interposing_linux.rs @@ -1,7 +1,6 @@ // From redhook, https://github.com/geofft/redhook, modified to not crash on non-existent interposed. - use libc::{c_char, c_void}; - +#[allow(clippy::missing_safety_doc)] #[link(name = "dl")] extern "C" { fn dlsym(handle: *const c_void, symbol: *const c_char) -> *const c_void; @@ -10,6 +9,7 @@ extern "C" { const RTLD_NEXT: *const c_void = -1isize as *const c_void; // NOTE: May be the null pointer. +#[allow(clippy::missing_safety_doc)] pub unsafe fn dlsym_next(symbol: &'static str) -> *const u8 { let ptr = dlsym(RTLD_NEXT, symbol.as_ptr() as *const c_char); ptr as *const u8 @@ -24,7 +24,7 @@ macro_rules! hook { static $real_fn: $real_fn = $real_fn {__private_field: ()}; impl $real_fn { - fn get(&self) -> unsafe extern fn ( $($v : $t),* ) -> $r { + unsafe fn get(&self) -> unsafe extern fn ( $($v : $t),* ) -> $r { use ::std::sync::Once; static mut REAL: *const u8 = 0 as *const u8; @@ -33,7 +33,7 @@ macro_rules! hook { unsafe { ONCE.call_once(|| { REAL = $crate::interposing_linux::dlsym_next(concat!(stringify!($real_fn), "\0")); - if REAL == (0 as *const u8) { + if REAL.is_null() { panic!("XetLDFS: Attempting to call hook to non-existant function {}.", stringify!($real_fn)); } }); @@ -42,11 +42,13 @@ macro_rules! hook { } #[no_mangle] + #[allow(clippy::missing_safety_doc)] pub unsafe extern fn $real_fn ( $($v : $t),* ) -> $r { ::std::panic::catch_unwind(|| $hook_fn ( $($v),* )).unwrap_or_else(|_| $real_fn.get() ( $($v),* )) } } + #[allow(clippy::missing_safety_doc)] pub unsafe fn $hook_fn ( $($v : $t),* ) -> $r { $body } @@ -59,14 +61,14 @@ macro_rules! hook { #[macro_export] macro_rules! real { - ($real_fn:ident) => {{ + ($real_fn:ident) => { $real_fn.get() - }}; + }; } #[macro_export] macro_rules! fn_is_valid { ($real_fn:ident) => { - $real_fn.get() != null() + $real_fn.get() != std::ptr::null() }; } diff --git a/xetldfs/src/interposing_osx.rs b/xetldfs/src/interposing_osx.rs index 298f334c..d75259d8 100644 --- a/xetldfs/src/interposing_osx.rs +++ b/xetldfs/src/interposing_osx.rs @@ -21,6 +21,7 @@ macro_rules! hook { pub fn $real_fn ( $($v : $t),* ) -> $r; } + #[allow(clippy::missing_safety_doc)] pub unsafe extern fn $hook_fn ( $($v : $t),* ) -> $r { ::std::panic::catch_unwind(|| $body ).unwrap_or_else(|_| $real_fn ( $($v),* )) } diff --git a/xetldfs/src/stat_interposing.rs b/xetldfs/src/stat_interposing.rs index d447719a..507e2f31 100644 --- a/xetldfs/src/stat_interposing.rs +++ b/xetldfs/src/stat_interposing.rs @@ -223,6 +223,7 @@ hook! { } } +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn is_regular_file(pathname: *const libc::c_char) -> bool { #[cfg(target_os = "macos")] unsafe { @@ -238,7 +239,7 @@ pub fn is_regular_file(pathname: *const libc::c_char) -> bool { use libc::STATX_BASIC_STATS; // Create a statx buffer to hold the results let mut statx_buf: libc::statx = std::mem::zeroed(); - + let statx_buf_ptr = &mut statx_buf as *mut libc::statx; // Call statx with AT_FDCWD (current working directory) and 0 flags @@ -247,6 +248,7 @@ pub fn is_regular_file(pathname: *const libc::c_char) -> bool { } } +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn is_regular_fd(fd: libc::c_int) -> bool { #[cfg(target_os = "macos")] unsafe { @@ -277,7 +279,7 @@ pub fn is_regular_fd(fd: libc::c_int) -> bool { path.as_ptr() as *const c_char, 0, STATX_BASIC_STATS, - statx_buf_ptr + statx_buf_ptr, ); ret == 0 && (statx_buf.stx_mode & (libc::S_IFMT as u16)) == libc::S_IFREG as u16 } From de5acf1c8a8a16f41c942f5cba8b36404022be6c Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Fri, 19 Jul 2024 13:04:04 -0700 Subject: [PATCH 4/8] Test for other runners. --- .github/workflows/commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/commit.yml b/.github/workflows/commit.yml index 9766b379..ffac7e8e 100644 --- a/.github/workflows/commit.yml +++ b/.github/workflows/commit.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, macos-latest, windows-2019] + os: [ubuntu-22.04, macos-latest, windows-2019, ubuntu-20.04, ubuntu-24.04] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 2dad3f4c2240b51fa63890434a7b349ef8815a67 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Mon, 22 Jul 2024 06:16:11 -0700 Subject: [PATCH 5/8] Updates. --- xetldfs/src/interposing_linux.rs | 25 ++++++++----- xetldfs/src/stat_interposing.rs | 60 ++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/xetldfs/src/interposing_linux.rs b/xetldfs/src/interposing_linux.rs index 90c4878a..eaba0baa 100644 --- a/xetldfs/src/interposing_linux.rs +++ b/xetldfs/src/interposing_linux.rs @@ -24,7 +24,7 @@ macro_rules! hook { static $real_fn: $real_fn = $real_fn {__private_field: ()}; impl $real_fn { - unsafe fn get(&self) -> unsafe extern fn ( $($v : $t),* ) -> $r { + unsafe fn get_raw(&self) -> *const u8 { use ::std::sync::Once; static mut REAL: *const u8 = 0 as *const u8; @@ -33,14 +33,16 @@ macro_rules! hook { unsafe { ONCE.call_once(|| { REAL = $crate::interposing_linux::dlsym_next(concat!(stringify!($real_fn), "\0")); - if REAL.is_null() { - panic!("XetLDFS: Attempting to call hook to non-existant function {}.", stringify!($real_fn)); - } }); - ::std::mem::transmute(REAL) + REAL } } + #[inline] + unsafe fn get(&self) -> unsafe extern fn ( $($v : $t),* ) -> $r { + ::std::mem::transmute(self.get_raw()) + } + #[no_mangle] #[allow(clippy::missing_safety_doc)] pub unsafe extern fn $real_fn ( $($v : $t),* ) -> $r { @@ -61,14 +63,21 @@ macro_rules! hook { #[macro_export] macro_rules! real { - ($real_fn:ident) => { + ($real_fn:ident) => {{ + if $real_fn.get_raw().is_null() { + panic!( + "XetLDFS: Attempting to call hook to non-existant function {}.", + stringify!($real_fn) + ); + } + $real_fn.get() - }; + }}; } #[macro_export] macro_rules! fn_is_valid { ($real_fn:ident) => { - $real_fn.get() != std::ptr::null() + !$real_fn.get_raw().is_null() }; } diff --git a/xetldfs/src/stat_interposing.rs b/xetldfs/src/stat_interposing.rs index 507e2f31..87fe0e92 100644 --- a/xetldfs/src/stat_interposing.rs +++ b/xetldfs/src/stat_interposing.rs @@ -89,7 +89,24 @@ unsafe fn stat_impl( // int stat(const char *pathname, struct stat *buf); hook! { unsafe fn stat(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => stat_hook { - stat_impl("stat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || real!(stat)(pathname, buf)) + stat_impl("stat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || { + #[cfg(target_os = "macos")] + { + real!(stat)(pathname, buf) + } + #[cfg(target_os = "linux")] + { + if fn_is_valid!(stat) { + real!(stat)(pathname, buf) + } else if fn_is_valid!(__xstat) { + real!(__xstat)(3, pathname, buf) + } else { + ld_error!("Unknown function stat"); + errno::set_errno(errno::Errno(ENOSYS)); // Function does not exist + return -1; + } + } + }) } } @@ -120,7 +137,25 @@ hook! { // int lstat(const char *pathname, struct stat *buf); hook! { unsafe fn lstat(pathname: *const libc::c_char, buf: *mut libc::stat) -> c_int => lstat_hook { - stat_impl("lstat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), || real!(lstat)(pathname, buf)) + stat_impl("lstat", PathMode::PathName(pathname), StatStructBuf::Stat(buf), + || { + #[cfg(target_os = "macos")] + { + real!(lstat)(pathname, buf) + } + #[cfg(target_os = "linux")] + { + if fn_is_valid!(lstat) { + real!(lstat)(pathname, buf) + } else if fn_is_valid!(__lxstat){ + real!(__lxstat)(3, pathname, buf) + } else { + ld_error!("Unknown function lstat"); + errno::set_errno(errno::Errno(ENOSYS)); // Function does not exist + return -1; + } + } + }) } } @@ -151,7 +186,26 @@ hook! { // int fstat(int fd, struct stat *buf); hook! { unsafe fn fstat(fd: c_int, buf: *mut libc::stat) -> c_int => fstat_hook { - stat_impl("fstat", PathMode::Fd(fd), StatStructBuf::Stat(buf), || real!(fstat)(fd, buf)) + stat_impl("fstat", PathMode::Fd(fd), StatStructBuf::Stat(buf), + || { + + #[cfg(target_os = "macos")] + { + real!(fstat)(fd, buf) + } + #[cfg(target_os = "linux")] + { + if fn_is_valid!(fstat) { + real!(fstat)(fd, buf) + } else if fn_is_valid!(__fxstat){ + real!(__fxstat)(3, fd, buf) + } else { + ld_error!("Unknown function fstat"); + errno::set_errno(errno::Errno(ENOSYS)); // Function does not exist + return -1; + } + } + }) } } From 01bbf85c79133c8c795936b27e6467901d273432 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Mon, 22 Jul 2024 12:59:26 -0700 Subject: [PATCH 6/8] Update. --- rust/gitxetcore/src/git_integration/git_xet_repo.rs | 10 ++++++++-- xetldfs/src/xet_repo_wrapper.rs | 7 +------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rust/gitxetcore/src/git_integration/git_xet_repo.rs b/rust/gitxetcore/src/git_integration/git_xet_repo.rs index 4a4f132e..adb07b2c 100644 --- a/rust/gitxetcore/src/git_integration/git_xet_repo.rs +++ b/rust/gitxetcore/src/git_integration/git_xet_repo.rs @@ -126,13 +126,19 @@ impl GitXetRepo { .map(|(_name, url)| url) .collect()) } + pub fn open(config: XetConfig) -> Result { + let repo_path = config.repo_path()?.clone(); + Ok(Self::open_at(config, repo_path)?) + } /// Open the repository, assuming that the current directory is itself in the repository. /// /// If we are running in a way that is not associated with a repo, then the XetConfig path /// will not show we are in a repo. - pub fn open(config: XetConfig) -> Result { - let repo = open_libgit2_repo(config.repo_path()?)?; + pub fn open_at(mut config: XetConfig, repo_path: PathBuf) -> Result { + let repo = open_libgit2_repo(&repo_path)?; + config.repo_path_if_present = Some(repo_path); + let config = config; let git_dir = repo.path().to_path_buf(); let repo_dir = repo_dir_from_repo(&repo); diff --git a/xetldfs/src/xet_repo_wrapper.rs b/xetldfs/src/xet_repo_wrapper.rs index 17a795c4..7fb936d0 100644 --- a/xetldfs/src/xet_repo_wrapper.rs +++ b/xetldfs/src/xet_repo_wrapper.rs @@ -107,12 +107,7 @@ impl XetFSRepoWrapper { tokio_run_error_checked( &format!("Initializing repository at {:?}", &self.repo_path), async move { - let cfg = base_cfg.switch_repo_path( - libxet::config::ConfigGitPathOption::PathDiscover(self.repo_path.clone()), - None, - )?; - - let xet_repo = GitXetRepo::open_and_verify_setup(cfg).await?; + let xet_repo = GitXetRepo::open_at(base_cfg.clone(), self.repo_path.clone())?; let pft = Arc::new( PointerFileTranslatorV2::from_config_smudge_only(&xet_repo.xet_config).await?, From 2640914bebba4356df6903b1f59a42e8d3df2f23 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Mon, 22 Jul 2024 13:09:30 -0700 Subject: [PATCH 7/8] Try again. --- rust/gitxetcore/src/git_integration/git_xet_repo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/gitxetcore/src/git_integration/git_xet_repo.rs b/rust/gitxetcore/src/git_integration/git_xet_repo.rs index adb07b2c..6569193c 100644 --- a/rust/gitxetcore/src/git_integration/git_xet_repo.rs +++ b/rust/gitxetcore/src/git_integration/git_xet_repo.rs @@ -128,7 +128,7 @@ impl GitXetRepo { } pub fn open(config: XetConfig) -> Result { let repo_path = config.repo_path()?.clone(); - Ok(Self::open_at(config, repo_path)?) + Self::open_at(config, repo_path) } /// Open the repository, assuming that the current directory is itself in the repository. From 554b3dc137d7f1826348a3fd51ccbb80321a1bd1 Mon Sep 17 00:00:00 2001 From: Hoyt Koepke Date: Tue, 23 Jul 2024 10:45:14 -0700 Subject: [PATCH 8/8] Updates to accomodate. --- rust/file_utils/src/safe_file_creator.rs | 10 ----- xetldfs/src/lib.rs | 5 +-- xetldfs/src/runtime.rs | 56 ++++++++++++++++++++---- xetldfs/src/stat_interposing.rs | 6 +-- 4 files changed, 51 insertions(+), 26 deletions(-) diff --git a/rust/file_utils/src/safe_file_creator.rs b/rust/file_utils/src/safe_file_creator.rs index 75cf2b2c..97b468dc 100644 --- a/rust/file_utils/src/safe_file_creator.rs +++ b/rust/file_utils/src/safe_file_creator.rs @@ -70,16 +70,6 @@ impl SafeFileCreator { if let Some(metadata) = self.original_metadata.as_ref() { set_file_metadata(&self.dest_path, metadata, false)?; } - let original_permissions = if self.dest_path.exists() { - Some(fs::metadata(&self.dest_path)?.permissions()) - } else { - None - }; - - // Set the original file's permissions to the new file if they exist - if let Some(permissions) = original_permissions { - fs::set_permissions(&self.dest_path, permissions.clone())?; - } Ok(()) } diff --git a/xetldfs/src/lib.rs b/xetldfs/src/lib.rs index 8ecc80d2..b023f8e6 100644 --- a/xetldfs/src/lib.rs +++ b/xetldfs/src/lib.rs @@ -28,8 +28,7 @@ mod xet_rfile; use crate::path_utils::resolve_path_from_fd; use crate::runtime::{ - activate_fd_runtime, interposing_disabled, process_in_interposable_state, - with_interposing_disabled, + activate_fd_runtime, in_interposable_state, interposing_disabled, with_interposing_disabled, }; #[allow(unused)] @@ -452,7 +451,7 @@ hook! { return real!(mmap)(addr, length, prot, flags, fd, offset); } - if process_in_interposable_state() { + if in_interposable_state() { if xet::materialize_file_under_fd(fd) { ld_trace!("mmap: Materialized pointer file under descriptor {fd}."); } else { diff --git a/xetldfs/src/runtime.rs b/xetldfs/src/runtime.rs index ca9f23e0..606b0983 100644 --- a/xetldfs/src/runtime.rs +++ b/xetldfs/src/runtime.rs @@ -12,7 +12,14 @@ use tokio::runtime::{Builder, Runtime}; use crate::ld_trace; thread_local! { - static INTERPOSING_DISABLE_REQUESTS : AtomicU32 = const { AtomicU32::new(0) }; + // External interposing is a bit less strict, as interactions between interposed + // functions (e.g. stat) sometimes need to be interposed to be correct. + static EXTERNAL_INTERPOSING_GUARDS : AtomicU32 = const { AtomicU32::new(0) }; + + // For internal processes, we need the interposing to be complete and strict; + // this covers all requests within xet-core. + // Here, no functions (stat functions included) should be interposed. + static ABSOLUTE_INTERPOSING_DISABLE_GUARDS: AtomicU32 = const { AtomicU32::new(0) }; } // Guaranteed to be zero on library load for all the static initializers. @@ -28,7 +35,7 @@ lazy_static! { let rt = Builder::new_multi_thread() .worker_threads(1) .on_thread_start(|| { - INTERPOSING_DISABLE_REQUESTS.with(|init| { + ABSOLUTE_INTERPOSING_DISABLE_GUARDS.with(|init| { init.store(1, Ordering::Relaxed); }); }) @@ -62,7 +69,18 @@ pub fn tokio_run(future: F) -> F::Output { } // Step 3: Run all the tokio stuff, which may include spawning other processes. - let result = tokio::task::block_in_place(|| TOKIO_RUNTIME.handle().block_on(future)); + let result = tokio::task::block_in_place(|| { + // This is needed as the task::block_in_place may either run this on the current thread or + // spin up a new thread to prevent tokio threads from blocking, + // so this ensures that interposing is disabled within this. + let _lg = absolute_interposing_disable(); + + TOKIO_RUNTIME.handle().block_on(async move { + // May not be necessary here but doesn't hurt. + let _lg = absolute_interposing_disable(); + future.await + }) + }); // Step 4: if unsafe { libc::sigaction(SIGCHLD, &old_action, std::ptr::null_mut()) } != 0 { @@ -94,11 +112,15 @@ where } #[inline] -pub fn process_in_interposable_state() -> bool { +pub fn in_interposable_state() -> bool { let pid = unsafe { libc::getpid() as u64 }; let s_pid = *FD_RUNTIME_PID; if pid == s_pid { - true + let ok = ABSOLUTE_INTERPOSING_DISABLE_GUARDS.with(|init| init.load(Ordering::Relaxed) == 0); + if !ok { + ld_trace!("XetLDFS: Disabling interposing from internal interposing state."); + } + ok } else { ld_trace!("XetLDFS: process not in interposable state: {pid} != {s_pid}"); false @@ -116,13 +138,13 @@ pub fn raw_runtime_activated() -> bool { #[inline] pub fn runtime_activated() -> bool { - raw_runtime_activated() && process_in_interposable_state() + raw_runtime_activated() && in_interposable_state() } #[inline] pub fn interposing_disabled() -> bool { if runtime_activated() { - INTERPOSING_DISABLE_REQUESTS.with(|init| init.load(Ordering::Relaxed) != 0) + EXTERNAL_INTERPOSING_GUARDS.with(|init| init.load(Ordering::Relaxed) != 0) } else { true } @@ -132,16 +154,32 @@ pub struct InterposingDisable {} impl Drop for InterposingDisable { fn drop(&mut self) { - let v = INTERPOSING_DISABLE_REQUESTS.with(|v| v.fetch_sub(1, Ordering::Relaxed)); + let v = EXTERNAL_INTERPOSING_GUARDS.with(|v| v.fetch_sub(1, Ordering::Relaxed)); assert_ne!(v, 0); } } pub fn with_interposing_disabled() -> InterposingDisable { - INTERPOSING_DISABLE_REQUESTS.with(|v| v.fetch_add(1, Ordering::Relaxed)); + EXTERNAL_INTERPOSING_GUARDS.with(|v| v.fetch_add(1, Ordering::Relaxed)); InterposingDisable {} } +//////////////////////// +// Internal +pub struct AbsoluteInterposingDisable {} + +impl Drop for AbsoluteInterposingDisable { + fn drop(&mut self) { + let v = ABSOLUTE_INTERPOSING_DISABLE_GUARDS.with(|v| v.fetch_sub(1, Ordering::Relaxed)); + assert_ne!(v, 0); + } +} + +pub fn absolute_interposing_disable() -> AbsoluteInterposingDisable { + ABSOLUTE_INTERPOSING_DISABLE_GUARDS.with(|v| v.fetch_add(1, Ordering::Relaxed)); + AbsoluteInterposingDisable {} +} + ///////////// // Some things for the Xet Runtime diff --git a/xetldfs/src/stat_interposing.rs b/xetldfs/src/stat_interposing.rs index 87fe0e92..eb7eecef 100644 --- a/xetldfs/src/stat_interposing.rs +++ b/xetldfs/src/stat_interposing.rs @@ -1,6 +1,4 @@ -use crate::runtime::{ - interposing_disabled, process_in_interposable_state, with_interposing_disabled, -}; +use crate::runtime::{in_interposable_state, interposing_disabled, with_interposing_disabled}; use crate::{hook, my_open, my_openat, real}; use libc::*; @@ -35,7 +33,7 @@ unsafe fn stat_impl( let r = base(); - if !process_in_interposable_state() || r < 0 { + if !in_interposable_state() || r < 0 { return r; }