Skip to content

Commit 2acc324

Browse files
committed
fix(fspy): correctly report access mode from node:fs on Windows
1 parent d4fa5ec commit 2acc324

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ dist
44
.claude/settings.local.json
55
*.tsbuildinfo
66
.DS_Store
7+
/.vscode/settings.json

crates/fspy/tests/node_fs.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
mod test_utils;
22

3-
use std::env::{current_dir, vars_os};
3+
use std::{
4+
env::{current_dir, vars_os},
5+
ffi::OsStr,
6+
};
47

58
use fspy::{AccessMode, PathAccessIterable};
69
use test_utils::assert_contains;
710

8-
async fn track_node_script(script: &str) -> anyhow::Result<PathAccessIterable> {
11+
async fn track_node_script(script: &str, args: &[&OsStr]) -> anyhow::Result<PathAccessIterable> {
912
let mut command = fspy::Command::new("node");
1013
command
1114
.arg("-e")
1215
.envs(vars_os()) // https://github.com/jdx/mise/discussions/5968
13-
.arg(script);
16+
.arg(script)
17+
.args(args);
1418
let child = command.spawn().await?;
1519
let termination = child.wait_handle.await?;
1620
assert!(termination.status.success());
@@ -19,14 +23,65 @@ async fn track_node_script(script: &str) -> anyhow::Result<PathAccessIterable> {
1923

2024
#[tokio::test]
2125
async fn read_sync() -> anyhow::Result<()> {
22-
let accesses = track_node_script("try { fs.readFileSync('hello') } catch {}").await?;
26+
let accesses = track_node_script("try { fs.readFileSync('hello') } catch {}", &[]).await?;
2327
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
2428
Ok(())
2529
}
2630

31+
#[tokio::test]
32+
async fn exist_sync() -> anyhow::Result<()> {
33+
let accesses = track_node_script("try { fs.existsSync('hello') } catch {}", &[]).await?;
34+
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
35+
Ok(())
36+
}
37+
38+
#[tokio::test]
39+
async fn stat_sync() -> anyhow::Result<()> {
40+
let accesses = track_node_script("try { fs.statSync('hello') } catch {}", &[]).await?;
41+
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
42+
Ok(())
43+
}
44+
45+
#[tokio::test]
46+
async fn create_read_stream() -> anyhow::Result<()> {
47+
let accesses = track_node_script(
48+
"try { fs.createReadStream('hello').on('error', () => {}) } catch {}",
49+
&[],
50+
)
51+
.await?;
52+
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
53+
Ok(())
54+
}
55+
56+
#[tokio::test]
57+
async fn create_write_stream() -> anyhow::Result<()> {
58+
let tmpdir = tempfile::tempdir()?;
59+
let file_path = tmpdir.path().join("hello");
60+
let accesses = track_node_script(
61+
"try { fs.createWriteStream(process.argv[1]).on('error', () => {}) } catch {}",
62+
&[file_path.as_os_str()],
63+
)
64+
.await?;
65+
assert_contains(&accesses, file_path.as_path(), AccessMode::Write);
66+
Ok(())
67+
}
68+
69+
#[tokio::test]
70+
async fn write_sync() -> anyhow::Result<()> {
71+
let tmpdir = tempfile::tempdir()?;
72+
let file_path = tmpdir.path().join("hello");
73+
let accesses = track_node_script(
74+
"try { fs.writeFileSync(process.argv[1], '') } catch {}",
75+
&[file_path.as_os_str()],
76+
)
77+
.await?;
78+
assert_contains(&accesses, &file_path, AccessMode::Write);
79+
Ok(())
80+
}
81+
2782
#[tokio::test]
2883
async fn read_dir_sync() -> anyhow::Result<()> {
29-
let accesses = track_node_script("try { fs.readdirSync('.') } catch {}").await?;
84+
let accesses = track_node_script("try { fs.readdirSync('.') } catch {}", &[]).await?;
3085
assert_contains(&accesses, &current_dir().unwrap(), AccessMode::ReadDir);
3186
Ok(())
3287
}
@@ -38,9 +93,10 @@ async fn subprocess() -> anyhow::Result<()> {
3893
} else {
3994
r"'/bin/sh', ['-c', 'cat hello']"
4095
};
41-
let accesses = track_node_script(&format!(
42-
"try {{ child_process.spawnSync({cmd}, {{ stdio: 'ignore' }}) }} catch {{}}"
43-
))
96+
let accesses = track_node_script(
97+
&format!("try {{ child_process.spawnSync({cmd}, {{ stdio: 'ignore' }}) }} catch {{}}"),
98+
&[],
99+
)
44100
.await?;
45101
assert_contains(&accesses, current_dir().unwrap().join("hello").as_path(), AccessMode::Read);
46102
Ok(())

crates/fspy_preload_windows/src/windows/winapi_utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use winapi::{
1212
},
1313
um::{
1414
fileapi::GetFinalPathNameByHandleW,
15-
winnt::{ACCESS_MASK, GENERIC_READ, GENERIC_WRITE},
15+
winnt::{ACCESS_MASK, FILE_APPEND_DATA, FILE_READ_DATA, FILE_WRITE_DATA},
1616
},
1717
};
1818
use winsafe::{GetLastError, co};
@@ -72,8 +72,8 @@ pub unsafe fn get_path_name(handle: HANDLE) -> winsafe::SysResult<SmallVec<u16,
7272
}
7373

7474
pub fn access_mask_to_mode(desired_access: ACCESS_MASK) -> AccessMode {
75-
let has_write = (desired_access & GENERIC_WRITE) != 0;
76-
let has_read = (desired_access & GENERIC_READ) != 0;
75+
let has_write = (desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
76+
let has_read = (desired_access & FILE_READ_DATA) != 0;
7777
if has_write {
7878
if has_read { AccessMode::ReadWrite } else { AccessMode::Write }
7979
} else {

0 commit comments

Comments
 (0)