Skip to main content

hotl_platform/privatefs/
unix.rs

1//! `0700`/`0600` at create, via the mode extensions.
2
3use super::{EffectiveAccess, PrivateFs, Writes};
4use std::fs::{DirBuilder, File, OpenOptions};
5use std::io;
6use std::os::unix::fs::{DirBuilderExt, MetadataExt, OpenOptionsExt, PermissionsExt};
7use std::path::Path;
8
9#[derive(Debug, Clone, Copy, Default)]
10pub struct UnixPrivateFs;
11
12impl UnixPrivateFs {
13    pub const fn new() -> Self {
14        Self
15    }
16}
17
18impl crate::sealed::Sealed for UnixPrivateFs {}
19
20impl PrivateFs for UnixPrivateFs {
21    fn create_dir(&self, path: &Path) -> io::Result<()> {
22        match DirBuilder::new().mode(0o700).recursive(false).create(path) {
23            Ok(()) => Ok(()),
24            // `recursive(true)` would swallow a pre-existing loose directory
25            // silently; tighten it instead so the postcondition holds either
26            // way.
27            Err(e) if e.kind() == io::ErrorKind::AlreadyExists => self.harden_existing(path),
28            Err(e) => Err(e),
29        }
30    }
31
32    fn create_file_new(&self, path: &Path, writes: Writes) -> io::Result<File> {
33        // `create_new` is the `O_EXCL`, `mode` is the at-create restriction —
34        // together they leave no window in which the file exists and is
35        // readable.
36        let mut opts = OpenOptions::new();
37        opts.create_new(true).mode(0o600);
38        match writes {
39            Writes::FromStart => opts.write(true),
40            Writes::Append => opts.append(true),
41        };
42        opts.open(path)
43    }
44
45    fn create_file_truncate(&self, path: &Path) -> io::Result<File> {
46        let file = OpenOptions::new()
47            .write(true)
48            .create(true)
49            .truncate(true)
50            .mode(0o600)
51            .open(path)?;
52        // `mode` was ignored if the file already existed, so narrow it now —
53        // the window the trait doc names.
54        self.harden_existing(path)?;
55        Ok(file)
56    }
57
58    fn harden_existing(&self, path: &Path) -> io::Result<()> {
59        let meta = std::fs::metadata(path)?;
60        let mode = if meta.is_dir() { 0o700 } else { 0o600 };
61        std::fs::set_permissions(path, std::fs::Permissions::from_mode(mode))
62    }
63
64    fn effective_access(&self, path: &Path) -> io::Result<EffectiveAccess> {
65        let mode = std::fs::metadata(path)?.mode() & 0o777;
66        let group = mode & 0o070;
67        let other = mode & 0o007;
68        let mut other_readers = Vec::new();
69        if group != 0 {
70            other_readers.push(format!("group ({:03o})", group >> 3));
71        }
72        if other != 0 {
73            other_readers.push(format!("other ({other:03o})"));
74        }
75        Ok(EffectiveAccess {
76            owner_only: other_readers.is_empty(),
77            other_readers,
78        })
79    }
80}