From be277879555c4b3c706a2ad89c3e2757b6eceb08 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Mon, 27 Mar 2023 11:46:11 +0800 Subject: [PATCH] nydus-image: generate dm-verity data for block device Add `--verity` option to `nydus-image export --block` to generate dm-verity data for block devices. ``` [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# tar -cvf src.tar src [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# sha256sum src.tar 0e2dbe8b6e0f55f42c75034ed9dfc582ad0a94098cfc248c968522e7ef02e00a src.tar [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# cp src.tar images/0e2dbe8b6e0f55f42c75034ed9dfc582ad0a94098cfc248c968522e7ef02e00a [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# target/debug/nydus-image create -t tar-tarfs -D images/ images/0e2dbe8b6e0f55f42c75034ed9dfc582ad0a94098cfc248c968522e7ef02e00a [2023-03-27 16:32:00.068730 +08:00] INFO successfully built RAFS filesystem: meta blob path: images/90f0e6e7e0ff822d4acddf30c36ac77fe06f549fe58f89a818fa824b19f70d47 data blob size: 0x3c000 data blobs: ["0e2dbe8b6e0f55f42c75034ed9dfc582ad0a94098cfc248c968522e7ef02e00a"] [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# target/debug/nydus-image export --block --verity -D images/ -B images/90f0e6e7e0ff822d4acddf30c36ac77fe06f549fe58f89a818fa824b19f70d47 [2023-03-27 23:49:14.450762 +08:00] INFO RAFS features: COMPRESSION_NONE | HASH_SHA256 | EXPLICIT_UID_GID | TARTFS_MODE dm-verity options: --no-superblock --format=1 -s "" --hash=sha256 --data-block-size=4096 --hash-block-size=4096 --data-blocks 572 --hash-offset 2342912 ab7b417fc284c3b58a72044a996ec55e2c68a8b9dcf10bc469f4e640e5d98e6a losetup -r /dev/loop1 images/90f0e6e7e0ff822d4acddf30c36ac77fe06f549fe58f89a818fa824b19f70d47.disk [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# veritysetup open -v --no-superblock --format=1 -s "" --hash=sha256 --data-block-size=4096 --hash-block-size=4096 --data-blocks 572 --hash-offset 2342912 /dev/loop1 verity /dev/loop1 ab7b417fc284c3b58a72044a996ec55e2c68a8b9dcf10bc469f4e640e5d98e6a [root@iZ0jl3vazmhc81dur3xnm3Z image-service]# veritysetup status verity /dev/mapper/verity is active. type: VERITY status: verified hash type: 1 data block: 4096 hash block: 4096 hash name: sha256 salt: - data device: /dev/loop1 data loop: /root/image-service/images/90f0e6e7e0ff822d4acddf30c36ac77fe06f549fe58f89a818fa824b19f70d47.disk size: 4576 sectors mode: readonly hash device: /dev/loop1 hash loop: /root/image-service/images/90f0e6e7e0ff822d4acddf30c36ac77fe06f549fe58f89a818fa824b19f70d47.disk hash offset: 4576 sectors root hash: ab7b417fc284c3b58a72044a996ec55e2c68a8b9dcf10bc469f4e640e5d98e6a ``` Signed-off-by: Jiang Liu --- service/src/block_device.rs | 74 +++++++++++++++++++++++++++++++++---- src/bin/nydus-image/main.rs | 11 +++++- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/service/src/block_device.rs b/service/src/block_device.rs index d3f921a3753..768eba4bb79 100644 --- a/service/src/block_device.rs +++ b/service/src/block_device.rs @@ -15,7 +15,7 @@ use std::cmp::{max, min}; use std::fs::OpenOptions; use std::io::Result; use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::thread; use std::thread::JoinHandle; @@ -25,6 +25,9 @@ use nydus_rafs::metadata::layout::v6::{ EROFS_BLOCK_BITS_12, EROFS_BLOCK_BITS_9, EROFS_BLOCK_SIZE_4096, EROFS_BLOCK_SIZE_512, }; use nydus_storage::utils::alloc_buf; +use nydus_utils::digest::{self, RafsDigest}; +use nydus_utils::round_up; +use nydus_utils::verity::VerityGenerator; use tokio_uring::buf::IoBufMut; use crate::blob_cache::{generate_blob_key, BlobCacheMgr, BlobConfig, DataBlob, MetaBlob}; @@ -287,6 +290,7 @@ impl BlockDevice { output: Option, localfs_dir: Option, threads: u32, + verity: bool, ) -> Result<()> { let cache_mgr = Arc::new(BlobCacheMgr::new()); cache_mgr.add_blob_entry(&blob_entry).map_err(|e| { @@ -303,6 +307,7 @@ impl BlockDevice { )) })?; let block_device = Arc::new(block_device); + let blocks = block_device.blocks(); let path = match output { Some(v) => PathBuf::from(v), @@ -353,7 +358,27 @@ impl BlockDevice { })?; let output_file = Arc::new(tokio_uring::fs::File::from_std(output_file)); - let blocks = block_device.blocks(); + let mut verity_offset = 0; + let generator = if verity { + let file = OpenOptions::new() + .read(true) + .write(true) + .open(&path) + .map_err(|e| { + eother!(format!( + "block_device: failed to create output file {}, {}", + path.display(), + e + )) + })?; + verity_offset = round_up(block_device.blocks_to_size(blocks), 4096); + let mut generator = VerityGenerator::new(file, verity_offset, blocks)?; + generator.initialize()?; + Some(Arc::new(Mutex::new(generator))) + } else { + None + }; + let batch_size = BLOCK_DEVICE_EXPORT_BATCH_SIZE as u32 / block_device.block_size() as u32; assert_eq!(batch_size.count_ones(), 1); let threads = max(threads, 1); @@ -363,8 +388,9 @@ impl BlockDevice { } if threads == 1 { + let generator = generator.clone(); tokio_uring::start(async move { - Self::do_export(block_device.clone(), output_file, 0, block_device.blocks()).await + Self::do_export(block_device, output_file, 0, blocks, generator).await })?; } else { let mut thread_handlers: Vec>> = @@ -377,6 +403,7 @@ impl BlockDevice { let mgr = cache_mgr.clone(); let id = blob_id.clone(); let path = path.to_path_buf(); + let generator = generator.clone(); let handler = thread::spawn(move || { let output_file = OpenOptions::new() @@ -399,9 +426,9 @@ impl BlockDevice { })?; let device = Arc::new(block_device); - tokio_uring::start( - async move { Self::do_export(device, file, pos, count).await }, - )?; + tokio_uring::start(async move { + Self::do_export(device, file, pos, count, generator).await + })?; Ok(()) }); pos += count; @@ -424,6 +451,21 @@ impl BlockDevice { })?; } } + + if let Some(generator) = generator.as_ref() { + let mut guard = generator.lock().unwrap(); + let root_digest = guard.generate_all_digests()?; + let root_digest: String = root_digest + .data + .iter() + .map(|v| format!("{:02x}", v)) + .collect(); + println!( + "dm-verity options: --no-superblock --format=1 -s \"\" --hash=sha256 --data-block-size=512 --hash-block-size=4096 --data-blocks {} --hash-offset {} {}", + blocks, verity_offset, root_digest + ); + } + Ok(()) } @@ -432,8 +474,10 @@ impl BlockDevice { output_file: Arc, start: u32, mut blocks: u32, + generator: Option>>, ) -> Result<()> { let batch_size = BLOCK_DEVICE_EXPORT_BATCH_SIZE as u32 / block_device.block_size() as u32; + let block_size = block_device.block_size() as usize; let mut pos = start; let mut buf = alloc_buf(BLOCK_DEVICE_EXPORT_BATCH_SIZE); @@ -441,7 +485,7 @@ impl BlockDevice { let count = min(batch_size, blocks); let (res, buf1) = block_device.async_read(pos, count, buf).await; let sz = res?; - if sz != count as usize * block_device.block_size() as usize { + if sz != count as usize * block_size { return Err(eio!( "block_device: failed to read data, got less data than requested" )); @@ -462,6 +506,22 @@ impl BlockDevice { } buf = buf2; + // Generate Merkle tree leaf nodes. + if let Some(generator) = generator.as_ref() { + let mut page_idx = (block_device.blocks_to_size(pos) / block_size as u64) as u32; + let mut offset = 0; + while offset < buf.len() { + let digest = RafsDigest::from_buf( + &buf[offset..offset + block_size], + digest::Algorithm::Sha256, + ); + let mut guard = generator.lock().unwrap(); + guard.set_digest(1, page_idx, &digest.data)?; + offset += block_size; + page_idx += 1; + } + } + pos += count; blocks -= count; } diff --git a/src/bin/nydus-image/main.rs b/src/bin/nydus-image/main.rs index d19c1d86900..0e970008d51 100644 --- a/src/bin/nydus-image/main.rs +++ b/src/bin/nydus-image/main.rs @@ -479,6 +479,14 @@ fn prepare_cmd_args(bti_string: &'static str) -> App { .help("File path for saving the exported content") .required_unless_present("localfs-dir") ) + .arg( + Arg::new("verity") + .long("verity") + .help("Generate dm-verity data for block device") + .action(ArgAction::SetTrue) + .required(false) + .requires("block") + ) ); let app = app.subcommand( @@ -1558,8 +1566,9 @@ impl Command { .map(|n| n.parse().unwrap_or(1)) .unwrap_or(1); let output = subargs.value_of("output").map(|v| v.to_string()); + let verity = subargs.is_present("verity"); - BlockDevice::export(entry, output, localfs_dir, threads) + BlockDevice::export(entry, output, localfs_dir, threads, verity) .context("failed to export RAFS filesystem as raw block device image") } }