61 lines
2.4 KiB
Rust
61 lines
2.4 KiB
Rust
//! Listening on a Unix socket: the directory at 0700, a stale socket removed, the socket at 0600
|
|
//! (M5a spec, section 7). The same steps as `brokerd`'s; no crate depends on another role's.
|
|
|
|
use std::fs::{DirBuilder, Permissions};
|
|
use std::io;
|
|
use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
|
|
use std::os::unix::net::UnixListener;
|
|
use std::path::Path;
|
|
|
|
/// Bind one socket: prepare its directory at 0700, make it private, remove any stale socket,
|
|
/// bind, then make the socket itself private at 0600. The error names the path.
|
|
pub fn listen(socket: &Path) -> Result<UnixListener, String> {
|
|
// A socket needs a directory of its own: step 2 makes that directory 0700, which must never
|
|
// be `/` or a shared directory reached through a symbolic link.
|
|
let dir = match socket.parent() {
|
|
Some(parent) if parent.parent().is_some() && !parent.as_os_str().is_empty() => {
|
|
parent.to_path_buf()
|
|
}
|
|
_ => {
|
|
return Err(format!(
|
|
"{}: a socket needs a directory of its own, not / or none",
|
|
socket.display()
|
|
));
|
|
}
|
|
};
|
|
|
|
// 1. The directory, at 0700.
|
|
DirBuilder::new()
|
|
.recursive(true)
|
|
.mode(0o700)
|
|
.create(&dir)
|
|
.map_err(|e| format!("{}: {e}", dir.display()))?;
|
|
// `set_permissions` follows a symbolic link, so a link would make the directory it points to
|
|
// private instead: refuse it.
|
|
let kind = std::fs::symlink_metadata(&dir)
|
|
.map_err(|e| format!("{}: {e}", dir.display()))?
|
|
.file_type();
|
|
if !kind.is_dir() {
|
|
return Err(format!(
|
|
"{}: is a symbolic link or not a directory; memoryd will not change its mode",
|
|
dir.display()
|
|
));
|
|
}
|
|
// 2. Make it private, always, even when it was already there at 0755.
|
|
std::fs::set_permissions(&dir, Permissions::from_mode(0o700))
|
|
.map_err(|e| format!("{}: {e}", dir.display()))?;
|
|
|
|
// 3. A stale socket is removed; a missing one is fine.
|
|
match std::fs::remove_file(socket) {
|
|
Ok(()) => {}
|
|
Err(e) if e.kind() == io::ErrorKind::NotFound => {}
|
|
Err(e) => return Err(format!("{}: {e}", socket.display())),
|
|
}
|
|
|
|
// 4. Bind, then 5. make the socket private.
|
|
let listener = UnixListener::bind(socket).map_err(|e| format!("{}: {e}", socket.display()))?;
|
|
std::fs::set_permissions(socket, Permissions::from_mode(0o600))
|
|
.map_err(|e| format!("{}: {e}", socket.display()))?;
|
|
Ok(listener)
|
|
}
|