use std::path::{Path, PathBuf};
use std::sync::OnceLock;
pub const ENTITLEMENTS_PLIST: &str =
include_str!("../entitlements.plist");
#[derive(Debug, Clone)]
pub struct AssetPaths {
pub kernel: Option<PathBuf>,
pub init_oci_bin: Option<PathBuf>,
pub init_oci_src: Option<PathBuf>,
pub supermachine_agent: Option<PathBuf>,
pub smpark_ko: Option<PathBuf>,
}
impl AssetPaths {
pub fn discover() -> Self {
if let Some(dir) = std::env::var_os("SUPERMACHINE_ASSETS_DIR") {
let dir = PathBuf::from(dir);
return Self::from_dir(&dir);
}
let exe = std::env::current_exe().ok();
if let Some(exe) = exe.as_deref() {
if let Some(macos_dir) = exe.parent() {
if macos_dir.file_name().and_then(|s| s.to_str()) == Some("MacOS") {
if let Some(contents) = macos_dir.parent() {
let res = contents.join("Resources");
let probe = Self::from_dir(&res);
if probe.kernel.is_some() {
return probe;
}
}
}
}
}
if let Some(exe) = exe.as_deref() {
for ancestor in exe.ancestors() {
let share = ancestor.join("share/supermachine");
if share.join("kernel").is_file() {
return Self::from_dir(&share);
}
}
}
if let Some(exe) = exe.as_deref() {
for ancestor in exe.ancestors() {
let kernel_crate = ancestor.join("crates/supermachine-kernel");
let main_crate = ancestor.join("crates/supermachine");
let agent_crate = ancestor.join("crates/supermachine-guest-agent");
if kernel_crate.join("kernel").is_file() {
return Self {
kernel: Some(kernel_crate.join("kernel")),
init_oci_bin: Some(main_crate.join("oci/init-oci"))
.filter(|p| p.is_file()),
init_oci_src: Some(main_crate.join("oci/init-oci.c"))
.filter(|p| p.is_file()),
supermachine_agent: Some(agent_crate.join(
"target/aarch64-unknown-linux-musl/release/supermachine-agent",
))
.filter(|p| p.is_file()),
smpark_ko: Some(
main_crate.join("oci/supermachine-smpark.ko"),
)
.filter(|p| p.is_file()),
};
}
}
}
if let Some(dir) = ensure_bundled_extracted() {
let probe = Self::from_dir(&dir);
if probe.kernel.is_some() {
return probe;
}
}
Self {
kernel: None,
init_oci_bin: None,
init_oci_src: None,
supermachine_agent: None,
smpark_ko: None,
}
}
pub fn from_dir(dir: &Path) -> Self {
Self {
kernel: Some(dir.join("kernel")).filter(|p| p.is_file()),
init_oci_bin: Some(dir.join("init-oci")).filter(|p| p.is_file()),
init_oci_src: Some(dir.join("init-oci.c")).filter(|p| p.is_file()),
supermachine_agent: Some(dir.join("supermachine-agent"))
.filter(|p| p.is_file()),
smpark_ko: Some(dir.join("supermachine-smpark.ko"))
.filter(|p| p.is_file()),
}
}
pub fn from_app_bundle(app: &Path) -> Self {
Self::from_dir(&app.join("Contents/Resources"))
}
}
fn ensure_bundled_extracted() -> Option<PathBuf> {
let dir = bundled_assets_dir()?;
let kernel_dst = dir.join("kernel");
let init_dst = dir.join("init-oci");
let agent_dst = dir.join("supermachine-agent");
let smpark_dst = dir.join("supermachine-smpark.ko");
let kernel_hash_dst = dir.join("kernel.hash");
let init_hash_dst = dir.join("init-oci.hash");
let agent_hash_dst = dir.join("supermachine-agent.hash");
let smpark_hash_dst = dir.join("supermachine-smpark.hash");
let want_kernel = kernel_bytes_hash();
let want_init = init_oci_bytes_hash();
let want_agent = supermachine_agent_bytes_hash();
let want_smpark = smpark_ko_bytes_hash();
if kernel_dst.is_file()
&& init_dst.is_file()
&& agent_dst.is_file()
&& smpark_dst.is_file()
&& read_hash_file(&kernel_hash_dst).as_deref() == Some(want_kernel)
&& read_hash_file(&init_hash_dst).as_deref() == Some(want_init)
&& read_hash_file(&agent_hash_dst).as_deref() == Some(want_agent)
&& read_hash_file(&smpark_hash_dst).as_deref() == Some(want_smpark)
{
return Some(dir);
}
if std::fs::create_dir_all(&dir).is_err() {
return None;
}
if !kernel_dst.is_file()
|| read_hash_file(&kernel_hash_dst).as_deref() != Some(want_kernel)
{
if !atomic_write(&dir, &kernel_dst, "kernel.partial", |tmp| {
std::fs::write(tmp, supermachine_kernel::KERNEL_BYTES)
}) {
return None;
}
if !atomic_write_str(&dir, &kernel_hash_dst, "kernel.hash.partial", want_kernel)
{
return None;
}
}
if !init_dst.is_file()
|| read_hash_file(&init_hash_dst).as_deref() != Some(want_init)
{
if !atomic_write(&dir, &init_dst, "init-oci.partial", |tmp| {
supermachine_kernel::extract_init_oci_to(tmp)
}) {
return None;
}
if !atomic_write_str(&dir, &init_hash_dst, "init-oci.hash.partial", want_init)
{
return None;
}
}
if !agent_dst.is_file()
|| read_hash_file(&agent_hash_dst).as_deref() != Some(want_agent)
{
if !atomic_write(&dir, &agent_dst, "supermachine-agent.partial", |tmp| {
supermachine_kernel::extract_supermachine_agent_to(tmp)
}) {
return None;
}
if !atomic_write_str(
&dir,
&agent_hash_dst,
"supermachine-agent.hash.partial",
want_agent,
) {
return None;
}
}
if !smpark_dst.is_file()
|| read_hash_file(&smpark_hash_dst).as_deref() != Some(want_smpark)
{
if !atomic_write(&dir, &smpark_dst, "supermachine-smpark.ko.partial", |tmp| {
supermachine_kernel::extract_smpark_ko_to(tmp)
}) {
return None;
}
if !atomic_write_str(
&dir,
&smpark_hash_dst,
"supermachine-smpark.hash.partial",
want_smpark,
) {
return None;
}
}
Some(dir)
}
fn atomic_write<F>(dir: &Path, dst: &Path, tmp_name: &str, write: F) -> bool
where
F: FnOnce(&Path) -> std::io::Result<()>,
{
let tmp = dir.join(tmp_name);
if write(&tmp).is_err() {
let _ = std::fs::remove_file(&tmp);
return false;
}
if std::fs::rename(&tmp, dst).is_err() {
let _ = std::fs::remove_file(&tmp);
return false;
}
true
}
fn atomic_write_str(dir: &Path, dst: &Path, tmp_name: &str, contents: &str) -> bool {
atomic_write(dir, dst, tmp_name, |tmp| std::fs::write(tmp, contents))
}
fn read_hash_file(path: &Path) -> Option<String> {
let bytes = std::fs::read(path).ok()?;
if bytes.len() > 128 {
return None;
}
Some(String::from_utf8(bytes).ok()?.trim().to_owned())
}
fn kernel_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::KERNEL_BYTES))
}
fn init_oci_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::INIT_OCI_BYTES))
}
fn supermachine_agent_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::SUPERMACHINE_AGENT_BYTES))
}
fn smpark_ko_bytes_hash() -> &'static str {
static H: OnceLock<String> = OnceLock::new();
H.get_or_init(|| short_sha256(supermachine_kernel::SMPARK_KO_BYTES))
}
fn short_sha256(bytes: &[u8]) -> String {
let digest = ring::digest::digest(&ring::digest::SHA256, bytes);
let mut s = String::with_capacity(12);
for b in &digest.as_ref()[..6] {
s.push_str(&format!("{:02x}", b));
}
s
}
pub fn ensure_worker_in_user_dir(source: &Path) -> Result<PathBuf, String> {
let dir = bundled_assets_dir().ok_or_else(|| {
format!(
"no $XDG_DATA_HOME or $HOME to extract worker into; \
set $SUPERMACHINE_WORKER_BIN to a user-writable path \
holding a v{} worker binary instead",
env!("CARGO_PKG_VERSION")
)
})?;
ensure_worker_in_dir(source, &dir)
}
pub(crate) fn ensure_worker_in_dir(source: &Path, dir: &Path) -> Result<PathBuf, String> {
let dst = dir.join("supermachine-worker");
let hash_dst = dir.join("supermachine-worker.hash");
let source_hash = short_sha256_file(source).map_err(|e| {
format!("hash worker source {}: {e}", source.display())
})?;
if dst.is_file() && read_hash_file(&hash_dst).as_deref() == Some(source_hash.as_str()) {
return Ok(dst);
}
std::fs::create_dir_all(dir).map_err(|e| {
format!("mkdir -p {}: {e}", dir.display())
})?;
let partial = dir.join("supermachine-worker.partial");
let _ = std::fs::remove_file(&partial);
std::fs::copy(source, &partial).map_err(|e| {
format!(
"copy {} -> {}: {e}",
source.display(),
partial.display()
)
})?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&partial)
.map_err(|e| format!("stat partial worker: {e}"))?
.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&partial, perms)
.map_err(|e| format!("chmod partial worker: {e}"))?;
}
std::fs::rename(&partial, &dst).map_err(|e| {
let _ = std::fs::remove_file(&partial);
format!("rename {} -> {}: {e}", partial.display(), dst.display())
})?;
if !atomic_write_str(
dir,
&hash_dst,
"supermachine-worker.hash.partial",
&source_hash,
) {
return Err(format!(
"write {}: failed (disk full? perms?)",
hash_dst.display()
));
}
Ok(dst)
}
fn short_sha256_file(path: &Path) -> Result<String, std::io::Error> {
let bytes = std::fs::read(path)?;
Ok(short_sha256(&bytes))
}
fn bundled_assets_dir() -> Option<PathBuf> {
let base = if let Some(d) = std::env::var_os("XDG_DATA_HOME") {
PathBuf::from(d)
} else if let Some(h) = std::env::var_os("HOME") {
PathBuf::from(h).join(".local/share")
} else {
return None;
};
Some(
base.join("supermachine")
.join(format!("v{}", env!("CARGO_PKG_VERSION"))),
)
}
#[cfg(test)]
mod ensure_worker_in_dir_tests {
use super::{ensure_worker_in_dir, short_sha256_file};
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
static TEST_ID: AtomicU64 = AtomicU64::new(0);
fn unique_tmp_dir(label: &str) -> PathBuf {
let id = TEST_ID.fetch_add(1, Ordering::Relaxed);
let p = std::env::temp_dir().join(format!(
"supermachine-userdir-test-{}-{label}-{id}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&p);
p
}
fn make_worker(path: &std::path::Path, contents: &[u8]) {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("mkdir parent");
}
let mut f = std::fs::File::create(path).expect("create worker source");
f.write_all(contents).expect("write worker source");
}
#[test]
fn first_call_copies_source_and_writes_hash_sentinel() {
let src_dir = unique_tmp_dir("first-src");
let dst_dir = unique_tmp_dir("first-dst");
let src = src_dir.join("worker-source");
let payload = b"#!/bin/sh\necho hi\n";
make_worker(&src, payload);
let dst = ensure_worker_in_dir(&src, &dst_dir).expect("first call");
assert_eq!(dst, dst_dir.join("supermachine-worker"));
assert!(dst.exists(), "dst worker should be present");
assert_eq!(std::fs::read(&dst).unwrap(), payload);
let hash_file = dst_dir.join("supermachine-worker.hash");
assert!(hash_file.exists());
let want = short_sha256_file(&src).unwrap();
let got = std::fs::read_to_string(&hash_file).unwrap();
assert_eq!(got.trim(), want);
let _ = std::fs::remove_dir_all(&src_dir);
let _ = std::fs::remove_dir_all(&dst_dir);
}
#[test]
fn second_call_on_unchanged_source_is_a_cache_hit() {
let src_dir = unique_tmp_dir("hit-src");
let dst_dir = unique_tmp_dir("hit-dst");
let src = src_dir.join("worker-source");
make_worker(&src, b"static contents");
let dst1 = ensure_worker_in_dir(&src, &dst_dir).unwrap();
let mtime1 = std::fs::metadata(&dst1).unwrap().modified().unwrap();
std::thread::sleep(std::time::Duration::from_millis(150));
let dst2 = ensure_worker_in_dir(&src, &dst_dir).unwrap();
assert_eq!(dst1, dst2);
let mtime2 = std::fs::metadata(&dst2).unwrap().modified().unwrap();
assert_eq!(
mtime1, mtime2,
"cache hit must NOT rewrite the file (mtime would change)"
);
let _ = std::fs::remove_dir_all(&src_dir);
let _ = std::fs::remove_dir_all(&dst_dir);
}
#[test]
fn source_change_invalidates_cache_and_recopies() {
let src_dir = unique_tmp_dir("inv-src");
let dst_dir = unique_tmp_dir("inv-dst");
let src = src_dir.join("worker-source");
make_worker(&src, b"version 1 contents");
let dst1 = ensure_worker_in_dir(&src, &dst_dir).unwrap();
let contents1 = std::fs::read(&dst1).unwrap();
make_worker(&src, b"version 2 contents - different");
let dst2 = ensure_worker_in_dir(&src, &dst_dir).unwrap();
let contents2 = std::fs::read(&dst2).unwrap();
assert_eq!(dst1, dst2);
assert_ne!(
contents1, contents2,
"source content change must invalidate the cache and re-copy"
);
assert_eq!(
contents2, b"version 2 contents - different",
"dst must reflect new source content"
);
let _ = std::fs::remove_dir_all(&src_dir);
let _ = std::fs::remove_dir_all(&dst_dir);
}
#[test]
fn copied_worker_is_executable() {
let src_dir = unique_tmp_dir("exec-src");
let dst_dir = unique_tmp_dir("exec-dst");
let src = src_dir.join("worker-source");
make_worker(&src, b"executable test");
let _ = std::fs::set_permissions(&src, std::fs::Permissions::from_mode(0o644));
let dst = ensure_worker_in_dir(&src, &dst_dir).unwrap();
let mode = std::fs::metadata(&dst).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o755, "dst worker must be 0755 regardless of source mode");
let _ = std::fs::remove_dir_all(&src_dir);
let _ = std::fs::remove_dir_all(&dst_dir);
}
#[test]
fn missing_source_returns_error_not_panic() {
let dst_dir = unique_tmp_dir("missing-dst");
let result = ensure_worker_in_dir(
std::path::Path::new("/does/not/exist/never/at/all"),
&dst_dir,
);
assert!(result.is_err(), "missing source should error");
let msg = result.err().unwrap();
assert!(
msg.contains("hash worker source") || msg.contains("copy "),
"error should be actionable, got: {msg}"
);
let _ = std::fs::remove_dir_all(&dst_dir);
}
#[test]
fn dst_dir_created_on_demand() {
let src_dir = unique_tmp_dir("mkdir-src");
let dst_dir = unique_tmp_dir("mkdir-dst").join("nested/deeper/v0.7.28");
let src = src_dir.join("worker-source");
make_worker(&src, b"mkdir test");
let dst = ensure_worker_in_dir(&src, &dst_dir).unwrap();
assert!(dst.exists(), "ensure_worker_in_dir must mkdir -p dst dir");
let _ = std::fs::remove_dir_all(&src_dir);
let _ = std::fs::remove_dir_all(dst_dir.ancestors().nth(2).unwrap());
}
#[test]
fn stale_partial_file_doesnt_block_copy() {
let src_dir = unique_tmp_dir("stale-src");
let dst_dir = unique_tmp_dir("stale-dst");
let src = src_dir.join("worker-source");
make_worker(&src, b"stale-partial test");
std::fs::create_dir_all(&dst_dir).unwrap();
let stale_partial = dst_dir.join("supermachine-worker.partial");
std::fs::write(&stale_partial, b"garbage from prior aborted run").unwrap();
let dst = ensure_worker_in_dir(&src, &dst_dir).unwrap();
assert_eq!(std::fs::read(&dst).unwrap(), b"stale-partial test");
assert!(!stale_partial.exists(), "stale .partial must be cleaned up");
let _ = std::fs::remove_dir_all(&src_dir);
let _ = std::fs::remove_dir_all(&dst_dir);
}
}