1use std::fs;
6use std::fs::DirEntry;
7
8use anyhow::{anyhow, Context, Result};
9use nydus_utils::{event_tracer, lazy_drop, root_tracer, timing_tracer};
10
11use crate::core::context::{Artifact, NoopArtifactWriter};
12use crate::core::prefetch;
13
14use super::core::blob::Blob;
15use super::core::context::{
16 ArtifactWriter, BlobManager, BootstrapManager, BuildContext, BuildOutput,
17};
18use super::core::node::Node;
19use super::{build_bootstrap, dump_bootstrap, finalize_blob, Builder, Overlay, Tree, TreeNode};
20
21struct FilesystemTreeBuilder {}
22
23impl FilesystemTreeBuilder {
24 fn new() -> Self {
25 Self {}
26 }
27
28 #[allow(clippy::only_used_in_recursion)]
29 fn load_children(
31 &self,
32 ctx: &mut BuildContext,
33 parent: &TreeNode,
34 layer_idx: u16,
35 ) -> Result<(Vec<Tree>, Vec<Tree>)> {
36 let mut trees = Vec::new();
37 let mut external_trees = Vec::new();
38 let parent = parent.borrow();
39 if !parent.is_dir() {
40 return Ok((trees.clone(), external_trees));
41 }
42
43 let children = fs::read_dir(parent.path())
44 .with_context(|| format!("failed to read dir {:?}", parent.path()))?;
45 let children = children.collect::<Result<Vec<DirEntry>, std::io::Error>>()?;
46
47 event_tracer!("load_from_directory", +children.len());
48 for child in children {
49 let path = child.path();
50 let target = Node::generate_target(&path, &ctx.source_path);
51 let mut file_size: u64 = 0;
52 if ctx.attributes.is_external(&target) {
53 if let Some(value) = ctx.attributes.get_value(&target, "file_size") {
54 file_size = value.parse::<u64>().ok().ok_or_else(|| {
55 anyhow!(
56 "failed to parse file_size for external file {}",
57 &target.display()
58 )
59 })?;
60 }
61 }
62
63 let mut child = Node::from_fs_object(
64 ctx.fs_version,
65 ctx.source_path.clone(),
66 path.clone(),
67 Overlay::UpperAddition,
68 ctx.chunk_size,
69 file_size,
70 parent.info.explicit_uidgid,
71 true,
72 )
73 .with_context(|| format!("failed to create node {:?}", path))?;
74 child.layer_idx = layer_idx;
75
76 if layer_idx == 0
79 && child.whiteout_type(ctx.whiteout_spec).is_some()
80 && !child.is_overlayfs_opaque(ctx.whiteout_spec)
81 {
82 continue;
83 }
84
85 let (mut child, mut external_child) = (Tree::new(child.clone()), Tree::new(child));
86 let (child_children, external_children) =
87 self.load_children(ctx, &child.node, layer_idx)?;
88 child.children = child_children;
89 external_child.children = external_children;
90 child
91 .borrow_mut_node()
92 .v5_set_dir_size(ctx.fs_version, &child.children);
93 external_child
94 .borrow_mut_node()
95 .v5_set_dir_size(ctx.fs_version, &external_child.children);
96
97 if ctx.attributes.is_external(&target) {
98 external_trees.push(external_child);
99 } else {
100 trees.push(child.clone());
103 if ctx.attributes.is_prefix_external(target) {
104 external_trees.push(external_child);
105 }
106 };
107 }
108
109 trees.sort_unstable_by(|a, b| a.name().cmp(b.name()));
110 external_trees.sort_unstable_by(|a, b| a.name().cmp(b.name()));
111
112 Ok((trees, external_trees))
113 }
114}
115
116#[derive(Default)]
117pub struct DirectoryBuilder {}
118
119impl DirectoryBuilder {
120 pub fn new() -> Self {
121 Self {}
122 }
123
124 fn build_tree(&mut self, ctx: &mut BuildContext, layer_idx: u16) -> Result<(Tree, Tree)> {
126 let node = Node::from_fs_object(
127 ctx.fs_version,
128 ctx.source_path.clone(),
129 ctx.source_path.clone(),
130 Overlay::UpperAddition,
131 ctx.chunk_size,
132 0,
133 ctx.explicit_uidgid,
134 true,
135 )?;
136 let mut tree = Tree::new(node.clone());
137 let mut external_tree = Tree::new(node);
138 let tree_builder = FilesystemTreeBuilder::new();
139
140 let (tree_children, external_tree_children) = timing_tracer!(
141 { tree_builder.load_children(ctx, &tree.node, layer_idx) },
142 "load_from_directory"
143 )?;
144 tree.children = tree_children;
145 external_tree.children = external_tree_children;
146 tree.borrow_mut_node()
147 .v5_set_dir_size(ctx.fs_version, &tree.children);
148 external_tree
149 .borrow_mut_node()
150 .v5_set_dir_size(ctx.fs_version, &external_tree.children);
151
152 Ok((tree, external_tree))
153 }
154
155 fn one_build(
156 &mut self,
157 ctx: &mut BuildContext,
158 bootstrap_mgr: &mut BootstrapManager,
159 blob_mgr: &mut BlobManager,
160 blob_writer: &mut Box<dyn Artifact>,
161 tree: Tree,
162 ) -> Result<BuildOutput> {
163 let mut bootstrap_ctx = bootstrap_mgr.create_ctx()?;
165 let mut bootstrap = timing_tracer!(
166 { build_bootstrap(ctx, bootstrap_mgr, &mut bootstrap_ctx, blob_mgr, tree) },
167 "build_bootstrap"
168 )?;
169
170 timing_tracer!(
172 { Blob::dump(ctx, blob_mgr, blob_writer.as_mut()) },
173 "dump_blob"
174 )?;
175
176 if let Some((_, blob_ctx)) = blob_mgr.get_current_blob() {
178 Blob::dump_meta_data(ctx, blob_ctx, blob_writer.as_mut())?;
179 }
180
181 if ctx.blob_inline_meta {
183 timing_tracer!(
184 {
185 dump_bootstrap(
186 ctx,
187 bootstrap_mgr,
188 &mut bootstrap_ctx,
189 &mut bootstrap,
190 blob_mgr,
191 blob_writer.as_mut(),
192 )
193 },
194 "dump_bootstrap"
195 )?;
196 finalize_blob(ctx, blob_mgr, blob_writer.as_mut())?;
197 } else {
198 finalize_blob(ctx, blob_mgr, blob_writer.as_mut())?;
199 timing_tracer!(
200 {
201 dump_bootstrap(
202 ctx,
203 bootstrap_mgr,
204 &mut bootstrap_ctx,
205 &mut bootstrap,
206 blob_mgr,
207 blob_writer.as_mut(),
208 )
209 },
210 "dump_bootstrap"
211 )?;
212 }
213
214 lazy_drop(bootstrap_ctx);
215
216 BuildOutput::new(blob_mgr, None, &bootstrap_mgr.bootstrap_storage, &None)
217 }
218}
219
220impl Builder for DirectoryBuilder {
221 fn build(
222 &mut self,
223 ctx: &mut BuildContext,
224 bootstrap_mgr: &mut BootstrapManager,
225 blob_mgr: &mut BlobManager,
226 ) -> Result<BuildOutput> {
227 let layer_idx = u16::from(bootstrap_mgr.f_parent_path.is_some());
228
229 let (tree, external_tree) =
231 timing_tracer!({ self.build_tree(ctx, layer_idx) }, "build_tree")?;
232
233 let mut blob_writer: Box<dyn Artifact> = if let Some(blob_stor) = ctx.blob_storage.clone() {
235 Box::new(ArtifactWriter::new(blob_stor)?)
236 } else {
237 Box::<NoopArtifactWriter>::default()
238 };
239 let mut output = self.one_build(ctx, bootstrap_mgr, blob_mgr, &mut blob_writer, tree)?;
240
241 ctx.prefetch = prefetch::Prefetch::new(prefetch::PrefetchPolicy::None)?;
243 let mut external_blob_mgr = BlobManager::new(ctx.digester, true);
244 let mut external_bootstrap_mgr = bootstrap_mgr.clone();
245 if let Some(stor) = external_bootstrap_mgr.bootstrap_storage.as_mut() {
246 stor.add_suffix("external")
247 }
248
249 let mut external_blob_writer: Box<dyn Artifact> =
250 if let Some(blob_stor) = ctx.external_blob_storage.clone() {
251 Box::new(ArtifactWriter::new(blob_stor)?)
252 } else {
253 Box::<NoopArtifactWriter>::default()
254 };
255 let external_output = self.one_build(
256 ctx,
257 &mut external_bootstrap_mgr,
258 &mut external_blob_mgr,
259 &mut external_blob_writer,
260 external_tree,
261 )?;
262 output.external_bootstrap_path = external_output.bootstrap_path;
263 output.external_blobs = external_output.blobs;
264
265 Ok(output)
266 }
267}