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