hexz_cli/cmd/data/
shell.rs1use anyhow::{Context, Result};
4use colored::Colorize;
5use hexz_fuse::fuse::Hexz;
6use std::path::PathBuf;
7use std::process::Command;
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!(
73 " {} Mounting archive at {}",
74 "→".yellow(),
75 mountpoint.display().to_string().cyan()
76 );
77 if let Some(ref o) = overlay {
78 println!(
79 " {} Using overlay: {}",
80 "→".yellow(),
81 o.display().to_string().bright_black()
82 );
83 }
84
85 let mountpoint_clone = mountpoint.clone();
87 let options_clone = options.clone();
88 drop(std::thread::spawn(move || {
89 let _ = fuser::mount2(fs, mountpoint_clone, &options_clone);
90 }));
91
92 std::thread::sleep(std::time::Duration::from_millis(200));
94
95 let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
97 println!(
98 " {} Dropping into shell: {}",
99 "→".yellow(),
100 shell.bright_black()
101 );
102 println!(
103 " {} Type {} to unmount and exit.",
104 "→".yellow(),
105 "exit".bold()
106 );
107
108 let status = Command::new(&shell)
109 .current_dir(&mountpoint)
110 .status()
111 .context("Failed to spawn shell")?;
112
113 if !status.success() {
114 eprintln!(" {} Shell exited with status: {}", "✗".red(), status);
115 }
116
117 println!(" {} Unmounting...", "→".yellow());
119
120 #[cfg(target_os = "linux")]
122 {
123 if Command::new("fusermount3")
125 .arg("-u")
126 .arg(&mountpoint)
127 .status()
128 .is_err()
129 {
130 let _ = Command::new("fusermount")
131 .arg("-u")
132 .arg(&mountpoint)
133 .status();
134 }
135 }
136 #[cfg(target_os = "macos")]
137 let _ = Command::new("umount").arg(&mountpoint).status();
138
139 if is_temp_overlay {
140 if let Some(o) = overlay {
141 let _ = std::fs::remove_dir_all(o);
142 }
143 }
144
145 Ok(())
146}