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