diff --git a/Cargo.lock b/Cargo.lock index d99662ea..08b5c7a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,7 +219,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -256,9 +256,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -573,7 +573,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm_winapi", "mio", "parking_lot", @@ -589,7 +589,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm_winapi", "derive_more", "document-features", @@ -1110,6 +1110,7 @@ dependencies = [ "allocator-api2", "assert2", "bincode", + "bitflags 2.10.0", "bstr", "bytemuck", "ctor", @@ -1591,7 +1592,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", "redox_syscall", ] @@ -1771,7 +1772,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -1783,7 +1784,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -1918,7 +1919,7 @@ version = "0.10.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -2316,7 +2317,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cassowary", "compact_str 0.8.1", "crossterm 0.28.1", @@ -2357,7 +2358,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -2426,7 +2427,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2470,7 +2471,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -2483,7 +2484,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", @@ -2540,7 +2541,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation", "core-foundation-sys", "libc", diff --git a/Cargo.toml b/Cargo.toml index 72847020..0c610c9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ attohttpc = { version = "0.30.1", features = ["tls-native", "tls-native-vendored base64 = "0.22.1" bincode = "2.0.1" bindgen = "0.72.1" +bitflags = "2.10.0" brush-parser = "0.2.18" bstr = { version = "1.12.0", default-features = false, features = ["alloc", "std"] } bumpalo = { version = "3.17.0", features = ["allocator-api2"] } diff --git a/crates/fspy/examples/cli.rs b/crates/fspy/examples/cli.rs index 54b2da0a..b31b9f94 100644 --- a/crates/fspy/examples/cli.rs +++ b/crates/fspy/examples/cli.rs @@ -1,6 +1,5 @@ use std::{env::args_os, ffi::OsStr, path::PathBuf, pin::Pin}; -use fspy::AccessMode; use tokio::{ fs::File, io::{AsyncWrite, stdout}, @@ -33,12 +32,7 @@ async fn main() -> anyhow::Result<()> { csv_writer .write_record(&[ acc.path.to_cow_os_str().to_string_lossy().as_ref().as_bytes(), - match acc.mode { - AccessMode::Read => b"read".as_slice(), - AccessMode::ReadWrite => b"readwrite", - AccessMode::Write => b"write", - AccessMode::ReadDir => b"readdir", - }, + format!("{:?}", acc.mode).as_bytes(), ]) .await?; } diff --git a/crates/fspy/src/unix/syscall_handler/mod.rs b/crates/fspy/src/unix/syscall_handler/mod.rs index d06580be..265e9855 100644 --- a/crates/fspy/src/unix/syscall_handler/mod.rs +++ b/crates/fspy/src/unix/syscall_handler/mod.rs @@ -60,9 +60,9 @@ impl SyscallHandler { } self.arena.add(PathAccess { mode: match flags & libc::O_ACCMODE { - libc::O_RDWR => AccessMode::ReadWrite, - libc::O_WRONLY => AccessMode::Write, - _ => AccessMode::Read, + libc::O_RDWR => AccessMode::READ | AccessMode::WRITE, + libc::O_WRONLY => AccessMode::WRITE, + _ => AccessMode::READ, }, path: NativeStr::from_bytes(path.as_os_str().as_bytes()), }); @@ -72,7 +72,7 @@ impl SyscallHandler { fn handle_open_dir(&mut self, caller: Caller, fd: Fd) -> io::Result<()> { let path = fd.get_path(caller)?; self.arena.add(PathAccess { - mode: AccessMode::ReadDir, + mode: AccessMode::READ_DIR, path: NativeStr::from_bytes(path.as_bytes()), }); Ok(()) diff --git a/crates/fspy/tests/node_fs.rs b/crates/fspy/tests/node_fs.rs index 54f6b786..bbbb2bb5 100644 --- a/crates/fspy/tests/node_fs.rs +++ b/crates/fspy/tests/node_fs.rs @@ -25,21 +25,21 @@ async fn track_node_script(script: &str, args: &[&OsStr]) -> anyhow::Result anyhow::Result<()> { let accesses = track_node_script("try { fs.readFileSync('hello') } catch {}", &[]).await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } #[test(tokio::test)] async fn exist_sync() -> anyhow::Result<()> { let accesses = track_node_script("try { fs.existsSync('hello') } catch {}", &[]).await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } #[test(tokio::test)] async fn stat_sync() -> anyhow::Result<()> { let accesses = track_node_script("try { fs.statSync('hello') } catch {}", &[]).await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } @@ -50,7 +50,7 @@ async fn create_read_stream() -> anyhow::Result<()> { &[], ) .await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } @@ -63,7 +63,7 @@ async fn create_write_stream() -> anyhow::Result<()> { &[file_path.as_os_str()], ) .await?; - assert_contains(&accesses, file_path.as_path(), AccessMode::Write); + assert_contains(&accesses, file_path.as_path(), AccessMode::WRITE); Ok(()) } @@ -76,14 +76,14 @@ async fn write_sync() -> anyhow::Result<()> { &[file_path.as_os_str()], ) .await?; - assert_contains(&accesses, &file_path, AccessMode::Write); + assert_contains(&accesses, &file_path, AccessMode::WRITE); Ok(()) } #[test(tokio::test)] async fn read_dir_sync() -> anyhow::Result<()> { let accesses = track_node_script("try { fs.readdirSync('.') } catch {}", &[]).await?; - assert_contains(&accesses, ¤t_dir().unwrap(), AccessMode::ReadDir); + assert_contains(&accesses, ¤t_dir().unwrap(), AccessMode::READ_DIR); Ok(()) } @@ -99,6 +99,6 @@ async fn subprocess() -> anyhow::Result<()> { &[], ) .await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } diff --git a/crates/fspy/tests/rust_std.rs b/crates/fspy/tests/rust_std.rs index 7db2d7d2..ea4a7f16 100644 --- a/crates/fspy/tests/rust_std.rs +++ b/crates/fspy/tests/rust_std.rs @@ -16,7 +16,7 @@ async fn open_read() -> anyhow::Result<()> { let _ = File::open("hello"); }) .await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } @@ -30,7 +30,7 @@ async fn open_write() -> anyhow::Result<()> { let _ = OpenOptions::new().write(true).open(tmp_path_str); }) .await?; - assert_contains(&accesses, tmp_path.as_path(), AccessMode::Write); + assert_contains(&accesses, tmp_path.as_path(), AccessMode::WRITE); Ok(()) } @@ -41,7 +41,7 @@ async fn readdir() -> anyhow::Result<()> { let _ = std::fs::read_dir("hello_dir"); }) .await?; - assert_contains(&accesses, current_dir()?.join("hello_dir").as_path(), AccessMode::ReadDir); + assert_contains(&accesses, current_dir()?.join("hello_dir").as_path(), AccessMode::READ_DIR); Ok(()) } @@ -68,7 +68,7 @@ async fn subprocess() -> anyhow::Result<()> { .unwrap(); }) .await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } diff --git a/crates/fspy/tests/rust_tokio.rs b/crates/fspy/tests/rust_tokio.rs index 437d6473..e847ba1a 100644 --- a/crates/fspy/tests/rust_tokio.rs +++ b/crates/fspy/tests/rust_tokio.rs @@ -17,7 +17,7 @@ async fn open_read() -> anyhow::Result<()> { ); }) .await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } @@ -35,7 +35,7 @@ async fn open_write() -> anyhow::Result<()> { ); }) .await?; - assert_contains(&accesses, tmp_path.as_path(), AccessMode::Write); + assert_contains(&accesses, tmp_path.as_path(), AccessMode::WRITE); Ok(()) } @@ -50,7 +50,7 @@ async fn readdir() -> anyhow::Result<()> { ); }) .await?; - assert_contains(&accesses, current_dir()?.join("hello_dir").as_path(), AccessMode::ReadDir); + assert_contains(&accesses, current_dir()?.join("hello_dir").as_path(), AccessMode::READ_DIR); Ok(()) } @@ -82,7 +82,7 @@ async fn subprocess() -> anyhow::Result<()> { ); }) .await?; - assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read); + assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::READ); Ok(()) } diff --git a/crates/fspy/tests/static_executable.rs b/crates/fspy/tests/static_executable.rs index 8fe3319b..57059c6d 100644 --- a/crates/fspy/tests/static_executable.rs +++ b/crates/fspy/tests/static_executable.rs @@ -53,59 +53,67 @@ async fn track_test_bin(args: &[&str], cwd: Option<&str>) -> PathAccessIterable #[test(tokio::test)] async fn open_read() { let accesses = track_test_bin(&["open_read", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::Read); + assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::READ); } #[test(tokio::test)] async fn open_write() { let accesses = track_test_bin(&["open_write", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::Write); + assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::WRITE); } #[test(tokio::test)] async fn open_readwrite() { let accesses = track_test_bin(&["open_readwrite", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::ReadWrite); + assert_contains( + &accesses, + Path::new("/hello"), + fspy::AccessMode::READ | fspy::AccessMode::WRITE, + ); } #[test(tokio::test)] async fn openat2_read() { let accesses = track_test_bin(&["openat2_read", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::Read); + assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::READ); } #[test(tokio::test)] async fn openat2_write() { let accesses = track_test_bin(&["openat2_write", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::Write); + assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::WRITE); } #[test(tokio::test)] async fn openat2_readwrite() { let accesses = track_test_bin(&["openat2_readwrite", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::ReadWrite); + assert_contains( + &accesses, + Path::new("/hello"), + fspy::AccessMode::READ | fspy::AccessMode::WRITE, + ); } #[test(tokio::test)] async fn open_relative() { let accesses = track_test_bin(&["open_read", "hello"], Some("/home")).await; - assert_contains(&accesses, Path::new("/home/hello"), fspy::AccessMode::Read); + assert_contains(&accesses, Path::new("/home/hello"), fspy::AccessMode::READ); } #[test(tokio::test)] async fn readdir() { let accesses = track_test_bin(&["readdir", "/home"], None).await; - assert_contains(&accesses, Path::new("/home"), fspy::AccessMode::ReadDir); + assert_contains(&accesses, Path::new("/home"), fspy::AccessMode::READ_DIR); } #[test(tokio::test)] async fn stat() { let accesses = track_test_bin(&["stat", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::Read); + assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::READ); } #[test(tokio::test)] async fn execve() { let accesses = track_test_bin(&["execve", "/hello"], None).await; - assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::Read); + assert_contains(&accesses, Path::new("/hello"), fspy::AccessMode::READ); } diff --git a/crates/fspy_e2e/src/main.rs b/crates/fspy_e2e/src/main.rs index b7e49442..7091858b 100644 --- a/crates/fspy_e2e/src/main.rs +++ b/crates/fspy_e2e/src/main.rs @@ -47,15 +47,7 @@ impl AccessCollector { } Entry::Occupied(mut occupied) => { let occupied_mode = occupied.get_mut(); - match (*occupied_mode, access.mode) { - (_, AccessMode::ReadWrite) => { - *occupied_mode = AccessMode::ReadWrite; - } - (AccessMode::Read, AccessMode::ReadDir) => { - *occupied_mode = AccessMode::ReadDir; - } - _ => {} - } + occupied_mode.insert(access.mode); } } } diff --git a/crates/fspy_preload_unix/src/client/convert.rs b/crates/fspy_preload_unix/src/client/convert.rs index 9a4fc39a..e20df867 100644 --- a/crates/fspy_preload_unix/src/client/convert.rs +++ b/crates/fspy_preload_unix/src/client/convert.rs @@ -103,9 +103,9 @@ pub struct OpenFlags(pub c_int); impl ToAccessMode for OpenFlags { unsafe fn to_access_mode(self) -> AccessMode { match self.0 & libc::O_ACCMODE { - libc::O_RDWR => AccessMode::ReadWrite, - libc::O_WRONLY => AccessMode::Write, - _ => AccessMode::Read, + libc::O_RDWR => AccessMode::READ | AccessMode::WRITE, + libc::O_WRONLY => AccessMode::WRITE, + _ => AccessMode::READ, } } } @@ -117,9 +117,9 @@ impl ToAccessMode for ModeStr { let has_read = mode_str.contains(&b'r'); let has_write = mode_str.contains(&b'w') || mode_str.contains(&b'a'); match (has_read, has_write) { - (false, true) => AccessMode::Write, - (true, true) => AccessMode::ReadWrite, - _ => AccessMode::Read, + (false, true) => AccessMode::WRITE, + (true, true) => AccessMode::READ | AccessMode::WRITE, + _ => AccessMode::READ, } } } diff --git a/crates/fspy_preload_unix/src/interceptions/access.rs b/crates/fspy_preload_unix/src/interceptions/access.rs index b581010b..945d7970 100644 --- a/crates/fspy_preload_unix/src/interceptions/access.rs +++ b/crates/fspy_preload_unix/src/interceptions/access.rs @@ -9,7 +9,7 @@ use crate::{ intercept!(access(64): unsafe extern "C" fn(pathname: *const c_char, mode: c_int) -> c_int); unsafe extern "C" fn access(pathname: *const c_char, mode: c_int) -> c_int { unsafe { - handle_open(pathname, AccessMode::Read); + handle_open(pathname, AccessMode::READ); } unsafe { access::original()(pathname, mode) } } @@ -22,7 +22,7 @@ unsafe extern "C" fn faccessat( flags: c_int, ) -> c_int { unsafe { - handle_open(PathAt(dirfd, pathname), AccessMode::Read); + handle_open(PathAt(dirfd, pathname), AccessMode::READ); } unsafe { faccessat::original()(dirfd, pathname, mode, flags) } } diff --git a/crates/fspy_preload_unix/src/interceptions/dirent.rs b/crates/fspy_preload_unix/src/interceptions/dirent.rs index d093fc44..c3a3584c 100644 --- a/crates/fspy_preload_unix/src/interceptions/dirent.rs +++ b/crates/fspy_preload_unix/src/interceptions/dirent.rs @@ -18,7 +18,7 @@ unsafe extern "C" fn scandir( select: *const c_void, compar: *const c_void, ) -> c_int { - unsafe { handle_open(dirname, AccessMode::ReadDir) } + unsafe { handle_open(dirname, AccessMode::READ_DIR) } unsafe { scandir::original()(dirname, namelist, select, compar) } } @@ -37,7 +37,7 @@ mod macos_only { select: *const c_void, compar: *const c_void, ) -> c_int { - unsafe { handle_open(dirname, AccessMode::ReadDir) }; + unsafe { handle_open(dirname, AccessMode::READ_DIR) }; unsafe { scandir_b::original()(dirname, namelist, select, compar) } } } @@ -49,18 +49,18 @@ unsafe extern "C" fn getdirentries( nbytes: c_int, basep: *mut c_long, ) -> c_int { - unsafe { handle_open(Fd(fd), AccessMode::ReadDir) }; + unsafe { handle_open(Fd(fd), AccessMode::READ_DIR) }; unsafe { getdirentries::original()(fd, buf, nbytes, basep) } } intercept!(fdopendir(64): unsafe extern "C" fn (fd: c_int) -> *mut DIR); unsafe extern "C" fn fdopendir(fd: c_int) -> *mut DIR { - unsafe { handle_open(Fd(fd), AccessMode::ReadDir) }; + unsafe { handle_open(Fd(fd), AccessMode::READ_DIR) }; unsafe { fdopendir::original()(fd) } } intercept!(opendir(64): unsafe extern "C" fn (*const c_char) -> *mut DIR); unsafe extern "C" fn opendir(dir_name: *const c_char) -> *mut DIR { - unsafe { handle_open(dir_name, AccessMode::ReadDir) }; + unsafe { handle_open(dir_name, AccessMode::READ_DIR) }; unsafe { opendir::original()(dir_name) } } diff --git a/crates/fspy_preload_unix/src/interceptions/linux_syscall.rs b/crates/fspy_preload_unix/src/interceptions/linux_syscall.rs index 1948d7ec..8ac348e3 100644 --- a/crates/fspy_preload_unix/src/interceptions/linux_syscall.rs +++ b/crates/fspy_preload_unix/src/interceptions/linux_syscall.rs @@ -22,7 +22,7 @@ unsafe extern "C" fn syscall(syscall_no: c_long, mut args: ...) -> c_long { let dirfd = a0 as c_int; let pathname = a1 as *const c_char; unsafe { - handle_open(PathAt(dirfd, pathname), AccessMode::Read); + handle_open(PathAt(dirfd, pathname), AccessMode::READ); } } _ => {} diff --git a/crates/fspy_preload_unix/src/interceptions/stat.rs b/crates/fspy_preload_unix/src/interceptions/stat.rs index a46a06f0..5782f123 100644 --- a/crates/fspy_preload_unix/src/interceptions/stat.rs +++ b/crates/fspy_preload_unix/src/interceptions/stat.rs @@ -12,7 +12,7 @@ use crate::{ intercept!(stat(64): unsafe extern "C" fn(path: *const c_char, buf: *mut stat_struct) -> c_int); unsafe extern "C" fn stat(path: *const c_char, buf: *mut stat_struct) -> c_int { unsafe { - handle_open(path, AccessMode::Read); + handle_open(path, AccessMode::READ); } unsafe { stat::original()(path, buf) } } @@ -21,7 +21,7 @@ intercept!(lstat(64): unsafe extern "C" fn(path: *const c_char, buf: *mut stat_s unsafe extern "C" fn lstat(path: *const c_char, buf: *mut stat_struct) -> c_int { // TODO: add accessmode ReadNoFollow unsafe { - handle_open(path, AccessMode::Read); + handle_open(path, AccessMode::READ); } unsafe { lstat::original()(path, buf) } } @@ -29,7 +29,7 @@ unsafe extern "C" fn lstat(path: *const c_char, buf: *mut stat_struct) -> c_int intercept!(fstat(64): unsafe extern "C" fn(fd: c_int, buf: *mut stat_struct) -> c_int); unsafe extern "C" fn fstat(fd: c_int, buf: *mut stat_struct) -> c_int { unsafe { - handle_open(Fd(fd), AccessMode::Read); + handle_open(Fd(fd), AccessMode::READ); } unsafe { fstat::original()(fd, buf) } } @@ -42,7 +42,7 @@ unsafe extern "C" fn fstatat( flags: c_int, ) -> c_int { unsafe { - handle_open(PathAt(dirfd, pathname), AccessMode::Read); + handle_open(PathAt(dirfd, pathname), AccessMode::READ); } unsafe { fstatat::original()(dirfd, pathname, buf, flags) } } diff --git a/crates/fspy_preload_windows/src/windows/detours/create_process.rs b/crates/fspy_preload_windows/src/windows/detours/create_process.rs index eceb543c..445d38b5 100644 --- a/crates/fspy_preload_windows/src/windows/detours/create_process.rs +++ b/crates/fspy_preload_windows/src/windows/detours/create_process.rs @@ -73,7 +73,7 @@ static DETOUR_CREATE_PROCESS_W: Detour< if !lp_application_name.is_null() { unsafe { sender.send(PathAccess { - mode: AccessMode::Read, + mode: AccessMode::READ, path: NativeStr::from_wide( U16CStr::from_ptr_str(lp_application_name).as_slice(), ), @@ -197,7 +197,7 @@ static DETOUR_CREATE_PROCESS_A: Detour< if !lp_application_name.is_null() { unsafe { sender.send(PathAccess { - mode: AccessMode::Read, + mode: AccessMode::READ, path: NativeStr::from_ansi(CStr::from_ptr(lp_application_name).to_bytes()), }); } diff --git a/crates/fspy_preload_windows/src/windows/detours/nt.rs b/crates/fspy_preload_windows/src/windows/detours/nt.rs index 09a74ddd..344837f6 100644 --- a/crates/fspy_preload_windows/src/windows/detours/nt.rs +++ b/crates/fspy_preload_windows/src/windows/detours/nt.rs @@ -121,7 +121,7 @@ static DETOUR_NT_QUERY_ATTRIBUTES_FILE: Detour< object_attributes: POBJECT_ATTRIBUTES, file_information: PFILE_BASIC_INFORMATION, ) -> HFILE { - unsafe { handle_open(AccessMode::Read, object_attributes) }; + unsafe { handle_open(AccessMode::READ, object_attributes) }; unsafe { (DETOUR_NT_QUERY_ATTRIBUTES_FILE.real())(object_attributes, file_information) } } new_nt_open_file @@ -145,7 +145,7 @@ unsafe fn handle_open(access_mode: impl ToAccessMode, path: impl ToAbsolutePath) .rposition(|c| *c == b'\\' as u16 || *c == b'/' as u16) .unwrap_or(0); PathAccess { - mode: AccessMode::ReadDir, + mode: AccessMode::READ_DIR, path: NativeStr::from_wide(&path[..slash_pos]), } } else { @@ -265,7 +265,7 @@ static DETOUR_NT_QUERY_DIRECTORY_FILE: Detour< file_name: PUNICODE_STRING, restart_scan: BOOLEAN, ) -> NTSTATUS { - unsafe { handle_open(AccessMode::ReadDir, file_handle) }; + unsafe { handle_open(AccessMode::READ_DIR, file_handle) }; unsafe { (DETOUR_NT_QUERY_DIRECTORY_FILE.real())( file_handle, diff --git a/crates/fspy_preload_windows/src/windows/winapi_utils.rs b/crates/fspy_preload_windows/src/windows/winapi_utils.rs index 3991563c..4f01c9a2 100644 --- a/crates/fspy_preload_windows/src/windows/winapi_utils.rs +++ b/crates/fspy_preload_windows/src/windows/winapi_utils.rs @@ -78,9 +78,9 @@ pub fn access_mask_to_mode(desired_access: ACCESS_MASK) -> AccessMode { let has_write = (desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) != 0; let has_read = (desired_access & (FILE_READ_DATA | GENERIC_READ)) != 0; if has_write { - if has_read { AccessMode::ReadWrite } else { AccessMode::Write } + if has_read { AccessMode::READ | AccessMode::WRITE } else { AccessMode::WRITE } } else { - AccessMode::Read + AccessMode::READ } } diff --git a/crates/fspy_shared/Cargo.toml b/crates/fspy_shared/Cargo.toml index c808828c..01cb6907 100644 --- a/crates/fspy_shared/Cargo.toml +++ b/crates/fspy_shared/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] allocator-api2 = { workspace = true } bincode = { workspace = true } +bitflags = { workspace = true } bstr = { workspace = true } bytemuck = { workspace = true, features = ["must_cast"] } shared_memory = { workspace = true, features = ["logging"] } diff --git a/crates/fspy_shared/src/ipc/mod.rs b/crates/fspy_shared/src/ipc/mod.rs index fe575a74..825e0e7d 100644 --- a/crates/fspy_shared/src/ipc/mod.rs +++ b/crates/fspy_shared/src/ipc/mod.rs @@ -1,17 +1,35 @@ pub mod channel; mod native_str; +use std::fmt::Debug; + use bincode::{BorrowDecode, Encode, config::Configuration}; +use bitflags::bitflags; pub use native_str::{NativeStr, NativeString}; pub const BINCODE_CONFIG: Configuration = bincode::config::standard(); -#[derive(Encode, BorrowDecode, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum AccessMode { - Read, - Write, - ReadWrite, - ReadDir, +#[derive(Encode, BorrowDecode, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub struct AccessMode(u8); + +bitflags! { + impl AccessMode: u8 { + const READ = 1; + const WRITE = 1 << 1; + const READ_DIR = 1 << 2; + } +} + +impl Debug for AccessMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + struct InternalAccessMode(AccessMode); + impl Debug for InternalAccessMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bitflags::parser::to_writer(&self.0, f) + } + } + f.debug_tuple("AccessMode").field(&InternalAccessMode(*self)).finish() + } } #[derive(Encode, BorrowDecode, Debug, Clone, Copy)] @@ -23,10 +41,10 @@ pub struct PathAccess<'a> { impl<'a> PathAccess<'a> { pub fn read(path: impl Into>) -> Self { - Self { mode: AccessMode::Read, path: path.into() } + Self { mode: AccessMode::READ, path: path.into() } } pub fn read_dir(path: impl Into>) -> Self { - Self { mode: AccessMode::ReadDir, path: path.into() } + Self { mode: AccessMode::READ_DIR, path: path.into() } } } diff --git a/crates/fspy_shared_unix/src/exec/mod.rs b/crates/fspy_shared_unix/src/exec/mod.rs index 94bde79a..2afba8bf 100644 --- a/crates/fspy_shared_unix/src/exec/mod.rs +++ b/crates/fspy_shared_unix/src/exec/mod.rs @@ -113,7 +113,7 @@ impl Exec { self.program.as_ref(), path, |path| { - on_path_access(PathAccess { path: path.into(), mode: AccessMode::Read }); + on_path_access(PathAccess { path: path.into(), mode: AccessMode::READ }); access(OsStr::from_bytes(path), AccessFlags::X_OK) }, |program| Ok(program.to_owned()), diff --git a/crates/fspy_shared_unix/src/spawn/mod.rs b/crates/fspy_shared_unix/src/spawn/mod.rs index eb80febc..9e2ad1ed 100644 --- a/crates/fspy_shared_unix/src/spawn/mod.rs +++ b/crates/fspy_shared_unix/src/spawn/mod.rs @@ -50,7 +50,7 @@ pub fn handle_exec( }; command.resolve(&mut on_path_access, config)?; - on_path_access(PathAccess { mode: AccessMode::Read, path: command.program.as_bstr().into() }); + on_path_access(PathAccess { mode: AccessMode::READ, path: command.program.as_bstr().into() }); os_specific::handle_exec(command, encoded_payload) } diff --git a/crates/vite_task/src/execute.rs b/crates/vite_task/src/execute.rs index 5aa4c3cd..da950f0f 100644 --- a/crates/vite_task/src/execute.rs +++ b/crates/vite_task/src/execute.rs @@ -497,25 +497,19 @@ pub async fn execute_task( // temp workaround for oxlint reading inside .git continue; } - match access.mode { - AccessMode::Read => { - path_reads.entry(relative_path).or_insert(PathRead { read_dir_entries: false }); - } - AccessMode::Write => { - path_writes.insert(relative_path, PathWrite); - } - AccessMode::ReadWrite => { - path_reads - .entry(relative_path.clone()) - .or_insert(PathRead { read_dir_entries: false }); - path_writes.insert(relative_path, PathWrite); - } - AccessMode::ReadDir => match path_reads.entry(relative_path) { + if access.mode.contains(AccessMode::READ) { + path_reads.entry(relative_path.clone()).or_insert(PathRead { read_dir_entries: false }); + } + if access.mode.contains(AccessMode::WRITE) { + path_writes.insert(relative_path.clone(), PathWrite); + } + if access.mode.contains(AccessMode::READ_DIR) { + match path_reads.entry(relative_path) { Entry::Occupied(mut occupied) => occupied.get_mut().read_dir_entries = true, Entry::Vacant(vacant) => { vacant.insert(PathRead { read_dir_entries: true }); } - }, + } } }