nydus_builder/
directory.rs

1// Copyright 2020 Ant Group. All rights reserved.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use 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    /// Walk directory to build node tree by DFS
30    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            // as per OCI spec, whiteout file should not be present within final image
77            // or filesystem, only existed in layers.
78            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                // TODO: need to implement type=ignore for nydus attributes,
101                // let's ignore the tree for workaround.
102                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    /// Build node tree from a filesystem directory
125    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        // Build bootstrap
164        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        // Dump blob file
171        timing_tracer!(
172            { Blob::dump(ctx, blob_mgr, blob_writer.as_mut()) },
173            "dump_blob"
174        )?;
175
176        // Dump blob meta information
177        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        // Dump RAFS meta/bootstrap and finalize the data blob.
182        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        // Scan source directory to build upper layer tree.
230        let (tree, external_tree) =
231            timing_tracer!({ self.build_tree(ctx, layer_idx) }, "build_tree")?;
232
233        // Build for tree
234        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        // Build for external tree
242        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}