diff options
| author | tv <tv@krebsco.de> | 2026-11-01 05:32:36 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-01 05:43:56 +0100 |
| commit | ac81f5a68bf3259d1e0c49c93b2f84635b873824 (patch) | |
| tree | f239d0a5d2f153ec4618b6413b9e5b735cb743eb /src/files.rs | |
| parent | b644b4a8084a1060c9bc27566dfac8470e1090b5 (diff) | |
allow creating all key files
Diffstat (limited to 'src/files.rs')
| -rw-r--r-- | src/files.rs | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/src/files.rs b/src/files.rs index cd2f38c..bc8fb8e 100644 --- a/src/files.rs +++ b/src/files.rs @@ -167,6 +167,57 @@ fn sync_parent(path: &Path) { } } +/// A key file's bytes: read from `path`; or with `create`, if there is no file there, +/// `len` random bytes in a new one only its owner may read (then also `true`). Without +/// `create`, a missing file is an error saying to give `create_option` (`what`: which +/// key; `hint`: what a new one means). Never created where a file is: a mistyped path +/// stays an error unless asked. +pub(crate) fn key_file( + path: &Path, + what: &str, + create: bool, + create_option: &str, + hint: &str, + len: usize, +) -> anyhow::Result<(Vec<u8>, bool)> { + use anyhow::Context; + use std::io::Write; + match std::fs::read(path) { + Ok(bytes) => { + warn_if_readable_by_others(path, what); + Ok((bytes, false)) + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + if !create { + anyhow::bail!( + "The {what} file {} doesn't exist; add --{create_option} to create a new {what} there ({hint})", + path.display() + ); + } + let mut bytes = vec![0u8; len]; + getrandom::fill(&mut bytes) + .map_err(|error| anyhow::anyhow!("No randomness: {error}"))?; + let mut options = std::fs::OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + let written = (|| { + let mut file = options.open(path)?; + file.write_all(&bytes)?; + file.sync_all() + })(); + written.with_context(|| format!("Creating the {what} file {}", path.display()))?; + Ok((bytes, true)) + } + Err(error) => { + Err(error).with_context(|| format!("Reading the {what} from {}", path.display())) + } + } +} + /// A key is a secret: a file others may read is worth a warning (not an error: a /// group may be deliberate, e.g. shared with a deployment tool). `what`: which key. pub(crate) fn warn_if_readable_by_others(path: &Path, what: &str) { |
