Skip to content
Closed
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
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,8 @@ codegen-units = 1
strip = "symbols" # set to `false` for debug information
debug = false # set to `true` for debug information
panic = "abort" # Let it crash and force ourselves to write safe Rust.

# Patch shared_memory to use nix 0.30+ so it compiles under
# target_env = "ohos". Upstream PR: https://github.com/elast0ny/shared_memory/pull/118
[patch.crates-io]
shared_memory = { git = "https://github.com/social4hyq/shared_memory", branch = "nix-030-ohos" }
7 changes: 5 additions & 2 deletions crates/fspy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ tokio = { workspace = true, features = ["net", "process", "io-util", "sync", "rt
tokio-util = { workspace = true }
which = { workspace = true, features = ["tracing"] }

[target.'cfg(target_os = "linux")'.dependencies]
[target.'cfg(all(target_os = "linux", not(target_env = "ohos")))'.dependencies]
fspy_seccomp_unotify = { workspace = true, features = ["supervisor"] }
nix = { workspace = true, features = ["uio"] }
tokio = { workspace = true, features = ["bytes"] }

[target.'cfg(unix)'.dependencies]
[target.'cfg(all(unix, not(target_env = "ohos")))'.dependencies]
fspy_shared_unix = { workspace = true }
nix = { workspace = true, features = ["fs", "process", "socket", "feature"] }

[target.'cfg(target_env = "ohos")'.dependencies]
nix = { workspace = true, features = ["fs", "process", "socket", "feature"] }

[target.'cfg(target_os = "windows")'.dependencies]
fspy_detours_sys = { workspace = true }
winapi = { workspace = true, features = ["winbase", "securitybaseapi", "handleapi"] }
Expand Down
6 changes: 3 additions & 3 deletions crates/fspy/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
process::Stdio,
};

#[cfg(unix)]
#[cfg(all(unix, not(target_env = "ohos")))]
use fspy_shared_unix::exec::Exec;
use rustc_hash::FxHashMap;
use tokio::process::Command as TokioCommand;
Expand Down Expand Up @@ -50,7 +50,7 @@ impl Command {
}
}

#[cfg(unix)]
#[cfg(all(unix, not(target_env = "ohos")))]
#[must_use]
pub(crate) fn get_exec(&self) -> Exec {
use std::{
Expand All @@ -74,7 +74,7 @@ impl Command {
}
}

#[cfg(unix)]
#[cfg(all(unix, not(target_env = "ohos")))]
pub(crate) fn set_exec(&mut self, mut exec: Exec) {
use std::os::unix::ffi::OsStringExt;

Expand Down
8 changes: 6 additions & 2 deletions crates/fspy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ pub mod error;
#[cfg(not(target_env = "musl"))]
mod ipc;

#[cfg(unix)]
#[cfg(all(unix, not(target_env = "ohos")))]
#[path = "./unix/mod.rs"]
mod os_impl;

#[cfg(all(unix, target_env = "ohos"))]
#[path = "./unix/mod_ohos.rs"]
mod os_impl;

#[cfg(target_os = "windows")]
#[path = "./windows/mod.rs"]
mod os_impl;

#[cfg(unix)]
#[cfg(all(unix, not(target_env = "ohos")))]
mod arena;
mod command;

Expand Down
79 changes: 79 additions & 0 deletions crates/fspy/src/unix/mod_ohos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Stub fspy backend for OpenHarmony — fspy is not supported on OHOS.
// All types exist but operations will no-op or return empty results.

use std::{
io,
path::Path,
process::ExitStatus,
};

use tokio_util::sync::CancellationToken;

use crate::{TrackedChild, command::Command, error::SpawnError};
pub use access_iter::PathAccessIterable;

mod access_iter {
use fspy_shared::ipc::PathAccess;
use std::path::Path;

/// Empty access iterable for OHOS (no fspy tracking available).
pub struct PathAccessIterable;

impl PathAccessIterable {
pub fn iter(&self) -> impl Iterator<Item = PathAccess<'_>> {
Vec::new().into_iter()
}
}

impl<'a> IntoIterator for &'a PathAccessIterable {
type IntoIter = std::vec::IntoIter<Result<(&'a Path, PathAccess<'a>), (&'a Path, std::io::Error)>>;
type Item = Result<(&'a Path, PathAccess<'a>), (&'a Path, std::io::Error)>;

fn into_iter(self) -> Self::IntoIter {
Vec::new().into_iter()
}
}
}

#[derive(Debug)]
pub struct SpyImpl;

impl SpyImpl {
pub fn init_in(_dir: &Path) -> io::Result<Self> {
Ok(SpyImpl)
}

pub fn new(_resolved_program: &Path) -> io::Result<Self> {
Ok(SpyImpl)
}

pub(crate) async fn spawn(
&self,
command: Command,
_cancellation_token: CancellationToken,
) -> Result<TrackedChild, SpawnError> {
let mut tokio_cmd = command.into_tokio_command();
let mut child = tokio_cmd.spawn().map_err(|e| SpawnError::OsSpawn(e))?;

let stdin = child.stdin.take();
let stdout = child.stdout.take();
let stderr = child.stderr.take();

let wait_handle = async move {
let status = child.wait().await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
Ok(crate::ChildTermination {
status,
path_accesses: PathAccessIterable,
})
};

Ok(TrackedChild {
stdin,
stdout,
stderr,
wait_handle: Box::pin(wait_handle),
#[cfg(windows)]
process_handle: std::os::windows::io::OwnedHandle::from(std::fs::File::open("/dev/null")?),
})
}
}
2 changes: 2 additions & 0 deletions crates/fspy_preload_unix/src/interceptions/spawn/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,15 @@ mod linux_only {
let _unused = execvpe::original;
handle_exec(ExecResolveConfig::search_path_enabled(None), file, argv, envp)
}
#[cfg(not(target_env = "ohos"))]
intercept!(execveat(64): unsafe extern "C" fn(
dirfd: c_int,
prog: *const libc::c_char,
argv: *const *mut libc::c_char,
envp: *const *mut libc::c_char,
flags: c_int
) -> libc::c_int);
#[cfg(not(target_env = "ohos"))]
unsafe extern "C" fn execveat(
dirfd: c_int,
pathname: *const libc::c_char,
Expand Down
4 changes: 2 additions & 2 deletions crates/fspy_preload_unix/src/macros/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ macro_rules! intercept {
::core::assert!($crate::macros::symbol_exists(::core::stringify!($name)));
}
}
#[cfg(not(test))] // Don't interpose on the test binary
#[cfg(all(not(test), target_arch = "aarch64"))] // Don't interpose on the test binary
const _: () = {
#[unsafe(naked)]
#[unsafe(export_name = ::core::concat!(::core::stringify!($name), 64))]
Expand Down Expand Up @@ -51,7 +51,7 @@ macro_rules! intercept_inner {
const _: $fn_sig = $name;
const _: $fn_sig = $crate::libc::$name;

#[cfg(not(test))] // Don't interpose on the test binary
#[cfg(all(not(test), target_arch = "aarch64"))] // Don't interpose on the test binary
const _: () = {
#[unsafe(naked)]
#[unsafe(export_name = ::core::stringify!($name))]
Expand Down