ic_host_fs/durable/directory/
mod.rs1use std::{fs::File, io, path::Path};
3
4mod publish;
5
6pub use publish::{
7 DirectoryCommitError, DirectoryCommitOutcome, DirectoryCommitStep,
8 commit_existing_directory_with,
9};
10
11#[derive(Debug, thiserror::Error)]
13pub enum DirectoryCreationError {
14 #[error("private directory creation refused")]
16 BeforeCreation(#[source] io::Error),
17 #[error("created private directory admission failed")]
20 AfterCreation(#[source] io::Error),
21}
22
23pub fn create_private_directory(path: &Path) -> Result<File, DirectoryCreationError> {
34 #[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))]
35 {
36 use std::os::unix::fs::DirBuilderExt as _;
37 let parent = path
38 .parent()
39 .filter(|p| !p.as_os_str().is_empty())
40 .unwrap_or_else(|| Path::new("."));
41 for directory in [
43 path.to_path_buf(),
44 parent
45 .canonicalize()
46 .map_err(DirectoryCreationError::BeforeCreation)?,
47 ] {
48 if directory
49 .as_os_str()
50 .as_encoded_bytes()
51 .iter()
52 .any(|b| matches!(b, b'\r' | b'\n'))
53 {
54 return Err(DirectoryCreationError::BeforeCreation(
55 io::ErrorKind::InvalidInput.into(),
56 ));
57 }
58 }
59 std::fs::DirBuilder::new()
60 .mode(0o700)
61 .create(path)
62 .map_err(DirectoryCreationError::BeforeCreation)?;
63 let finish = || -> io::Result<File> {
64 use rustix::fs::{Mode, OFlags, open};
65 File::open(parent)?.sync_all()?;
66 let file = File::from(open(
67 path,
68 OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC,
69 Mode::empty(),
70 )?);
71 admit_directory(path, &file)?;
72 Ok(file)
73 };
74 finish().map_err(DirectoryCreationError::AfterCreation)
75 }
76 #[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))]
77 {
78 let _ = path;
79 Err(DirectoryCreationError::BeforeCreation(
80 io::ErrorKind::Unsupported.into(),
81 ))
82 }
83}
84
85pub fn admit_directory(path: &Path, directory: &File) -> io::Result<()> {
92 #[cfg(unix)]
93 {
94 use crate::identity::FileIdentity;
95 let current = std::fs::symlink_metadata(path)?;
96 let held = directory.metadata()?;
97 if !current.is_dir()
98 || !held.is_dir()
99 || FileIdentity::from_metadata(¤t) != FileIdentity::from_metadata(&held)
100 {
101 return Err(io::ErrorKind::InvalidInput.into());
102 }
103 Ok(())
104 }
105 #[cfg(not(unix))]
106 {
107 let _ = (path, directory);
108 Err(io::ErrorKind::Unsupported.into())
109 }
110}
111
112#[cfg(test)]
113mod tests;