ic_host_fs/durable/tree/
mod.rs1use crate::read::tree::{
7 TreeVisitor,
8 checksum::{ChecksumVisitor, TreeChecksumError},
9 walk_tree,
10};
11use ic_host_artifacts::artifact::Sha256Digest;
12use rustix::{fd::OwnedFd, fs::FileType};
13use std::{
14 fs::File,
15 io::{self, Seek, SeekFrom},
16 os::fd::AsFd,
17 path::Path,
18};
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum TreeSyncStep {
23 RegularFile,
25 NestedDirectory,
27 RootDirectory,
29}
30
31pub fn sync_checksum_directory_at(
47 directory: &File,
48 display_root: &Path,
49 max_file_bytes: u64,
50 mut before_sync: impl FnMut(TreeSyncStep, &Path) -> io::Result<()>,
51) -> Result<Sha256Digest, TreeChecksumError> {
52 let directory_fd = directory.as_fd().try_clone_to_owned()?;
53 let mut visitor = SyncVisitor {
54 checksum: ChecksumVisitor::new(max_file_bytes),
55 before_sync: &mut before_sync,
56 };
57 walk_tree(&directory_fd, Path::new(""), display_root, &mut visitor)?;
58 visitor.checksum.finish()
59}
60
61struct SyncVisitor<'a, F> {
62 checksum: ChecksumVisitor,
63 before_sync: &'a mut F,
64}
65
66impl<F> TreeVisitor for SyncVisitor<'_, F>
67where
68 F: FnMut(TreeSyncStep, &Path) -> io::Result<()>,
69{
70 type Error = TreeChecksumError;
71
72 fn file(&mut self, fd: &OwnedFd, relative: &Path, display: &Path) -> Result<(), Self::Error> {
73 let mut file = File::from(fd.try_clone()?);
74 (self.before_sync)(TreeSyncStep::RegularFile, display)?;
75 file.sync_all()?;
76 file.seek(SeekFrom::Start(0))?;
77 self.checksum.record_file(file, relative)
78 }
79
80 fn enter_directory(&mut self, _relative: &Path, _display: &Path) -> Result<(), Self::Error> {
81 Ok(())
82 }
83
84 fn leave_directory(
85 &mut self,
86 fd: &OwnedFd,
87 relative: &Path,
88 display: &Path,
89 ) -> Result<(), Self::Error> {
90 let step = if relative.as_os_str().is_empty() {
91 TreeSyncStep::RootDirectory
92 } else {
93 TreeSyncStep::NestedDirectory
94 };
95 (self.before_sync)(step, display)?;
96 rustix::fs::fsync(fd).map_err(io::Error::from)?;
97 Ok(())
98 }
99
100 fn unsupported(&self, display: &Path, kind: FileType) -> Self::Error {
101 self.checksum.unsupported(display, kind)
102 }
103
104 fn non_utf8(&self, display: &Path) -> Self::Error {
105 self.checksum.non_utf8(display)
106 }
107}
108
109#[cfg(test)]
110mod tests;