nydus_builder/core/
bootstrap.rs

1// Copyright 2020 Ant Group. All rights reserved.
2// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
3//
4// SPDX-License-Identifier: Apache-2.0
5
6use anyhow::{Context, Error, Result};
7use nydus_utils::digest::{self, RafsDigest};
8use std::ops::Deref;
9
10use nydus_rafs::metadata::layout::{RafsBlobTable, RAFS_V5_ROOT_INODE};
11use nydus_rafs::metadata::{RafsSuper, RafsSuperConfig, RafsSuperFlags};
12
13use crate::{ArtifactStorage, BlobManager, BootstrapContext, BootstrapManager, BuildContext, Tree};
14
15/// RAFS bootstrap/meta builder.
16pub struct Bootstrap {
17    pub(crate) tree: Tree,
18}
19
20impl Bootstrap {
21    /// Create a new instance of [Bootstrap].
22    pub fn new(tree: Tree) -> Result<Self> {
23        Ok(Self { tree })
24    }
25
26    /// Build the final view of the RAFS filesystem meta from the hierarchy `tree`.
27    pub fn build(
28        &mut self,
29        ctx: &mut BuildContext,
30        bootstrap_ctx: &mut BootstrapContext,
31    ) -> Result<()> {
32        // Special handling of the root inode
33        let mut root_node = self.tree.borrow_mut_node();
34        assert!(root_node.is_dir());
35        let index = bootstrap_ctx.generate_next_ino();
36        // 0 is reserved and 1 also matches RAFS_V5_ROOT_INODE.
37        assert_eq!(index, RAFS_V5_ROOT_INODE);
38        root_node.index = index;
39        root_node.inode.set_ino(index);
40        ctx.prefetch.insert(&self.tree.node, root_node.deref());
41        bootstrap_ctx.inode_map.insert(
42            (
43                root_node.layer_idx,
44                root_node.info.src_ino,
45                root_node.info.src_dev,
46            ),
47            vec![self.tree.node.clone()],
48        );
49        drop(root_node);
50
51        Self::build_rafs(ctx, bootstrap_ctx, &mut self.tree)?;
52        if ctx.fs_version.is_v6() {
53            let root_offset = self.tree.node.borrow().v6_offset;
54            Self::v6_update_dirents(&self.tree, root_offset);
55        }
56
57        Ok(())
58    }
59
60    /// Dump the RAFS filesystem meta information to meta blob.
61    pub fn dump(
62        &mut self,
63        ctx: &mut BuildContext,
64        bootstrap_storage: &mut Option<ArtifactStorage>,
65        bootstrap_ctx: &mut BootstrapContext,
66        blob_table: &RafsBlobTable,
67    ) -> Result<()> {
68        match blob_table {
69            RafsBlobTable::V5(table) => self.v5_dump(ctx, bootstrap_ctx, table)?,
70            RafsBlobTable::V6(table) => self.v6_dump(ctx, bootstrap_ctx, table)?,
71        }
72
73        if let Some(ArtifactStorage::FileDir(p)) = bootstrap_storage {
74            let bootstrap_data = bootstrap_ctx.writer.as_bytes()?;
75            let digest = RafsDigest::from_buf(&bootstrap_data, digest::Algorithm::Sha256);
76            let name = digest.to_string();
77            bootstrap_ctx.writer.finalize(Some(name.clone()))?;
78            let mut path = p.0.join(name);
79            path.set_extension(&p.1);
80            *bootstrap_storage = Some(ArtifactStorage::SingleFile(path));
81            Ok(())
82        } else {
83            bootstrap_ctx.writer.finalize(Some(String::default()))
84        }
85    }
86
87    /// Traverse node tree, set inode index, ino, child_index and child_count etc according to the
88    /// RAFS metadata format, then store to nodes collection.
89    fn build_rafs(
90        ctx: &mut BuildContext,
91        bootstrap_ctx: &mut BootstrapContext,
92        tree: &mut Tree,
93    ) -> Result<()> {
94        let parent_node = tree.node.clone();
95        let mut parent_node = parent_node.borrow_mut();
96        let parent_ino = parent_node.inode.ino();
97        let block_size = ctx.v6_block_size();
98
99        // In case of multi-layer building, it's possible that the parent node is not a directory.
100        if parent_node.is_dir() {
101            parent_node
102                .inode
103                .set_child_count(tree.children.len() as u32);
104            if ctx.fs_version.is_v5() {
105                parent_node
106                    .inode
107                    .set_child_index(bootstrap_ctx.get_next_ino() as u32);
108            } else if ctx.fs_version.is_v6() {
109                // Layout directory entries for v6.
110                let d_size = parent_node.v6_dirent_size(ctx, tree)?;
111                parent_node.v6_set_dir_offset(bootstrap_ctx, d_size, block_size)?;
112            }
113        }
114
115        let mut dirs: Vec<&mut Tree> = Vec::new();
116        for child in tree.children.iter_mut() {
117            let child_node = child.node.clone();
118            let mut child_node = child_node.borrow_mut();
119            let index = bootstrap_ctx.generate_next_ino();
120            child_node.index = index;
121            if ctx.fs_version.is_v5() {
122                child_node.inode.set_parent(parent_ino);
123            }
124
125            // Handle hardlink.
126            // All hardlink nodes' ino and nlink should be the same.
127            // We need to find hardlink node index list in the layer where the node is located
128            // because the real_ino may be different among different layers,
129            let mut v6_hardlink_offset: Option<u64> = None;
130            let key = (
131                child_node.layer_idx,
132                child_node.info.src_ino,
133                child_node.info.src_dev,
134            );
135            if let Some(indexes) = bootstrap_ctx.inode_map.get_mut(&key) {
136                let nlink = indexes.len() as u32 + 1;
137                // Update nlink for previous hardlink inodes
138                for n in indexes.iter() {
139                    n.borrow_mut().inode.set_nlink(nlink);
140                }
141
142                let (first_ino, first_offset) = {
143                    let first_node = indexes[0].borrow_mut();
144                    (first_node.inode.ino(), first_node.v6_offset)
145                };
146                // set offset for rafs v6 hardlinks
147                v6_hardlink_offset = Some(first_offset);
148                child_node.inode.set_nlink(nlink);
149                child_node.inode.set_ino(first_ino);
150                indexes.push(child.node.clone());
151            } else {
152                child_node.inode.set_ino(index);
153                child_node.inode.set_nlink(1);
154                // Store inode real ino
155                bootstrap_ctx
156                    .inode_map
157                    .insert(key, vec![child.node.clone()]);
158            }
159
160            // update bootstrap_ctx.offset for rafs v6 non-dir nodes.
161            if !child_node.is_dir() && ctx.fs_version.is_v6() {
162                child_node.v6_set_offset(bootstrap_ctx, v6_hardlink_offset, block_size)?;
163            }
164            ctx.prefetch.insert(&child.node, child_node.deref());
165            if child_node.is_dir() {
166                dirs.push(child);
167            }
168        }
169
170        // According to filesystem semantics, a parent directory should have nlink equal to
171        // the number of its child directories plus 2.
172        if parent_node.is_dir() {
173            parent_node.inode.set_nlink((2 + dirs.len()) as u32);
174        }
175        for dir in dirs {
176            Self::build_rafs(ctx, bootstrap_ctx, dir)?;
177        }
178
179        Ok(())
180    }
181
182    /// Load a parent RAFS bootstrap and return the `Tree` object representing the filesystem.
183    pub fn load_parent_bootstrap(
184        ctx: &mut BuildContext,
185        bootstrap_mgr: &mut BootstrapManager,
186        blob_mgr: &mut BlobManager,
187    ) -> Result<Tree> {
188        let rs = if let Some(path) = bootstrap_mgr.f_parent_path.as_ref() {
189            RafsSuper::load_from_file(path, ctx.configuration.clone(), false).map(|(rs, _)| rs)?
190        } else {
191            return Err(Error::msg("bootstrap context's parent bootstrap is null"));
192        };
193
194        let config = RafsSuperConfig {
195            compressor: ctx.compressor,
196            digester: ctx.digester,
197            chunk_size: ctx.chunk_size,
198            batch_size: ctx.batch_size,
199            explicit_uidgid: ctx.explicit_uidgid,
200            version: ctx.fs_version,
201            is_tarfs_mode: rs.meta.flags.contains(RafsSuperFlags::TARTFS_MODE),
202        };
203        config.check_compatibility(&rs.meta)?;
204
205        // Reuse lower layer blob table,
206        // we need to append the blob entry of upper layer to the table
207        blob_mgr.extend_from_blob_table(ctx, rs.superblock.get_blob_infos())?;
208
209        // Build node tree of lower layer from a bootstrap file, and add chunks
210        // of lower node to layered_chunk_dict for chunk deduplication on next.
211        Tree::from_bootstrap(&rs, &mut blob_mgr.layered_chunk_dict)
212            .context("failed to build tree from bootstrap")
213    }
214}