Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions crates/fspy/examples/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ async fn main() -> anyhow::Result<()> {
let child = command.spawn(tokio_util::sync::CancellationToken::new()).await?;
let termination = child.wait_handle.await?;

let mut path_count = 0usize;
let mut access_count = 0usize;
let out_file: Pin<Box<dyn AsyncWrite>> =
if out_path == "-" { Box::pin(stdout()) } else { Box::pin(File::create(out_path).await?) };

Expand All @@ -32,10 +32,9 @@ async fn main() -> anyhow::Result<()> {
.expect("the tracking region holds every record this run makes")
.iter()
{
path_count += 1;
let path_str = format!("{:?}", acc.path);
let mode_str = format!("{:?}", acc.mode);
csv_writer.write_record(&[path_str.as_bytes(), mode_str.as_bytes()]).await?;
access_count += 1;
let access_str = format!("{acc:?}");
csv_writer.write_record(&[access_str.as_bytes()]).await?;
}
csv_writer.flush().await?;

Expand All @@ -44,7 +43,7 @@ async fn main() -> anyhow::Result<()> {
reason = "CLI example: stderr output is intentional for user feedback"
)]
{
eprintln!("\nfspy: {path_count} paths accessed. status: {}", termination.status);
eprintln!("\nfspy: {access_count} accesses. status: {}", termination.status);
}
Ok(())
}
4 changes: 1 addition & 3 deletions crates/fspy/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ impl Default for PathAccessArena {
impl PathAccessArena {
pub fn add(&mut self, access: PathAccess<'_>) {
self.with_mut(|fields| {
let path = access.path.clone_in(fields.bump);
let path_access = PathAccess { mode: access.mode, path };
fields.accesses.push(path_access);
fields.accesses.push(access.clone_in(fields.bump));
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/fspy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::{env::temp_dir, fs::create_dir, io, process::ExitStatus, sync::LazyLock

pub use command::Command;
pub use error::TrackingIncomplete;
pub use fspy_shared::ipc::{AccessMode, PathAccess};
pub use fspy_shared::ipc::{IpcPath, PathAccess};
use futures_util::future::BoxFuture;
pub use os_impl::PathAccessIterable;
use os_impl::SpyImpl;
Expand Down
4 changes: 2 additions & 2 deletions crates/fspy/src/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ impl SpyImpl {
&mut exec,
ExecResolveConfig::search_path_enabled(None),
&encoded_payload,
|mode, path| {
exec_resolve_accesses.add(PathAccess { mode, path: path.into() });
|path| {
exec_resolve_accesses.add(PathAccess::Read(path.into()));
},
)
.map_err(|err| SpawnError::Injection(err.into()))?;
Expand Down
19 changes: 7 additions & 12 deletions crates/fspy/src/unix/syscall_handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use fspy_seccomp_unotify::{
impl_handler,
supervisor::handler::arg::{CStrPtr, Caller, Fd},
};
use fspy_shared::ipc::{AccessMode, PathAccess, channel::Sender};
use fspy_shared::ipc::{PathAccess, channel::Sender};

const PATH_MAX: usize = libc::PATH_MAX as usize;

Expand Down Expand Up @@ -54,13 +54,11 @@ impl SyscallHandler {
}
path = Cow::Owned(resolved_path);
}
self.ipc_sender.send(&PathAccess {
mode: match flags & libc::O_ACCMODE {
libc::O_RDWR => AccessMode::ReadWrite,
libc::O_WRONLY => AccessMode::Write,
_ => AccessMode::Read,
},
path: path.as_os_str().into(),
let path = path.as_os_str().into();
self.ipc_sender.send(&match flags & libc::O_ACCMODE {
libc::O_RDWR => PathAccess::ReadWrite(path),
libc::O_WRONLY => PathAccess::Write(path),
_ => PathAccess::Read(path),
});
Ok(())
}
Expand All @@ -71,10 +69,7 @@ impl SyscallHandler {
)]
fn handle_open_dir(&mut self, caller: Caller, fd: Fd) -> io::Result<()> {
let path = fd.get_path(caller)?;
self.ipc_sender.send(&PathAccess {
mode: AccessMode::ReadDir,
path: OsStr::from_bytes(path.as_bytes()).into(),
});
self.ipc_sender.send(&PathAccess::ReadDir(OsStr::from_bytes(path.as_bytes()).into()));
Ok(())
}
}
Expand Down
18 changes: 9 additions & 9 deletions crates/fspy/tests/node_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
path::PathBuf,
};

use fspy::{AccessMode, PathAccessIterable};
use fspy::{PathAccess, PathAccessIterable};
use ntest::test_case;
use test_utils::assert_contains;

Expand Down Expand Up @@ -64,7 +64,7 @@ fn track_script(
#[cfg_attr(not(target_env = "musl"), ignore = "requires node")]
fn read_sync(runtime: &str) -> anyhow::Result<()> {
let accesses = track_script(runtime, "try { fs.readFileSync('hello') } catch {}", &[])?;
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), PathAccess::Read);
Ok(())
}

Expand All @@ -76,7 +76,7 @@ fn read_sync(runtime: &str) -> anyhow::Result<()> {
#[cfg_attr(not(target_env = "musl"), ignore = "requires node")]
fn exist_sync(runtime: &str) -> anyhow::Result<()> {
let accesses = track_script(runtime, "try { fs.existsSync('hello') } catch {}", &[])?;
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), PathAccess::Read);
Ok(())
}

Expand All @@ -88,7 +88,7 @@ fn exist_sync(runtime: &str) -> anyhow::Result<()> {
#[cfg_attr(not(target_env = "musl"), ignore = "requires node")]
fn stat_sync(runtime: &str) -> anyhow::Result<()> {
let accesses = track_script(runtime, "try { fs.statSync('hello') } catch {}", &[])?;
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), PathAccess::Read);
Ok(())
}

Expand All @@ -104,7 +104,7 @@ fn create_read_stream(runtime: &str) -> anyhow::Result<()> {
"try { fs.createReadStream('hello').on('error', () => {}) } catch {}",
&[],
)?;
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), PathAccess::Read);
Ok(())
}

Expand All @@ -122,7 +122,7 @@ fn create_write_stream(runtime: &str) -> anyhow::Result<()> {
"try { fs.createWriteStream(process.argv.at(-1)).on('error', () => {}) } catch {}",
&[file_path.as_os_str()],
)?;
assert_contains(&accesses, file_path.as_path(), AccessMode::Write);
assert_contains(&accesses, file_path.as_path(), PathAccess::Write);
Ok(())
}

Expand All @@ -140,7 +140,7 @@ fn write_sync(runtime: &str) -> anyhow::Result<()> {
"try { fs.writeFileSync(process.argv.at(-1), '') } catch {}",
&[file_path.as_os_str()],
)?;
assert_contains(&accesses, &file_path, AccessMode::Write);
assert_contains(&accesses, &file_path, PathAccess::Write);
Ok(())
}

Expand All @@ -152,7 +152,7 @@ fn write_sync(runtime: &str) -> anyhow::Result<()> {
#[cfg_attr(not(target_env = "musl"), ignore = "requires node")]
fn read_dir_sync(runtime: &str) -> anyhow::Result<()> {
let accesses = track_script(runtime, "try { fs.readdirSync('.') } catch {}", &[])?;
assert_contains(&accesses, &current_dir().unwrap(), AccessMode::ReadDir);
assert_contains(&accesses, &current_dir().unwrap(), PathAccess::ReadDir);
Ok(())
}

Expand All @@ -173,6 +173,6 @@ fn subprocess(runtime: &str) -> anyhow::Result<()> {
&format!("try {{ child_process.spawnSync({cmd}, {{ stdio: 'ignore' }}) }} catch {{}}"),
&[],
)?;
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), PathAccess::Read);
Ok(())
}
4 changes: 2 additions & 2 deletions crates/fspy/tests/node_sea.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
process::Command,
};

use fspy::AccessMode;
use fspy::PathAccess;
use test_log::test;
use test_utils::assert_contains;

Expand Down Expand Up @@ -87,6 +87,6 @@ async fn node_sea() -> anyhow::Result<()> {
command.spawn(tokio_util::sync::CancellationToken::new()).await?.wait_handle.await?;
assert!(termination.status.success(), "{:?}", termination.status);
let accesses = termination.path_accesses.expect("the test region holds every record");
assert_contains(&accesses, &input, AccessMode::Read);
assert_contains(&accesses, &input, PathAccess::Read);
Ok(())
}
16 changes: 9 additions & 7 deletions crates/fspy/tests/oxlint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod test_utils;

use std::{env::vars_os, ffi::OsString};

use fspy::{AccessMode, PathAccessIterable};
use fspy::{PathAccess, PathAccessIterable};
use test_log::test;

/// Get the packages/tools/.bin directory path
Expand Down Expand Up @@ -63,7 +63,7 @@ async fn oxlint_reads_js_file() -> anyhow::Result<()> {
let accesses = track_oxlint(&tmpdir_path, &[]).await?;

// Check that oxlint read the JS file
test_utils::assert_contains(&accesses, &js_file, AccessMode::Read);
test_utils::assert_contains(&accesses, &js_file, PathAccess::Read);

Ok(())
}
Expand All @@ -80,7 +80,7 @@ async fn oxlint_reads_directory() -> anyhow::Result<()> {

// Check that oxlint read the directory to find JS files
// This is the key check - if READ_DIR is not tracked, cache won't detect new files
test_utils::assert_contains(&accesses, &tmpdir_path, AccessMode::ReadDir);
test_utils::assert_contains(&accesses, &tmpdir_path, PathAccess::ReadDir);
Ok(())
}

Expand All @@ -104,17 +104,19 @@ declare const _foo: Foo;
// Run oxlint without --type-aware first
let accesses = track_oxlint(&tmpdir_path, &[""]).await?;
let access_to_types_ts = accesses.iter().find(|access| {
access
.path
.strip_path_prefix(&tmpdir_path, |result| result.is_ok_and(|p| p.ends_with("types.ts")))
access.paths().any(|path| {
path.strip_path_prefix(&tmpdir_path, |result| {
result.is_ok_and(|p| p.ends_with("types.ts"))
})
})
});
assert_eq!(access_to_types_ts, None, "oxlint should not read types.ts without --type-aware");

// Run oxlint with --type-aware to enable type-aware linting
let accesses = track_oxlint(&tmpdir_path, &["--type-aware"]).await?;

// Check that oxlint read types.ts
test_utils::assert_contains(&accesses, &tmpdir_path.join("types.ts"), AccessMode::Read);
test_utils::assert_contains(&accesses, &tmpdir_path.join("types.ts"), PathAccess::Read);

Ok(())
}
14 changes: 7 additions & 7 deletions crates/fspy/tests/rust_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
process::Stdio,
};

use fspy::AccessMode;
use fspy::PathAccess;
use test_log::test;
use test_utils::assert_contains;

Expand All @@ -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(), PathAccess::Read);

Ok(())
}
Expand All @@ -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(), PathAccess::Write);

Ok(())
}
Expand All @@ -46,7 +46,7 @@ async fn metadata() -> anyhow::Result<()> {
let _ = fs::metadata(tmp_path_str);
})
.await?;
assert_contains(&accesses, tmp_path.as_path(), AccessMode::Read);
assert_contains(&accesses, tmp_path.as_path(), PathAccess::Read);

Ok(())
}
Expand All @@ -72,7 +72,7 @@ async fn readdir() -> anyhow::Result<()> {
let _ = std::fs::read_dir("hello_dir").unwrap().next();
})
.await?;
assert_contains(&accesses, tmpdir_path.join("hello_dir").as_path(), AccessMode::ReadDir);
assert_contains(&accesses, tmpdir_path.join("hello_dir").as_path(), PathAccess::ReadDir);

Ok(())
}
Expand All @@ -99,7 +99,7 @@ async fn read_in_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(), PathAccess::Read);

Ok(())
}
Expand All @@ -113,7 +113,7 @@ async fn read_program() -> anyhow::Result<()> {
assert_contains(
&accesses,
current_dir().unwrap().join("not_exist.exe").as_path(),
AccessMode::Read,
PathAccess::Read,
);

Ok(())
Expand Down
10 changes: 5 additions & 5 deletions crates/fspy/tests/rust_tokio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod test_utils;

use std::{env::current_dir, process::Stdio};

use fspy::AccessMode;
use fspy::PathAccess;
use test_log::test;
use test_utils::assert_contains;
use tokio::fs::OpenOptions;
Expand All @@ -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(), PathAccess::Read);

Ok(())
}
Expand All @@ -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(), PathAccess::Write);

Ok(())
}
Expand All @@ -56,7 +56,7 @@ async fn readdir() -> anyhow::Result<()> {
);
})
.await?;
assert_contains(&accesses, tmpdir_path.join("hello_dir").as_path(), AccessMode::ReadDir);
assert_contains(&accesses, tmpdir_path.join("hello_dir").as_path(), PathAccess::ReadDir);

Ok(())
}
Expand Down Expand Up @@ -88,7 +88,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(), PathAccess::Read);

Ok(())
}
8 changes: 4 additions & 4 deletions crates/fspy/tests/shebang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
process::{Command, Stdio},
};

use fspy::AccessMode;
use fspy::PathAccess;
use test_log::test;
use test_utils::assert_contains;
use tokio::fs;
Expand Down Expand Up @@ -36,8 +36,8 @@ async fn spawn_sh_shebang() -> anyhow::Result<()> {
})
.await?;

assert_contains(&accesses, Path::new(&shebang_script_path), AccessMode::Read);
assert_contains(&accesses, Path::new("/hello"), AccessMode::Read);
assert_contains(&accesses, Path::new(&shebang_script_path), PathAccess::Read);
assert_contains(&accesses, Path::new("/hello"), PathAccess::Read);

Ok(())
}
Expand Down Expand Up @@ -80,7 +80,7 @@ async fn spawn_shebang_with_long_interpreter_path() -> anyhow::Result<()> {
})
.await?;

assert_contains(&accesses, &interpreter, AccessMode::Read);
assert_contains(&accesses, &interpreter, PathAccess::Read);

Ok(())
}
Loading
Loading