1use std::any::Any;
9use std::collections::{HashMap, HashSet};
10use std::convert::{TryFrom, TryInto};
11use std::ffi::{OsStr, OsString};
12use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
13use std::fs::OpenOptions;
14use std::io::{Error, Result};
15use std::os::unix::ffi::OsStrExt;
16use std::path::{Component, Path, PathBuf};
17use std::str::FromStr;
18use std::sync::Arc;
19use std::time::Duration;
20use thiserror::Error;
21
22use anyhow::{bail, ensure};
23use fuse_backend_rs::abi::fuse_abi::Attr;
24use fuse_backend_rs::api::filesystem::Entry;
25use nydus_api::{ConfigV2, RafsConfigV2};
26use nydus_storage::device::{
27 BlobChunkInfo, BlobDevice, BlobFeatures, BlobInfo, BlobIoMerge, BlobIoVec,
28};
29use nydus_storage::meta::toc::TocEntryList;
30use nydus_utils::digest::{self, RafsDigest};
31use nydus_utils::{compress, crypt};
32use serde::Serialize;
33
34use self::layout::v5::RafsV5PrefetchTable;
35use self::layout::v6::RafsV6PrefetchTable;
36use self::layout::{XattrName, XattrValue, RAFS_SUPER_VERSION_V5, RAFS_SUPER_VERSION_V6};
37use self::noop::NoopSuperBlock;
38use crate::fs::{RAFS_DEFAULT_ATTR_TIMEOUT, RAFS_DEFAULT_ENTRY_TIMEOUT};
39use crate::{RafsError, RafsIoReader, RafsIoWrite, RafsResult};
40
41mod md_v5;
42mod md_v6;
43mod noop;
44
45pub mod cached_v5;
46pub mod chunk;
47pub mod direct_v5;
48pub mod direct_v6;
49pub mod inode;
50pub mod layout;
51
52pub use nydus_storage::{RAFS_DEFAULT_CHUNK_SIZE, RAFS_MAX_CHUNK_SIZE};
54
55pub const RAFS_BLOB_ID_MAX_LENGTH: usize = 64;
57pub const RAFS_ATTR_BLOCK_SIZE: u32 = 4096;
59pub const RAFS_MAX_NAME: usize = 255;
61pub const RAFS_MAX_METADATA_SIZE: usize = 0x8000_0000;
63pub const DOT: &str = ".";
65pub const DOTDOT: &str = "..";
67
68pub type Inode = u64;
70pub type ArcRafsInodeExt = Arc<dyn RafsInodeExt>;
71
72#[derive(Debug, Clone)]
73pub struct RafsBlobExtraInfo {
74 pub mapped_blkaddr: u32,
78}
79
80pub trait RafsSuperInodes {
82 fn get_max_ino(&self) -> Inode;
84
85 fn get_inode(&self, ino: Inode, validate_inode: bool) -> Result<Arc<dyn RafsInode>>;
87
88 fn get_extended_inode(&self, ino: Inode, validate_inode: bool)
90 -> Result<Arc<dyn RafsInodeExt>>;
91}
92
93pub trait RafsSuperBlock: RafsSuperInodes + Send + Sync {
95 fn load(&mut self, r: &mut RafsIoReader) -> Result<()>;
97
98 fn update(&self, r: &mut RafsIoReader) -> RafsResult<()>;
100
101 fn destroy(&mut self);
103
104 fn get_blob_infos(&self) -> Vec<Arc<BlobInfo>>;
106
107 fn get_blob_extra_infos(&self) -> Result<HashMap<String, RafsBlobExtraInfo>> {
109 Ok(HashMap::new())
110 }
111
112 fn root_ino(&self) -> u64;
114
115 fn get_chunk_info(&self, _idx: usize) -> Result<Arc<dyn BlobChunkInfo>>;
117
118 fn set_blob_device(&self, blob_device: BlobDevice);
120}
121
122pub enum RafsInodeWalkAction {
124 Continue,
126 Break,
128}
129
130pub type RafsInodeWalkHandler<'a> = &'a mut dyn FnMut(
132 Option<Arc<dyn RafsInode>>,
133 OsString,
134 u64,
135 u64,
136) -> Result<RafsInodeWalkAction>;
137
138pub trait RafsInode: Any {
144 fn validate(&self, max_inode: Inode, chunk_size: u64) -> Result<()>;
149
150 fn alloc_bio_vecs(
152 &self,
153 device: &BlobDevice,
154 offset: u64,
155 size: usize,
156 user_io: bool,
157 ) -> Result<Vec<BlobIoVec>>;
158
159 fn collect_descendants_inodes(
161 &self,
162 descendants: &mut Vec<Arc<dyn RafsInode>>,
163 ) -> Result<usize>;
164
165 fn get_entry(&self) -> Entry;
167
168 fn get_attr(&self) -> Attr;
170
171 fn ino(&self) -> u64;
173
174 fn rdev(&self) -> u32;
176
177 fn projid(&self) -> u32;
179
180 fn is_blkdev(&self) -> bool;
182
183 fn is_chrdev(&self) -> bool;
185
186 fn is_sock(&self) -> bool;
188
189 fn is_fifo(&self) -> bool;
191
192 fn is_dir(&self) -> bool;
194
195 fn is_symlink(&self) -> bool;
197
198 fn is_reg(&self) -> bool;
200
201 fn is_hardlink(&self) -> bool;
203
204 fn has_xattr(&self) -> bool;
206
207 fn get_xattr(&self, name: &OsStr) -> Result<Option<XattrValue>>;
209
210 fn get_xattrs(&self) -> Result<Vec<XattrName>>;
212
213 fn get_symlink(&self) -> Result<OsString>;
215
216 fn get_symlink_size(&self) -> u16;
218
219 fn walk_children_inodes(&self, entry_offset: u64, handler: RafsInodeWalkHandler) -> Result<()>;
221
222 fn get_child_by_name(&self, name: &OsStr) -> Result<Arc<dyn RafsInodeExt>>;
224
225 fn get_child_by_index(&self, idx: u32) -> Result<Arc<dyn RafsInodeExt>>;
227
228 fn get_child_count(&self) -> u32;
230
231 fn get_child_index(&self) -> Result<u32>;
233
234 fn size(&self) -> u64;
236
237 fn is_empty_size(&self) -> bool {
239 self.size() == 0
240 }
241
242 fn get_chunk_count(&self) -> u32;
244
245 fn as_any(&self) -> &dyn Any;
246}
247
248pub trait RafsInodeExt: RafsInode {
250 fn as_inode(&self) -> &dyn RafsInode;
252
253 fn parent(&self) -> u64;
255
256 fn name(&self) -> OsString;
258
259 fn get_name_size(&self) -> u16;
261
262 fn flags(&self) -> u64;
264
265 fn get_digest(&self) -> RafsDigest;
267
268 fn get_chunk_info(&self, idx: u32) -> Result<Arc<dyn BlobChunkInfo>>;
270}
271
272pub trait RafsStore {
274 fn store(&self, w: &mut dyn RafsIoWrite) -> Result<usize>;
276}
277
278bitflags! {
279 #[derive(Serialize)]
281 pub struct RafsSuperFlags: u64 {
282 const COMPRESSION_NONE = 0x0000_0001;
284 const COMPRESSION_LZ4 = 0x0000_0002;
286 const HASH_BLAKE3 = 0x0000_0004;
288 const HASH_SHA256 = 0x0000_0008;
290 const EXPLICIT_UID_GID = 0x0000_0010;
294 const HAS_XATTR = 0x0000_0020;
296 const COMPRESSION_GZIP = 0x0000_0040;
298 const COMPRESSION_ZSTD = 0x0000_0080;
300 const INLINED_CHUNK_DIGEST = 0x0000_0100;
302 const TARTFS_MODE = 0x0000_0200;
304 const ENCRYPTION_NONE = 0x0100_0000;
306 const ENCRYPTION_ASE_128_XTS = 0x0200_0000;
308
309 const PRESERVED_COMPAT_5 = 0x0400_0000;
311 const PRESERVED_COMPAT_4 = 0x0800_0000;
312 const PRESERVED_COMPAT_3 = 0x1000_0000;
313 const PRESERVED_COMPAT_2 = 0x2000_0000;
314 const PRESERVED_COMPAT_1 = 0x4000_0000;
315 const PRESERVED_COMPAT_0 = 0x8000_0000;
316 }
317}
318
319impl Default for RafsSuperFlags {
320 fn default() -> Self {
321 RafsSuperFlags::empty()
322 }
323}
324
325impl Display for RafsSuperFlags {
326 fn fmt(&self, f: &mut Formatter) -> FmtResult {
327 write!(f, "{:?}", self)?;
328 Ok(())
329 }
330}
331
332impl From<RafsSuperFlags> for digest::Algorithm {
333 fn from(flags: RafsSuperFlags) -> Self {
334 match flags {
335 x if x.contains(RafsSuperFlags::HASH_BLAKE3) => digest::Algorithm::Blake3,
336 x if x.contains(RafsSuperFlags::HASH_SHA256) => digest::Algorithm::Sha256,
337 _ => digest::Algorithm::Blake3,
338 }
339 }
340}
341
342impl From<digest::Algorithm> for RafsSuperFlags {
343 fn from(d: digest::Algorithm) -> RafsSuperFlags {
344 match d {
345 digest::Algorithm::Blake3 => RafsSuperFlags::HASH_BLAKE3,
346 digest::Algorithm::Sha256 => RafsSuperFlags::HASH_SHA256,
347 }
348 }
349}
350
351impl From<RafsSuperFlags> for compress::Algorithm {
352 fn from(flags: RafsSuperFlags) -> Self {
353 match flags {
354 x if x.contains(RafsSuperFlags::COMPRESSION_NONE) => compress::Algorithm::None,
355 x if x.contains(RafsSuperFlags::COMPRESSION_LZ4) => compress::Algorithm::Lz4Block,
356 x if x.contains(RafsSuperFlags::COMPRESSION_GZIP) => compress::Algorithm::GZip,
357 x if x.contains(RafsSuperFlags::COMPRESSION_ZSTD) => compress::Algorithm::Zstd,
358 _ => compress::Algorithm::Lz4Block,
359 }
360 }
361}
362
363impl From<compress::Algorithm> for RafsSuperFlags {
364 fn from(c: compress::Algorithm) -> RafsSuperFlags {
365 match c {
366 compress::Algorithm::None => RafsSuperFlags::COMPRESSION_NONE,
367 compress::Algorithm::Lz4Block => RafsSuperFlags::COMPRESSION_LZ4,
368 compress::Algorithm::GZip => RafsSuperFlags::COMPRESSION_GZIP,
369 compress::Algorithm::Zstd => RafsSuperFlags::COMPRESSION_ZSTD,
370 }
371 }
372}
373
374impl From<RafsSuperFlags> for crypt::Algorithm {
375 fn from(flags: RafsSuperFlags) -> Self {
376 match flags {
377 x if x.contains(RafsSuperFlags::ENCRYPTION_ASE_128_XTS) => crypt::Algorithm::Aes128Xts,
379 _ => crypt::Algorithm::None,
380 }
381 }
382}
383
384impl From<crypt::Algorithm> for RafsSuperFlags {
385 fn from(c: crypt::Algorithm) -> RafsSuperFlags {
386 match c {
387 crypt::Algorithm::Aes128Xts => RafsSuperFlags::ENCRYPTION_ASE_128_XTS,
389 _ => RafsSuperFlags::ENCRYPTION_NONE,
390 }
391 }
392}
393
394#[derive(Clone, Copy, Debug)]
396pub struct RafsSuperConfig {
397 pub version: RafsVersion,
399 pub compressor: compress::Algorithm,
401 pub digester: digest::Algorithm,
403 pub chunk_size: u32,
405 pub batch_size: u32,
407 pub explicit_uidgid: bool,
409 pub is_tarfs_mode: bool,
411}
412
413#[derive(Error, Debug)]
414pub enum MergeError {
415 #[error("Inconsistent RAFS Filesystem: {0}")]
416 InconsistentFilesystem(String),
417 #[error(transparent)]
418 Other(#[from] anyhow::Error),
419}
420
421impl RafsSuperConfig {
422 pub fn check_compatibility(&self, meta: &RafsSuperMeta) -> anyhow::Result<()> {
424 ensure!(
425 self.chunk_size == meta.chunk_size,
426 MergeError::InconsistentFilesystem(format!(
427 "Inconsistent configuration of chunk_size: {} vs {}",
428 self.chunk_size, meta.chunk_size
429 ))
430 );
431
432 ensure!(
433 self.explicit_uidgid == meta.explicit_uidgid(),
434 MergeError::InconsistentFilesystem(format!(
435 "Using inconsistent explicit_uidgid setting {:?}, target explicit_uidgid setting {:?}",
436 self.explicit_uidgid,
437 meta.explicit_uidgid()
438 ))
439 );
440
441 let meta_version = RafsVersion::try_from(meta.version);
442 ensure!(
443 u32::from(self.version) == meta.version,
444 MergeError::InconsistentFilesystem(format!(
445 "Using inconsistent RAFS version {:?}, target RAFS version {:?}",
446 self.version, meta_version
447 ))
448 );
449
450 ensure!(
451 self.version != RafsVersion::V5 || self.digester == meta.get_digester(),
452 MergeError::InconsistentFilesystem(format!(
453 "RAFS v5 can not support different digest algorithm due to inode digest, {} vs {}",
454 self.digester,
455 meta.get_digester()
456 ))
457 );
458 let is_tarfs_mode = meta.flags.contains(RafsSuperFlags::TARTFS_MODE);
459 ensure!(
460 is_tarfs_mode == self.is_tarfs_mode,
461 MergeError::InconsistentFilesystem("Using inconsistent RAFS TARFS mode".to_string(),)
462 );
463
464 Ok(())
465 }
466}
467
468#[derive(Clone, Copy, Debug, Serialize)]
470pub struct RafsSuperMeta {
471 pub magic: u32,
473 pub version: u32,
475 pub sb_size: u32,
477 pub root_inode: Inode,
479 pub chunk_size: u32,
481 pub batch_size: u32,
483 pub inodes_count: u64,
485 pub flags: RafsSuperFlags,
487 pub inode_table_entries: u32,
489 pub inode_table_offset: u64,
491 pub blob_table_size: u32,
493 pub blob_table_offset: u64,
495 pub extended_blob_table_offset: u64,
497 pub extended_blob_table_entries: u32,
499 pub blob_device_table_count: u32,
501 pub blob_device_table_offset: u64,
503 pub prefetch_table_offset: u64,
505 pub prefetch_table_entries: u32,
507 pub attr_timeout: Duration,
509 pub entry_timeout: Duration,
511 pub is_chunk_dict: bool,
513 pub meta_blkaddr: u32,
515 pub root_nid: u16,
517 pub chunk_table_offset: u64,
519 pub chunk_table_size: u64,
521}
522
523impl RafsSuperMeta {
524 pub fn is_v5(&self) -> bool {
526 self.version == RAFS_SUPER_VERSION_V5
527 }
528
529 pub fn is_v6(&self) -> bool {
531 self.version == RAFS_SUPER_VERSION_V6
532 }
533
534 pub fn is_chunk_dict(&self) -> bool {
536 self.is_chunk_dict
537 }
538
539 pub fn explicit_uidgid(&self) -> bool {
541 self.flags.contains(RafsSuperFlags::EXPLICIT_UID_GID)
542 }
543
544 pub fn has_xattr(&self) -> bool {
546 self.flags.contains(RafsSuperFlags::HAS_XATTR)
547 }
548
549 pub fn has_inlined_chunk_digest(&self) -> bool {
551 self.is_v6() && self.flags.contains(RafsSuperFlags::INLINED_CHUNK_DIGEST)
552 }
553
554 pub fn get_compressor(&self) -> compress::Algorithm {
556 if self.is_v5() || self.is_v6() {
557 self.flags.into()
558 } else {
559 compress::Algorithm::None
560 }
561 }
562
563 pub fn get_digester(&self) -> digest::Algorithm {
565 if self.is_v5() || self.is_v6() {
566 self.flags.into()
567 } else {
568 digest::Algorithm::Blake3
569 }
570 }
571
572 pub fn get_cipher(&self) -> crypt::Algorithm {
574 if self.is_v6() {
575 self.flags.into()
576 } else {
577 crypt::Algorithm::None
578 }
579 }
580
581 pub fn get_config(&self) -> RafsSuperConfig {
583 RafsSuperConfig {
584 version: self.version.try_into().unwrap_or_default(),
585 compressor: self.get_compressor(),
586 digester: self.get_digester(),
587 chunk_size: self.chunk_size,
588 batch_size: self.batch_size,
589 explicit_uidgid: self.explicit_uidgid(),
590 is_tarfs_mode: self.flags.contains(RafsSuperFlags::TARTFS_MODE),
591 }
592 }
593}
594
595impl Default for RafsSuperMeta {
596 fn default() -> Self {
597 RafsSuperMeta {
598 magic: 0,
599 version: 0,
600 sb_size: 0,
601 inodes_count: 0,
602 root_inode: 0,
603 chunk_size: 0,
604 batch_size: 0,
605 flags: RafsSuperFlags::empty(),
606 inode_table_entries: 0,
607 inode_table_offset: 0,
608 blob_table_size: 0,
609 blob_table_offset: 0,
610 extended_blob_table_offset: 0,
611 extended_blob_table_entries: 0,
612 blob_device_table_count: 0,
613 blob_device_table_offset: 0,
614 prefetch_table_offset: 0,
615 prefetch_table_entries: 0,
616 attr_timeout: Duration::from_secs(RAFS_DEFAULT_ATTR_TIMEOUT),
617 entry_timeout: Duration::from_secs(RAFS_DEFAULT_ENTRY_TIMEOUT),
618 meta_blkaddr: 0,
619 root_nid: 0,
620 is_chunk_dict: false,
621 chunk_table_offset: 0,
622 chunk_table_size: 0,
623 }
624 }
625}
626
627#[derive(Clone, Copy, Debug, Default, PartialEq)]
629pub enum RafsVersion {
630 #[default]
632 V5,
633 V6,
635}
636
637impl TryFrom<u32> for RafsVersion {
638 type Error = Error;
639
640 fn try_from(version: u32) -> std::result::Result<Self, Self::Error> {
641 if version == RAFS_SUPER_VERSION_V5 {
642 return Ok(RafsVersion::V5);
643 } else if version == RAFS_SUPER_VERSION_V6 {
644 return Ok(RafsVersion::V6);
645 }
646 Err(einval!(format!("invalid RAFS version number {}", version)))
647 }
648}
649
650impl From<RafsVersion> for u32 {
651 fn from(v: RafsVersion) -> Self {
652 match v {
653 RafsVersion::V5 => RAFS_SUPER_VERSION_V5,
654 RafsVersion::V6 => RAFS_SUPER_VERSION_V6,
655 }
656 }
657}
658
659impl std::fmt::Display for RafsVersion {
660 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661 match self {
662 RafsVersion::V5 => write!(f, "5"),
663 RafsVersion::V6 => write!(f, "6"),
664 }
665 }
666}
667
668impl RafsVersion {
669 pub fn is_v5(&self) -> bool {
671 self == &Self::V5
672 }
673
674 pub fn is_v6(&self) -> bool {
676 self == &Self::V6
677 }
678}
679
680#[derive(Clone, Debug, Default, Eq, PartialEq)]
682pub enum RafsMode {
683 #[default]
685 Direct,
686 Cached,
688}
689
690impl FromStr for RafsMode {
691 type Err = Error;
692
693 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
694 match s {
695 "direct" => Ok(Self::Direct),
696 "cached" => Ok(Self::Cached),
697 _ => Err(einval!("rafs mode should be direct or cached")),
698 }
699 }
700}
701
702impl Display for RafsMode {
703 fn fmt(&self, f: &mut Formatter) -> FmtResult {
704 match self {
705 Self::Direct => write!(f, "direct"),
706 Self::Cached => write!(f, "cached"),
707 }
708 }
709}
710
711pub struct RafsSuper {
713 pub mode: RafsMode,
715 pub validate_digest: bool,
717 pub meta: RafsSuperMeta,
719 pub superblock: Arc<dyn RafsSuperBlock>,
721}
722
723impl Default for RafsSuper {
724 fn default() -> Self {
725 Self {
726 mode: RafsMode::Direct,
727 validate_digest: false,
728 meta: RafsSuperMeta::default(),
729 superblock: Arc::new(NoopSuperBlock::new()),
730 }
731 }
732}
733
734impl RafsSuper {
735 pub fn new(conf: &RafsConfigV2) -> Result<Self> {
737 Ok(Self {
738 mode: RafsMode::from_str(conf.mode.as_str())?,
739 validate_digest: conf.validate,
740 ..Default::default()
741 })
742 }
743
744 pub fn destroy(&mut self) {
746 Arc::get_mut(&mut self.superblock)
747 .expect("Inodes are no longer used.")
748 .destroy();
749 }
750
751 pub fn load_from_file<P: AsRef<Path>>(
753 path: P,
754 config: Arc<ConfigV2>,
755 is_chunk_dict: bool,
756 ) -> Result<(Self, RafsIoReader)> {
757 let validate_digest = config
758 .rafs
759 .as_ref()
760 .map(|rafs| rafs.validate)
761 .unwrap_or_default();
762 let mut rs = RafsSuper {
763 mode: RafsMode::Direct,
764 validate_digest,
765 ..Default::default()
766 };
767 rs.meta.is_chunk_dict = is_chunk_dict;
768
769 let file = OpenOptions::new()
771 .read(true)
772 .write(false)
773 .open(path.as_ref())?;
774 let mut reader = Box::new(file) as RafsIoReader;
775 let mut blob_accessible = config.internal.blob_accessible();
776
777 if let Err(e) = rs.load(&mut reader) {
778 let id = BlobInfo::get_blob_id_from_meta_path(path.as_ref())?;
779 let new_path = match TocEntryList::extract_rafs_meta(&id, config.clone()) {
780 Ok(v) => v,
781 Err(_e) => {
782 debug!("failed to load inlined RAFS meta, {}", _e);
783 return Err(e);
784 }
785 };
786 let file = OpenOptions::new().read(true).write(false).open(new_path)?;
787 reader = Box::new(file) as RafsIoReader;
788 rs.load(&mut reader)?;
789 rs.set_blob_id_from_meta_path(path.as_ref())?;
790 blob_accessible = true;
791 } else {
792 let blobs = rs.superblock.get_blob_infos();
797 for blob in blobs.iter() {
798 if blob.has_feature(BlobFeatures::INLINED_FS_META) {
800 blob.set_blob_id_from_meta_path(path.as_ref())?;
801 }
802 }
803 }
804
805 if !config.is_fs_cache()
806 && blob_accessible
807 && (validate_digest || config.is_chunk_validation_enabled())
808 && rs.meta.has_inlined_chunk_digest()
809 {
810 rs.create_blob_device(config)?;
811 }
812
813 Ok((rs, reader))
814 }
815
816 pub(crate) fn load(&mut self, r: &mut RafsIoReader) -> Result<()> {
818 if self.try_load_v5(r)? {
820 return Ok(());
821 }
822
823 if self.try_load_v6(r)? {
824 return Ok(());
825 }
826
827 Err(Error::other("invalid RAFS superblock"))
828 }
829
830 pub fn set_blob_id_from_meta_path(&self, meta_path: &Path) -> Result<()> {
834 let blobs = self.superblock.get_blob_infos();
835 for blob in blobs.iter() {
836 if blob.has_feature(BlobFeatures::INLINED_FS_META)
837 || !blob.has_feature(BlobFeatures::CAP_TAR_TOC)
838 {
839 blob.set_blob_id_from_meta_path(meta_path)?;
840 }
841 }
842 Ok(())
843 }
844
845 pub fn create_blob_device(&self, config: Arc<ConfigV2>) -> Result<()> {
849 let blobs = self.superblock.get_blob_infos();
850 let device = BlobDevice::new(&config, &blobs)?;
851 self.superblock.set_blob_device(device);
852 Ok(())
853 }
854
855 pub fn update(&self, r: &mut RafsIoReader) -> RafsResult<()> {
857 if self.meta.is_v5() {
858 self.skip_v5_superblock(r)
859 .map_err(RafsError::FillSuperBlock)?;
860 }
861
862 self.superblock.update(r)
863 }
864
865 pub fn get_max_ino(&self) -> Inode {
867 self.superblock.get_max_ino()
868 }
869
870 pub fn get_inode(&self, ino: Inode, validate_inode: bool) -> Result<Arc<dyn RafsInode>> {
872 self.superblock.get_inode(ino, validate_inode)
873 }
874
875 pub fn get_extended_inode(
877 &self,
878 ino: Inode,
879 validate_inode: bool,
880 ) -> Result<Arc<dyn RafsInodeExt>> {
881 self.superblock.get_extended_inode(ino, validate_inode)
882 }
883
884 pub fn ino_from_path(&self, f: &Path) -> Result<Inode> {
886 let root_ino = self.superblock.root_ino();
887 if f == Path::new("/") {
888 return Ok(root_ino);
889 } else if !f.starts_with("/") {
890 return Err(einval!());
891 }
892
893 let entries = f
894 .components()
895 .filter(|comp| *comp != Component::RootDir)
896 .map(|comp| match comp {
897 Component::Normal(name) => Some(name),
898 Component::ParentDir => Some(OsStr::from_bytes(DOTDOT.as_bytes())),
899 Component::CurDir => Some(OsStr::from_bytes(DOT.as_bytes())),
900 _ => None,
901 })
902 .collect::<Vec<_>>();
903 if entries.is_empty() {
904 warn!("Path can't be parsed {:?}", f);
905 return Err(enoent!());
906 }
907
908 let mut parent = self.get_extended_inode(root_ino, self.validate_digest)?;
909 for p in entries {
910 match p {
911 None => {
912 error!("Illegal specified path {:?}", f);
913 return Err(einval!());
914 }
915 Some(name) => {
916 parent = parent.get_child_by_name(name).map_err(|e| {
917 warn!("File {:?} not in RAFS filesystem, {}", name, e);
918 enoent!()
919 })?;
920 }
921 }
922 }
923
924 Ok(parent.ino())
925 }
926
927 pub fn prefetch_files(
942 &self,
943 device: &BlobDevice,
944 r: &mut RafsIoReader,
945 root_ino: Inode,
946 files: Option<Vec<Inode>>,
947 fetcher: &dyn Fn(&mut BlobIoVec, bool),
948 ) -> RafsResult<bool> {
949 if let Some(files) = files {
951 let mut hardlinks: HashSet<u64> = HashSet::new();
953 let mut state = BlobIoMerge::default();
954 for f_ino in files {
955 self.prefetch_data(device, f_ino, &mut state, &mut hardlinks, fetcher)
956 .map_err(|e| RafsError::Prefetch(e.to_string()))?;
957 }
958 for (_id, mut desc) in state.drain() {
959 fetcher(&mut desc, true);
960 }
961 Ok(false)
963 } else if self.meta.is_v5() {
964 self.prefetch_data_v5(device, r, root_ino, fetcher)
965 } else if self.meta.is_v6() {
966 self.prefetch_data_v6(device, r, root_ino, fetcher)
967 } else {
968 Err(RafsError::Prefetch(
969 "Unknown filesystem version, prefetch disabled".to_string(),
970 ))
971 }
972 }
973
974 #[inline]
975 fn prefetch_inode(
976 device: &BlobDevice,
977 inode: &Arc<dyn RafsInode>,
978 state: &mut BlobIoMerge,
979 hardlinks: &mut HashSet<u64>,
980 fetcher: &dyn Fn(&mut BlobIoVec, bool),
981 ) -> Result<()> {
982 if inode.is_hardlink() {
984 if hardlinks.contains(&inode.ino()) {
985 return Ok(());
986 } else {
987 hardlinks.insert(inode.ino());
988 }
989 }
990
991 let descs = inode.alloc_bio_vecs(device, 0, inode.size() as usize, false)?;
992 for desc in descs {
993 state.append(desc);
994 if let Some(desc) = state.get_current_element() {
995 fetcher(desc, false);
996 }
997 }
998
999 Ok(())
1000 }
1001
1002 fn prefetch_data(
1003 &self,
1004 device: &BlobDevice,
1005 ino: u64,
1006 state: &mut BlobIoMerge,
1007 hardlinks: &mut HashSet<u64>,
1008 fetcher: &dyn Fn(&mut BlobIoVec, bool),
1009 ) -> Result<()> {
1010 let inode = self
1011 .superblock
1012 .get_inode(ino, self.validate_digest)
1013 .map_err(|_e| enoent!("Can't find inode"))?;
1014
1015 if inode.is_dir() {
1016 let mut descendants = Vec::new();
1017 let _ = inode.collect_descendants_inodes(&mut descendants)?;
1018 for i in descendants.iter() {
1019 Self::prefetch_inode(device, i, state, hardlinks, fetcher)?;
1020 }
1021 } else if !inode.is_empty_size() && inode.is_reg() {
1022 Self::prefetch_inode(device, &inode, state, hardlinks, fetcher)?;
1028 }
1029
1030 Ok(())
1031 }
1032}
1033
1034impl RafsSuper {
1036 pub fn path_from_ino(&self, ino: Inode) -> Result<PathBuf> {
1038 if ino == self.superblock.root_ino() {
1039 return Ok(self.get_extended_inode(ino, false)?.name().into());
1040 }
1041
1042 let mut path = PathBuf::new();
1043 let mut cur_ino = ino;
1044 let mut inode;
1045
1046 loop {
1047 inode = self.get_extended_inode(cur_ino, false)?;
1048 let e: PathBuf = inode.name().into();
1049 path = e.join(path);
1050
1051 if inode.ino() == self.superblock.root_ino() {
1052 break;
1053 } else {
1054 cur_ino = inode.parent();
1055 }
1056 }
1057
1058 Ok(path)
1059 }
1060
1061 pub fn get_prefetched_inos(&self, bootstrap: &mut RafsIoReader) -> Result<Vec<u32>> {
1063 if self.meta.is_v5() {
1064 let mut pt = RafsV5PrefetchTable::new();
1065 pt.load_prefetch_table_from(
1066 bootstrap,
1067 self.meta.prefetch_table_offset,
1068 self.meta.prefetch_table_entries as usize,
1069 )?;
1070 Ok(pt.inodes)
1071 } else {
1072 let mut pt = RafsV6PrefetchTable::new();
1073 pt.load_prefetch_table_from(
1074 bootstrap,
1075 self.meta.prefetch_table_offset,
1076 self.meta.prefetch_table_entries as usize,
1077 )?;
1078 Ok(pt.inodes)
1079 }
1080 }
1081
1082 pub fn walk_directory<P: AsRef<Path>>(
1085 &self,
1086 ino: Inode,
1087 parent: Option<P>,
1088 cb: &mut dyn FnMut(ArcRafsInodeExt, &Path) -> anyhow::Result<()>,
1089 ) -> anyhow::Result<()> {
1090 let inode = self.get_extended_inode(ino, false)?;
1091 if !inode.is_dir() {
1092 bail!("inode {} is not a directory", ino);
1093 }
1094 self.do_walk_directory(inode, parent, cb)
1095 }
1096
1097 #[allow(clippy::only_used_in_recursion)]
1098 fn do_walk_directory<P: AsRef<Path>>(
1099 &self,
1100 inode: Arc<dyn RafsInodeExt>,
1101 parent: Option<P>,
1102 cb: &mut dyn FnMut(ArcRafsInodeExt, &Path) -> anyhow::Result<()>,
1103 ) -> anyhow::Result<()> {
1104 let path = if let Some(parent) = parent {
1105 parent.as_ref().join(inode.name())
1106 } else {
1107 PathBuf::from("/")
1108 };
1109 cb(inode.clone(), &path)?;
1110 if inode.is_dir() {
1111 for idx in 0..inode.get_child_count() {
1112 let child = inode.get_child_by_index(idx)?;
1113 self.do_walk_directory(child, Some(&path), cb)?;
1114 }
1115 }
1116 Ok(())
1117 }
1118}
1119
1120#[cfg(test)]
1121mod tests {
1122 use super::*;
1123
1124 #[test]
1125 fn test_rafs_mode() {
1126 assert!(RafsMode::from_str("").is_err());
1127 assert!(RafsMode::from_str("directed").is_err());
1128 assert!(RafsMode::from_str("Direct").is_err());
1129 assert!(RafsMode::from_str("Cached").is_err());
1130 assert_eq!(RafsMode::from_str("direct").unwrap(), RafsMode::Direct);
1131 assert_eq!(RafsMode::from_str("cached").unwrap(), RafsMode::Cached);
1132 assert_eq!(&format!("{}", RafsMode::Direct), "direct");
1133 assert_eq!(&format!("{}", RafsMode::Cached), "cached");
1134 }
1135
1136 #[test]
1137 fn test_rafs_compressor() {
1138 assert_eq!(
1139 compress::Algorithm::from(RafsSuperFlags::COMPRESSION_NONE),
1140 compress::Algorithm::None
1141 );
1142 assert_eq!(
1143 compress::Algorithm::from(RafsSuperFlags::COMPRESSION_GZIP),
1144 compress::Algorithm::GZip
1145 );
1146 assert_eq!(
1147 compress::Algorithm::from(RafsSuperFlags::COMPRESSION_LZ4),
1148 compress::Algorithm::Lz4Block
1149 );
1150 assert_eq!(
1151 compress::Algorithm::from(RafsSuperFlags::COMPRESSION_ZSTD),
1152 compress::Algorithm::Zstd
1153 );
1154 assert_eq!(
1155 compress::Algorithm::from(
1156 RafsSuperFlags::COMPRESSION_ZSTD | RafsSuperFlags::COMPRESSION_LZ4,
1157 ),
1158 compress::Algorithm::Lz4Block
1159 );
1160 assert_eq!(
1161 compress::Algorithm::from(RafsSuperFlags::empty()),
1162 compress::Algorithm::Lz4Block
1163 );
1164 }
1165
1166 #[test]
1167 fn test_rafs_digestor() {
1168 assert_eq!(
1169 digest::Algorithm::from(RafsSuperFlags::HASH_BLAKE3),
1170 digest::Algorithm::Blake3
1171 );
1172 assert_eq!(
1173 digest::Algorithm::from(RafsSuperFlags::HASH_SHA256),
1174 digest::Algorithm::Sha256
1175 );
1176 assert_eq!(
1177 digest::Algorithm::from(RafsSuperFlags::HASH_SHA256 | RafsSuperFlags::HASH_BLAKE3,),
1178 digest::Algorithm::Blake3
1179 );
1180 assert_eq!(
1181 digest::Algorithm::from(RafsSuperFlags::empty()),
1182 digest::Algorithm::Blake3
1183 );
1184 }
1185
1186 #[test]
1187 fn test_rafs_crypt_from() {
1188 assert_eq!(
1189 crypt::Algorithm::from(RafsSuperFlags::ENCRYPTION_ASE_128_XTS),
1190 crypt::Algorithm::Aes128Xts
1191 );
1192 assert_eq!(
1193 crypt::Algorithm::from(RafsSuperFlags::empty()),
1194 crypt::Algorithm::None
1195 );
1196 }
1197
1198 #[test]
1199 fn test_rafs_super_meta() {
1200 let mut meta = RafsSuperMeta::default();
1201 assert!(!meta.has_xattr());
1202 assert!(!meta.has_inlined_chunk_digest());
1203 assert_eq!(meta.get_compressor(), compress::Algorithm::None);
1204 assert_eq!(meta.get_digester(), digest::Algorithm::Blake3);
1205 assert_eq!(meta.get_cipher(), crypt::Algorithm::None);
1206
1207 meta.version = RAFS_SUPER_VERSION_V6;
1208 meta.flags |= RafsSuperFlags::INLINED_CHUNK_DIGEST;
1209 meta.flags |= RafsSuperFlags::HASH_SHA256;
1210 meta.flags |= RafsSuperFlags::COMPRESSION_GZIP;
1211 meta.flags |= RafsSuperFlags::ENCRYPTION_ASE_128_XTS;
1212
1213 assert!(meta.has_inlined_chunk_digest());
1214 assert_eq!(meta.get_compressor(), compress::Algorithm::GZip);
1215 assert_eq!(meta.get_digester(), digest::Algorithm::Sha256);
1216 assert_eq!(meta.get_cipher(), crypt::Algorithm::Aes128Xts);
1217
1218 meta.version = RAFS_SUPER_VERSION_V5;
1219 assert_eq!(meta.get_compressor(), compress::Algorithm::GZip);
1220 assert_eq!(meta.get_digester(), digest::Algorithm::Sha256);
1221 assert_eq!(meta.get_cipher(), crypt::Algorithm::None);
1222
1223 let cfg = meta.get_config();
1224 assert!(cfg.check_compatibility(&meta).is_ok());
1225 }
1226
1227 #[test]
1228 fn test_rafs_super_new() {
1229 let cfg = RafsConfigV2 {
1230 mode: "direct".into(),
1231 ..RafsConfigV2::default()
1232 };
1233 let mut rs = RafsSuper::new(&cfg).unwrap();
1234 rs.destroy();
1235 }
1236
1237 fn get_meta(
1238 chunk_size: u32,
1239 explice_uidgid: bool,
1240 tartfs_mode: bool,
1241 hash: RafsSuperFlags,
1242 comp: RafsSuperFlags,
1243 crypt: RafsSuperFlags,
1244 version: u32,
1245 ) -> RafsSuperMeta {
1246 let mut meta = RafsSuperMeta {
1247 chunk_size,
1248 ..Default::default()
1249 };
1250 if explice_uidgid {
1251 meta.flags |= RafsSuperFlags::EXPLICIT_UID_GID;
1252 }
1253 if tartfs_mode {
1254 meta.flags |= RafsSuperFlags::TARTFS_MODE;
1255 }
1256 meta.flags |= hash;
1257 meta.flags |= comp;
1258 meta.flags |= crypt;
1259 meta.version = version;
1260 meta
1261 }
1262
1263 #[test]
1264 fn test_rafs_super_config_check_compatibility_fail() {
1265 let meta1 = get_meta(
1266 1024 as u32,
1267 true,
1268 true,
1269 RafsSuperFlags::HASH_BLAKE3,
1270 RafsSuperFlags::COMPRESSION_GZIP,
1271 RafsSuperFlags::ENCRYPTION_ASE_128_XTS,
1272 RAFS_SUPER_VERSION_V5,
1273 );
1274 let meta2 = get_meta(
1275 2048 as u32,
1276 true,
1277 true,
1278 RafsSuperFlags::HASH_BLAKE3,
1279 RafsSuperFlags::COMPRESSION_GZIP,
1280 RafsSuperFlags::ENCRYPTION_ASE_128_XTS,
1281 RAFS_SUPER_VERSION_V5,
1282 );
1283 let meta3 = get_meta(
1284 1024 as u32,
1285 false,
1286 true,
1287 RafsSuperFlags::HASH_BLAKE3,
1288 RafsSuperFlags::COMPRESSION_GZIP,
1289 RafsSuperFlags::ENCRYPTION_ASE_128_XTS,
1290 RAFS_SUPER_VERSION_V5,
1291 );
1292 let meta4 = get_meta(
1293 1024 as u32,
1294 true,
1295 false,
1296 RafsSuperFlags::HASH_BLAKE3,
1297 RafsSuperFlags::COMPRESSION_GZIP,
1298 RafsSuperFlags::ENCRYPTION_ASE_128_XTS,
1299 RAFS_SUPER_VERSION_V5,
1300 );
1301 let meta5 = get_meta(
1302 1024 as u32,
1303 true,
1304 true,
1305 RafsSuperFlags::HASH_SHA256,
1306 RafsSuperFlags::COMPRESSION_GZIP,
1307 RafsSuperFlags::ENCRYPTION_ASE_128_XTS,
1308 RAFS_SUPER_VERSION_V5,
1309 );
1310 let meta6 = get_meta(
1311 1024 as u32,
1312 true,
1313 true,
1314 RafsSuperFlags::HASH_BLAKE3,
1315 RafsSuperFlags::COMPRESSION_GZIP,
1316 RafsSuperFlags::ENCRYPTION_NONE,
1317 RAFS_SUPER_VERSION_V6,
1318 );
1319
1320 assert!(meta1.get_config().check_compatibility(&meta2).is_err());
1321 assert!(meta1.get_config().check_compatibility(&meta3).is_err());
1322 assert!(meta1.get_config().check_compatibility(&meta4).is_err());
1323 assert!(meta1.get_config().check_compatibility(&meta5).is_err());
1324 assert!(meta1.get_config().check_compatibility(&meta6).is_err());
1325 }
1326}