nydus_builder/core/
tree.rs

1// Copyright 2020 Ant Group. All rights reserved.
2// Copyright 2023 Alibaba Cloud. All rights reserved.
3//
4// SPDX-License-Identifier: Apache-2.0
5
6//! An in-memory tree structure to maintain information for filesystem metadata.
7//!
8//! Steps to build the first layer for a Rafs image:
9//! - Build the upper tree (FileSystemTree) from the source directory.
10//! - Traverse the upper tree (FileSystemTree) to dump bootstrap and data blobs.
11//!
12//! Steps to build the second and following on layers for a Rafs image:
13//! - Build the upper tree (FileSystemTree) from the source directory.
14//! - Load the lower tree (MetadataTree) from a metadata blob.
15//! - Merge the final tree (OverlayTree) by applying the upper tree (FileSystemTree) to the
16//!   lower tree (MetadataTree).
17//! - Traverse the merged tree (OverlayTree) to dump bootstrap and data blobs.
18
19use std::cell::{RefCell, RefMut};
20use std::ffi::OsString;
21use std::os::unix::ffi::OsStrExt;
22use std::path::{Path, PathBuf};
23use std::rc::Rc;
24use std::sync::Arc;
25
26use anyhow::{bail, Result};
27use nydus_rafs::metadata::chunk::ChunkWrapper;
28use nydus_rafs::metadata::inode::InodeWrapper;
29use nydus_rafs::metadata::layout::{bytes_to_os_str, RafsXAttrs};
30use nydus_rafs::metadata::{Inode, RafsInodeExt, RafsSuper};
31use nydus_utils::{lazy_drop, root_tracer, timing_tracer};
32
33use super::node::{ChunkSource, Node, NodeChunk, NodeInfo};
34use super::overlay::{Overlay, WhiteoutType};
35use crate::core::overlay::OVERLAYFS_WHITEOUT_OPAQUE;
36use crate::{BuildContext, ChunkDict};
37
38/// Type alias for tree internal node.
39pub type TreeNode = Rc<RefCell<Node>>;
40
41/// An in-memory tree structure to maintain information and topology of filesystem nodes.
42#[derive(Clone)]
43pub struct Tree {
44    /// Filesystem node.
45    pub node: TreeNode,
46    /// Cached base name.
47    name: Vec<u8>,
48    /// Children tree nodes.
49    pub children: Vec<Tree>,
50}
51
52impl Tree {
53    /// Create a new instance of `Tree` from a filesystem node.
54    pub fn new(node: Node) -> Self {
55        let name = node.name().as_bytes().to_vec();
56        Tree {
57            node: Rc::new(RefCell::new(node)),
58            name,
59            children: Vec::new(),
60        }
61    }
62
63    /// Load a `Tree` from a bootstrap file, and optionally caches chunk information.
64    pub fn from_bootstrap<T: ChunkDict>(rs: &RafsSuper, chunk_dict: &mut T) -> Result<Self> {
65        let tree_builder = MetadataTreeBuilder::new(rs);
66        let root_ino = rs.superblock.root_ino();
67        let root_inode = rs.get_extended_inode(root_ino, true)?;
68        let root_node = MetadataTreeBuilder::parse_node(rs, root_inode, PathBuf::from("/"))?;
69        let mut tree = Tree::new(root_node);
70
71        tree.children = timing_tracer!(
72            { tree_builder.load_children(root_ino, Option::<PathBuf>::None, chunk_dict, true,) },
73            "load_tree_from_bootstrap"
74        )?;
75
76        Ok(tree)
77    }
78
79    /// Get name of the tree node.
80    pub fn name(&self) -> &[u8] {
81        &self.name
82    }
83
84    /// Set `Node` associated with the tree node.
85    pub fn set_node(&mut self, node: Node) {
86        self.node.replace(node);
87    }
88
89    /// Get mutably borrowed value to access the associated `Node` object.
90    pub fn borrow_mut_node(&self) -> RefMut<'_, Node> {
91        self.node.as_ref().borrow_mut()
92    }
93
94    /// Walk all nodes in DFS mode.
95    pub fn walk_dfs<F1, F2>(&self, pre: &mut F1, post: &mut F2) -> Result<()>
96    where
97        F1: FnMut(&Tree) -> Result<()>,
98        F2: FnMut(&Tree) -> Result<()>,
99    {
100        pre(self)?;
101        for child in &self.children {
102            child.walk_dfs(pre, post)?;
103        }
104        post(self)?;
105
106        Ok(())
107    }
108
109    /// Walk all nodes in pre DFS mode.
110    pub fn walk_dfs_pre<F>(&self, cb: &mut F) -> Result<()>
111    where
112        F: FnMut(&Tree) -> Result<()>,
113    {
114        self.walk_dfs(cb, &mut |_t| Ok(()))
115    }
116
117    /// Walk all nodes in post DFS mode.
118    pub fn walk_dfs_post<F>(&self, cb: &mut F) -> Result<()>
119    where
120        F: FnMut(&Tree) -> Result<()>,
121    {
122        self.walk_dfs(&mut |_t| Ok(()), cb)
123    }
124
125    /// Walk the tree in BFS mode.
126    pub fn walk_bfs<F>(&self, handle_self: bool, cb: &mut F) -> Result<()>
127    where
128        F: FnMut(&Tree) -> Result<()>,
129    {
130        if handle_self {
131            cb(self)?;
132        }
133
134        let mut dirs = Vec::with_capacity(32);
135        for child in &self.children {
136            cb(child)?;
137            if child.borrow_mut_node().is_dir() {
138                dirs.push(child);
139            }
140        }
141        for dir in dirs {
142            dir.walk_bfs(false, cb)?;
143        }
144
145        Ok(())
146    }
147
148    /// Insert a new child node into the tree.
149    pub fn insert_child(&mut self, child: Tree) {
150        if let Err(idx) = self
151            .children
152            .binary_search_by_key(&&child.name, |n| &n.name)
153        {
154            self.children.insert(idx, child);
155        }
156    }
157
158    /// Get index of child node with specified `name`.
159    pub fn get_child_idx(&self, name: &[u8]) -> Option<usize> {
160        self.children.binary_search_by_key(&name, |n| &n.name).ok()
161    }
162
163    /// Get the tree node corresponding to the path.
164    pub fn get_node(&self, path: &Path) -> Option<&Tree> {
165        let target_vec = Node::generate_target_vec(path);
166        assert!(!target_vec.is_empty());
167        let mut tree = self;
168        for name in &target_vec[1..] {
169            match tree.get_child_idx(name.as_bytes()) {
170                Some(idx) => tree = &tree.children[idx],
171                None => return None,
172            }
173        }
174        Some(tree)
175    }
176
177    /// Get the mutable tree node corresponding to the path.
178    pub fn get_node_mut(&mut self, path: &Path) -> Option<&mut Tree> {
179        let target_vec = Node::generate_target_vec(path);
180        assert!(!target_vec.is_empty());
181        let mut tree = self;
182
183        let last_idx = target_vec.len() - 1;
184        for name in &target_vec[1..last_idx] {
185            match tree.get_child_idx(name.as_bytes()) {
186                Some(idx) => tree = &mut tree.children[idx],
187                None => return None,
188            }
189        }
190
191        if let Some(last_name) = target_vec.last() {
192            match tree.get_child_idx(last_name.as_bytes()) {
193                Some(idx) => Some(&mut tree.children[idx]),
194                None => None,
195            }
196        } else {
197            Some(tree)
198        }
199    }
200
201    /// Merge the upper layer tree into the lower layer tree, applying whiteout rules.
202    pub fn merge_overaly(&mut self, ctx: &BuildContext, upper: Tree) -> Result<()> {
203        assert_eq!(self.name, "/".as_bytes());
204        assert_eq!(upper.name, "/".as_bytes());
205
206        // Handle the root node.
207        upper.borrow_mut_node().overlay = Overlay::UpperModification;
208        self.node = upper.node.clone();
209        self.merge_children(ctx, &upper)?;
210        lazy_drop(upper);
211
212        Ok(())
213    }
214
215    fn merge_children(&mut self, ctx: &BuildContext, upper: &Tree) -> Result<()> {
216        // Handle whiteout nodes in the first round, and handle other nodes in the second round.
217        let mut modified = Vec::with_capacity(upper.children.len());
218        for u in upper.children.iter() {
219            let mut u_node = u.borrow_mut_node();
220            match u_node.whiteout_type(ctx.whiteout_spec) {
221                Some(WhiteoutType::OciRemoval) => {
222                    if let Some(origin_name) = u_node.origin_name(WhiteoutType::OciRemoval) {
223                        if let Some(idx) = self.get_child_idx(origin_name.as_bytes()) {
224                            self.children.remove(idx);
225                        }
226                    }
227                }
228                Some(WhiteoutType::OciOpaque) => {
229                    self.children.clear();
230                }
231                Some(WhiteoutType::OverlayFsRemoval) => {
232                    if let Some(idx) = self.get_child_idx(&u.name) {
233                        self.children.remove(idx);
234                    }
235                }
236                Some(WhiteoutType::OverlayFsOpaque) => {
237                    if let Some(idx) = self.get_child_idx(&u.name) {
238                        self.children[idx].children.clear();
239                    }
240                    u_node.remove_xattr(&OsString::from(OVERLAYFS_WHITEOUT_OPAQUE));
241                    modified.push(u);
242                }
243                None => modified.push(u),
244            }
245        }
246
247        let mut dirs = Vec::new();
248        for u in modified {
249            let mut u_node = u.borrow_mut_node();
250            if let Some(idx) = self.get_child_idx(&u.name) {
251                u_node.overlay = Overlay::UpperModification;
252                self.children[idx].node = u.node.clone();
253            } else {
254                u_node.overlay = Overlay::UpperAddition;
255                self.insert_child(Tree {
256                    node: u.node.clone(),
257                    name: u.name.clone(),
258                    children: vec![],
259                });
260            }
261            if u_node.is_dir() {
262                dirs.push(u);
263            }
264        }
265        for dir in dirs {
266            if let Some(idx) = self.get_child_idx(&dir.name) {
267                self.children[idx].merge_children(ctx, dir)?;
268            } else {
269                bail!("builder: can not find directory in merged tree");
270            }
271        }
272
273        Ok(())
274    }
275}
276
277pub struct MetadataTreeBuilder<'a> {
278    rs: &'a RafsSuper,
279}
280
281impl<'a> MetadataTreeBuilder<'a> {
282    fn new(rs: &'a RafsSuper) -> Self {
283        Self { rs }
284    }
285
286    /// Build node tree by loading bootstrap file
287    fn load_children<T: ChunkDict, P: AsRef<Path>>(
288        &self,
289        ino: Inode,
290        parent: Option<P>,
291        chunk_dict: &mut T,
292        validate_digest: bool,
293    ) -> Result<Vec<Tree>> {
294        let inode = self.rs.get_extended_inode(ino, validate_digest)?;
295        if !inode.is_dir() {
296            return Ok(Vec::new());
297        }
298
299        let parent_path = if let Some(parent) = parent {
300            parent.as_ref().join(inode.name())
301        } else {
302            PathBuf::from("/")
303        };
304
305        let blobs = self.rs.superblock.get_blob_infos();
306        let child_count = inode.get_child_count();
307        let mut children = Vec::with_capacity(child_count as usize);
308        for idx in 0..child_count {
309            let child = inode.get_child_by_index(idx)?;
310            let child_path = parent_path.join(child.name());
311            let child = Self::parse_node(self.rs, child.clone(), child_path)?;
312
313            if child.is_reg() {
314                for chunk in &child.chunks {
315                    let blob_idx = chunk.inner.blob_index();
316                    if let Some(blob) = blobs.get(blob_idx as usize) {
317                        chunk_dict.add_chunk(chunk.inner.clone(), blob.digester());
318                    }
319                }
320            }
321
322            let child = Tree::new(child);
323            children.push(child);
324        }
325        children.sort_unstable_by(|a, b| a.name.cmp(&b.name));
326
327        for child in children.iter_mut() {
328            let child_node = child.borrow_mut_node();
329            if child_node.is_dir() {
330                let child_ino = child_node.inode.ino();
331                drop(child_node);
332                child.children =
333                    self.load_children(child_ino, Some(&parent_path), chunk_dict, validate_digest)?;
334            }
335        }
336
337        Ok(children)
338    }
339
340    /// Convert a `RafsInode` object to an in-memory `Node` object.
341    pub fn parse_node(rs: &RafsSuper, inode: Arc<dyn RafsInodeExt>, path: PathBuf) -> Result<Node> {
342        let chunks = if inode.is_reg() {
343            let chunk_count = inode.get_chunk_count();
344            let mut chunks = Vec::with_capacity(chunk_count as usize);
345            for i in 0..chunk_count {
346                let cki = inode.get_chunk_info(i)?;
347                chunks.push(NodeChunk {
348                    source: ChunkSource::Parent,
349                    inner: Arc::new(ChunkWrapper::from_chunk_info(cki)),
350                });
351            }
352            chunks
353        } else {
354            Vec::new()
355        };
356
357        let symlink = if inode.is_symlink() {
358            Some(inode.get_symlink()?)
359        } else {
360            None
361        };
362
363        let mut xattrs = RafsXAttrs::new();
364        for name in inode.get_xattrs()? {
365            let name = bytes_to_os_str(&name);
366            let value = inode.get_xattr(name)?;
367            xattrs.add(name.to_os_string(), value.unwrap_or_default())?;
368        }
369
370        // Nodes loaded from bootstrap will only be used as `Overlay::Lower`, so make `dev` invalid
371        // to avoid breaking hardlink detecting logic.
372        let src_dev = u64::MAX;
373        let rdev = inode.rdev() as u64;
374        let inode = InodeWrapper::from_inode_info(inode.clone());
375        let source = PathBuf::from("/");
376        let target = Node::generate_target(&path, &source);
377        let target_vec = Node::generate_target_vec(&target);
378        let info = NodeInfo {
379            explicit_uidgid: rs.meta.explicit_uidgid(),
380            src_ino: inode.ino(),
381            src_dev,
382            rdev,
383            path,
384            source,
385            target,
386            target_vec,
387            symlink,
388            xattrs,
389            v6_force_extended_inode: false,
390        };
391
392        Ok(Node {
393            info: Arc::new(info),
394            index: 0,
395            layer_idx: 0,
396            overlay: Overlay::Lower,
397            inode,
398            chunks,
399            v6_offset: 0,
400            v6_dirents: Vec::new(),
401            v6_datalayout: 0,
402            v6_compact_inode: false,
403            v6_dirents_offset: 0,
404        })
405    }
406}
407
408#[cfg(test)]
409mod tests {
410    use super::*;
411    use nydus_rafs::metadata::RafsVersion;
412    use nydus_storage::RAFS_DEFAULT_CHUNK_SIZE;
413    use vmm_sys_util::tempdir::TempDir;
414    use vmm_sys_util::tempfile::TempFile;
415
416    #[test]
417    fn test_set_lock_node() {
418        let tmpdir = TempDir::new().unwrap();
419        let tmpfile = TempFile::new_in(tmpdir.as_path()).unwrap();
420        let node = Node::from_fs_object(
421            RafsVersion::V6,
422            tmpdir.as_path().to_path_buf(),
423            tmpfile.as_path().to_path_buf(),
424            Overlay::UpperAddition,
425            RAFS_DEFAULT_CHUNK_SIZE as u32,
426            0,
427            true,
428            false,
429        )
430        .unwrap();
431        let mut tree = Tree::new(node);
432        assert_eq!(tree.name, tmpfile.as_path().file_name().unwrap().as_bytes());
433        let node1 = tree.borrow_mut_node();
434        drop(node1);
435
436        let tmpfile = TempFile::new_in(tmpdir.as_path()).unwrap();
437        let node = Node::from_fs_object(
438            RafsVersion::V6,
439            tmpdir.as_path().to_path_buf(),
440            tmpfile.as_path().to_path_buf(),
441            Overlay::UpperAddition,
442            RAFS_DEFAULT_CHUNK_SIZE as u32,
443            0,
444            true,
445            false,
446        )
447        .unwrap();
448        tree.set_node(node);
449        let node2 = tree.borrow_mut_node();
450        assert_eq!(node2.name(), tmpfile.as_path().file_name().unwrap());
451    }
452
453    #[test]
454    fn test_walk_tree() {
455        let tmpdir = TempDir::new().unwrap();
456        let tmpfile = TempFile::new_in(tmpdir.as_path()).unwrap();
457        let node = Node::from_fs_object(
458            RafsVersion::V6,
459            tmpdir.as_path().to_path_buf(),
460            tmpfile.as_path().to_path_buf(),
461            Overlay::UpperAddition,
462            RAFS_DEFAULT_CHUNK_SIZE as u32,
463            0,
464            true,
465            false,
466        )
467        .unwrap();
468        let mut tree = Tree::new(node);
469
470        let tmpfile2 = TempFile::new_in(tmpdir.as_path()).unwrap();
471        let node = Node::from_fs_object(
472            RafsVersion::V6,
473            tmpdir.as_path().to_path_buf(),
474            tmpfile2.as_path().to_path_buf(),
475            Overlay::UpperAddition,
476            RAFS_DEFAULT_CHUNK_SIZE as u32,
477            0,
478            true,
479            false,
480        )
481        .unwrap();
482        let tree2 = Tree::new(node);
483        tree.insert_child(tree2);
484
485        let tmpfile3 = TempFile::new_in(tmpdir.as_path()).unwrap();
486        let node = Node::from_fs_object(
487            RafsVersion::V6,
488            tmpdir.as_path().to_path_buf(),
489            tmpfile3.as_path().to_path_buf(),
490            Overlay::UpperAddition,
491            RAFS_DEFAULT_CHUNK_SIZE as u32,
492            0,
493            true,
494            false,
495        )
496        .unwrap();
497        let tree3 = Tree::new(node);
498        tree.insert_child(tree3);
499
500        let mut count = 0;
501        tree.walk_bfs(true, &mut |_n| -> Result<()> {
502            count += 1;
503            Ok(())
504        })
505        .unwrap();
506        assert_eq!(count, 3);
507
508        let mut count = 0;
509        tree.walk_bfs(false, &mut |_n| -> Result<()> {
510            count += 1;
511            Ok(())
512        })
513        .unwrap();
514        assert_eq!(count, 2);
515
516        let mut count = 0;
517        tree.walk_bfs(true, &mut |_n| -> Result<()> {
518            count += 1;
519            bail!("test")
520        })
521        .unwrap_err();
522        assert_eq!(count, 1);
523
524        let idx = tree
525            .get_child_idx(tmpfile2.as_path().file_name().unwrap().as_bytes())
526            .unwrap();
527        assert!(idx == 0 || idx == 1);
528        let idx = tree
529            .get_child_idx(tmpfile3.as_path().file_name().unwrap().as_bytes())
530            .unwrap();
531        assert!(idx == 0 || idx == 1);
532    }
533}