memoryd: the crate, memoryd.toml, and listening on a socket
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
//! 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)
|
||||
}
|
||||
Reference in New Issue
Block a user