wasmer_cli/commands/
binfmt.rs1use std::{
2 env,
3 fs::{self, Permissions},
4 io::Write,
5 os::unix::{
6 ffi::OsStrExt,
7 fs::{MetadataExt, PermissionsExt},
8 },
9 path::{Path, PathBuf},
10};
11
12use Action::*;
13use anyhow::{Context, Result, bail};
14use clap::Parser;
15
16#[derive(Debug, Parser, Clone, Copy)]
17enum Action {
18 Register,
20 Unregister,
22 Reregister,
24}
25
26#[derive(Debug, Parser)]
31pub struct Binfmt {
32 #[clap(long, default_value = "/proc/sys/fs/binfmt_misc/")]
35 binfmt_misc: PathBuf,
36
37 #[clap(subcommand)]
38 action: Action,
39}
40
41fn seccheck(path: &Path) -> Result<()> {
46 if let Some(parent) = path.parent() {
47 seccheck(parent)?;
48 }
49 let m = std::fs::metadata(path)
50 .with_context(|| format!("Can't check permissions of {}", path.to_string_lossy()))?;
51 use unix_mode::*;
52 anyhow::ensure!(
53 !is_allowed(Accessor::Other, Access::Write, m.mode()) || is_sticky(m.mode()),
54 "{} is world writeable and not sticky ({m:?})",
55 path.to_string_lossy()
56 );
57 Ok(())
58}
59
60impl Binfmt {
61 pub const FILENAME: &'static str = "wasmer-binfmt-interpreter";
63
64 pub fn execute(&self) -> Result<()> {
66 if !self.binfmt_misc.exists() {
67 panic!("{} does not exist", self.binfmt_misc.to_string_lossy());
68 }
69 let temp_dir;
70 let specs = match self.action {
71 Register | Reregister => {
72 temp_dir = tempfile::Builder::new()
73 .permissions(Permissions::from_mode(0o1755))
74 .tempdir()
75 .context("Make temporary directory")?;
76 seccheck(temp_dir.path())?;
77 let bin_path_orig: PathBuf = env::current_exe()
78 .and_then(|p| p.canonicalize())
79 .context("Cannot get path to wasmer executable")?;
80 let bin_path = temp_dir.path().join(Binfmt::FILENAME);
81 fs::copy(bin_path_orig, &bin_path).context("Copy wasmer binary to temp folder")?;
82 let bin_path = fs::canonicalize(&bin_path).with_context(|| {
83 format!(
84 "Couldn't get absolute path for {}",
85 bin_path.to_string_lossy()
86 )
87 })?;
88 Some([
93 [
94 b":wasm32:M::\\x00asm\\x01\\x00\\x00::".as_ref(),
95 bin_path.as_os_str().as_bytes(),
96 b":PFC",
97 ]
98 .concat(),
99 [
100 b":wasm32-wat:E::wat::".as_ref(),
101 bin_path.as_os_str().as_bytes(),
102 b":PFC",
103 ]
104 .concat(),
105 [
106 b":wasm32-webc:M::\\x00webc::".as_ref(),
107 bin_path.as_os_str().as_bytes(),
108 b":PFC",
109 ]
110 .concat(),
111 ])
112 }
113 _ => None,
114 };
115 let wasm_registration = self.binfmt_misc.join("wasm32");
116 let wat_registration = self.binfmt_misc.join("wasm32-wat");
117 let webc_registration = self.binfmt_misc.join("wasm32-webc");
118 match self.action {
119 Register | Reregister | Unregister => {
120 let unregister = [wasm_registration, wat_registration, webc_registration]
121 .iter()
122 .map(|registration| {
123 if registration.exists() {
124 let mut registration = fs::OpenOptions::new()
125 .write(true)
126 .open(registration)
127 .context("Open existing binfmt entry to remove")?;
128 registration
129 .write_all(b"-1")
130 .context("Couldn't write binfmt unregister request")?;
131 Ok(true)
132 } else {
133 Ok(false)
134 }
135 })
136 .collect::<Vec<_>>()
137 .into_iter()
138 .collect::<Result<Vec<_>>>()?;
139 if let (Unregister, false) = (self.action, unregister.into_iter().any(|b| b)) {
140 bail!("Nothing unregistered");
141 }
142 }
143 };
144 if let Some(specs) = specs {
145 specs
146 .iter()
147 .map(|spec| {
148 let register = self.binfmt_misc.join("register");
149 let mut register = fs::OpenOptions::new()
150 .write(true)
151 .open(register)
152 .context("Open binfmt misc for registration")?;
153 register
154 .write_all(spec)
155 .context("Couldn't register binfmt")?;
156 Ok(())
157 })
158 .collect::<Vec<_>>()
159 .into_iter()
160 .collect::<Result<Vec<_>>>()?;
161 }
162 Ok(())
163 }
164}