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 b":wasm32-webc:M::\\x00webc::".as_ref(),
96 bin_path.as_os_str().as_bytes(),
97 b":PFC",
98 ]
99 .concat(),
100 ])
101 }
102 _ => None,
103 };
104 let wasm_registration = self.binfmt_misc.join("wasm32");
105 let wat_registration = self.binfmt_misc.join("wasm32-wat");
106 let webc_registration = self.binfmt_misc.join("wasm32-webc");
107 match self.action {
108 Register | Reregister | Unregister => {
109 let unregister = [wasm_registration, wat_registration, webc_registration]
110 .iter()
111 .map(|registration| {
112 if registration.exists() {
113 let mut registration = fs::OpenOptions::new()
114 .write(true)
115 .open(registration)
116 .context("Open existing binfmt entry to remove")?;
117 registration
118 .write_all(b"-1")
119 .context("Couldn't write binfmt unregister request")?;
120 Ok(true)
121 } else {
122 Ok(false)
123 }
124 })
125 .collect::<Vec<_>>()
126 .into_iter()
127 .collect::<Result<Vec<_>>>()?;
128 if let (Unregister, false) = (self.action, unregister.into_iter().any(|b| b)) {
129 bail!("Nothing unregistered");
130 }
131 }
132 };
133 if let Some(specs) = specs {
134 specs
135 .iter()
136 .map(|spec| {
137 let register = self.binfmt_misc.join("register");
138 let mut register = fs::OpenOptions::new()
139 .write(true)
140 .open(register)
141 .context("Open binfmt misc for registration")?;
142 register
143 .write_all(spec)
144 .context("Couldn't register binfmt")?;
145 Ok(())
146 })
147 .collect::<Vec<_>>()
148 .into_iter()
149 .collect::<Result<Vec<_>>>()?;
150 }
151 Ok(())
152 }
153}