use std::fs;
use std::fs::DirEntry;
use anyhow::{Context, Result};
use crate::builder::{build_bootstrap, dump_bootstrap, finalize_blob, Builder};
use crate::core::blob::Blob;
use crate::core::context::{
ArtifactWriter, BlobManager, BootstrapContext, BootstrapManager, BuildContext, BuildOutput,
};
use crate::core::node::{Node, Overlay};
use crate::core::tree::Tree;
struct FilesystemTreeBuilder {}
impl FilesystemTreeBuilder {
fn new() -> Self {
Self {}
}
#[allow(clippy::only_used_in_recursion)]
fn load_children(
&self,
ctx: &mut BuildContext,
bootstrap_ctx: &mut BootstrapContext,
parent: &mut Node,
layer_idx: u16,
) -> Result<Vec<Tree>> {
let mut result = Vec::new();
if !parent.is_dir() {
return Ok(result);
}
let children = fs::read_dir(parent.path())
.with_context(|| format!("failed to read dir {:?}", parent.path()))?;
let children = children.collect::<Result<Vec<DirEntry>, std::io::Error>>()?;
event_tracer!("load_from_directory", +children.len());
for child in children {
let path = child.path();
let mut child = Node::new(
ctx.fs_version,
ctx.source_path.clone(),
path.clone(),
Overlay::UpperAddition,
ctx.chunk_size,
parent.explicit_uidgid,
true,
)
.with_context(|| format!("failed to create node {:?}", path))?;
child.layer_idx = layer_idx;
if !bootstrap_ctx.layered
&& child.whiteout_type(ctx.whiteout_spec).is_some()
&& !child.is_overlayfs_opaque(ctx.whiteout_spec)
{
continue;
}
let mut child = Tree::new(child);
child.children = self.load_children(ctx, bootstrap_ctx, &mut child.node, layer_idx)?;
child.node.v5_set_dir_size(ctx.fs_version, &child.children);
result.push(child);
}
Ok(result)
}
}
pub(crate) struct DirectoryBuilder {}
impl DirectoryBuilder {
pub fn new() -> Self {
Self {}
}
fn build_tree(
&mut self,
ctx: &mut BuildContext,
bootstrap_ctx: &mut BootstrapContext,
layer_idx: u16,
) -> Result<Tree> {
let node = Node::new(
ctx.fs_version,
ctx.source_path.clone(),
ctx.source_path.clone(),
Overlay::UpperAddition,
ctx.chunk_size,
ctx.explicit_uidgid,
true,
)?;
let mut tree = Tree::new(node);
let tree_builder = FilesystemTreeBuilder::new();
tree.children = timing_tracer!(
{ tree_builder.load_children(ctx, bootstrap_ctx, &mut tree.node, layer_idx) },
"load_from_directory"
)?;
tree.node.v5_set_dir_size(ctx.fs_version, &tree.children);
Ok(tree)
}
}
impl Builder for DirectoryBuilder {
fn build(
&mut self,
ctx: &mut BuildContext,
bootstrap_mgr: &mut BootstrapManager,
blob_mgr: &mut BlobManager,
) -> Result<BuildOutput> {
let mut bootstrap_ctx = bootstrap_mgr.create_ctx(ctx.blob_inline_meta)?;
let layer_idx = u16::from(bootstrap_ctx.layered);
let mut blob_writer = if let Some(blob_stor) = ctx.blob_storage.clone() {
ArtifactWriter::new(blob_stor, ctx.blob_inline_meta)?
} else {
return Err(anyhow!(
"target blob path should always be valid for directory builder"
));
};
let tree = timing_tracer!(
{ self.build_tree(ctx, &mut bootstrap_ctx, layer_idx) },
"build_tree"
)?;
let mut bootstrap = timing_tracer!(
{ build_bootstrap(ctx, bootstrap_mgr, &mut bootstrap_ctx, blob_mgr, tree) },
"build_bootstrap"
)?;
timing_tracer!(
{ Blob::dump(ctx, &mut bootstrap_ctx.nodes, blob_mgr, &mut blob_writer,) },
"dump_blob"
)?;
if let Some((_, blob_ctx)) = blob_mgr.get_current_blob() {
Blob::dump_meta_data(ctx, blob_ctx, &mut blob_writer)?;
}
if ctx.blob_inline_meta {
timing_tracer!(
{
dump_bootstrap(
ctx,
bootstrap_mgr,
&mut bootstrap_ctx,
&mut bootstrap,
blob_mgr,
&mut blob_writer,
)
},
"dump_bootstrap"
)?;
finalize_blob(ctx, blob_mgr, &mut blob_writer)?;
} else {
finalize_blob(ctx, blob_mgr, &mut blob_writer)?;
timing_tracer!(
{
dump_bootstrap(
ctx,
bootstrap_mgr,
&mut bootstrap_ctx,
&mut bootstrap,
blob_mgr,
&mut blob_writer,
)
},
"dump_bootstrap"
)?;
}
BuildOutput::new(blob_mgr, &bootstrap_mgr.bootstrap_storage)
}
}