hexz_cli/cmd/data/
shell.rs1use anyhow::{Context, Result};
4use hexz_fuse::fuse::Hexz;
5use std::path::PathBuf;
6use std::process::Command;
7use colored::Colorize;
8
9use super::mount::open_archive;
10
11#[allow(unsafe_code)]
13pub fn run(
14 hexz_path: &str,
15 overlay: Option<PathBuf>,
16 editable: bool,
17 cache_size: Option<&str>,
18) -> Result<()> {
19 let uid = unsafe { libc::getuid() };
21 let gid = unsafe { libc::getgid() };
23
24 let snap = open_archive(hexz_path, cache_size, None)?;
25
26 let tmp_dir = tempfile::tempdir().context("Failed to create temporary directory")?;
28 let mountpoint = tmp_dir.path().to_path_buf();
29
30 let tmp_meta = tempfile::tempdir().context("Failed to create temporary metadata directory")?;
32 let metadata_dir = tmp_meta.path().to_path_buf();
33
34 let (overlay, is_temp_overlay) = if let Some(o) = overlay {
36 std::fs::create_dir_all(&o)?;
37 (Some(o), false)
38 } else if editable {
39 let temp_overlay = tempfile::tempdir().context("Failed to create temporary overlay")?;
40 let path = temp_overlay.path().to_path_buf();
41 let _ = temp_overlay.keep();
42 (Some(path), true)
43 } else {
44 (None, false)
45 };
46
47 {
49 let host_cwd = std::env::current_dir().ok();
50 let config = crate::cmd::data::workspace::WorkspaceConfig {
51 base_archive: Some(std::fs::canonicalize(hexz_path)?),
52 overlay_path: overlay.clone(),
53 host_cwd,
54 remotes: std::collections::HashMap::new(),
55 };
56 let config_path = metadata_dir.join("config.json");
57 let f = std::fs::File::create(config_path)?;
58 serde_json::to_writer_pretty(f, &config)?;
59 }
60
61 let fs = Hexz::new(snap, uid, gid, overlay.clone(), Some(&metadata_dir))?;
62
63 let mut options = vec![
64 fuser::MountOption::FSName("hexz".to_string()),
65 fuser::MountOption::DefaultPermissions,
66 ];
67
68 if overlay.is_none() {
69 options.push(fuser::MountOption::RO);
70 }
71
72 println!(" {} Mounting archive at {}", "→".yellow(), mountpoint.display().to_string().cyan());
73 if let Some(ref o) = overlay {
74 println!(" {} Using overlay: {}", "→".yellow(), o.display().to_string().bright_black());
75 }
76
77 let mountpoint_clone = mountpoint.clone();
79 let options_clone = options.clone();
80 drop(std::thread::spawn(move || {
81 let _ = fuser::mount2(fs, mountpoint_clone, &options_clone);
82 }));
83
84 std::thread::sleep(std::time::Duration::from_millis(200));
86
87 let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
89 println!(" {} Dropping into shell: {}", "→".yellow(), shell.bright_black());
90 println!(" {} Type {} to unmount and exit.", "→".yellow(), "exit".bold());
91
92 let status = Command::new(&shell)
93 .current_dir(&mountpoint)
94 .status()
95 .context("Failed to spawn shell")?;
96
97 if !status.success() {
98 eprintln!(" {} Shell exited with status: {}", "✗".red(), status);
99 }
100
101 println!(" {} Unmounting...", "→".yellow());
103
104 #[cfg(target_os = "linux")]
106 {
107 if Command::new("fusermount3").arg("-u").arg(&mountpoint).status().is_err() {
109 let _ = Command::new("fusermount").arg("-u").arg(&mountpoint).status();
110 }
111 }
112 #[cfg(target_os = "macos")]
113 let _ = Command::new("umount").arg(&mountpoint).status();
114
115 if is_temp_overlay {
116 if let Some(o) = overlay {
117 let _ = std::fs::remove_dir_all(o);
118 }
119 }
120
121 Ok(())
122}