diff --git a/src/bin/download_sysext.rs b/src/bin/download_sysext.rs index 9107b49..62ebf9b 100644 --- a/src/bin/download_sysext.rs +++ b/src/bin/download_sysext.rs @@ -4,7 +4,6 @@ use std::ffi::OsStr; use std::fs::File; use std::fs; use std::io; -use std::io::BufReader; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::Duration; @@ -136,16 +135,13 @@ impl<'a> Package<'a> { fn verify_signature_on_disk(&mut self, from_path: &Path, pubkey_path: &str) -> Result { let upfile = File::open(from_path).context(format!("failed to open path ({:?})", from_path.display()))?; - // create a BufReader to pass down to parsing functions. - let upfreader = &mut BufReader::new(upfile); - // Read update payload from file, read delta update header from the payload. - let header = delta_update::read_delta_update_header(upfreader).context(format!("failed to read_delta_update_header path ({:?})", from_path.display()))?; + let header = delta_update::read_delta_update_header(&upfile).context(format!("failed to read_delta_update_header path ({:?})", from_path.display()))?; - let mut delta_archive_manifest = delta_update::get_manifest_bytes(upfreader, &header).context(format!("failed to get_manifest_bytes path ({:?})", from_path.display()))?; + let mut delta_archive_manifest = delta_update::get_manifest_bytes(&upfile, &header).context(format!("failed to get_manifest_bytes path ({:?})", from_path.display()))?; // Extract signature from header. - let sigbytes = delta_update::get_signatures_bytes(upfreader, &header, &mut delta_archive_manifest).context(format!("failed to get_signatures_bytes path ({:?})", from_path.display()))?; + let sigbytes = delta_update::get_signatures_bytes(&upfile, &header, &mut delta_archive_manifest).context(format!("failed to get_signatures_bytes path ({:?})", from_path.display()))?; // tmp dir == "/var/tmp/outdir/.tmp" let tmpdirpathbuf = from_path.parent().ok_or(anyhow!("unable to get parent dir"))?.parent().ok_or(anyhow!("unable to get parent dir"))?.join(".tmp"); @@ -158,7 +154,7 @@ impl<'a> Package<'a> { let hdhashvec: Vec = hdhash.clone().into(); // Extract data blobs into a file, datablobspath. - delta_update::get_data_blobs(upfreader, &header, &delta_archive_manifest, datablobspath.as_path()).context(format!("failed to get_data_blobs path ({:?})", datablobspath.display()))?; + delta_update::get_data_blobs(&upfile, &header, &delta_archive_manifest, datablobspath.as_path()).context(format!("failed to get_data_blobs path ({:?})", datablobspath.display()))?; // Check for hash of data blobs with new_partition_info hash. let pinfo_hash = match &delta_archive_manifest.new_partition_info.hash { diff --git a/src/download.rs b/src/download.rs index 90e7653..27d3a9d 100644 --- a/src/download.rs +++ b/src/download.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result, bail}; -use std::io::{BufReader, Read, Seek, SeekFrom}; +use std::io::{BufReader, Read}; use std::fs::File; use std::path::Path; use log::info; @@ -39,7 +39,6 @@ pub fn hash_on_disk_sha256(path: &Path, maxlen: Option) -> Result 0 { if maxlen_to_read < CHUNKLEN { chunklen = maxlen_to_read; diff --git a/test/crau_verify.rs b/test/crau_verify.rs index 32a242a..bd86474 100644 --- a/test/crau_verify.rs +++ b/test/crau_verify.rs @@ -1,4 +1,4 @@ -use std::io::{BufReader, Write}; +use std::io::Write; use std::error::Error; use std::fs; @@ -30,14 +30,13 @@ fn main() -> Result<(), Box> { // Read update payload from srcpath, read delta update header from the payload. let upfile = fs::File::open(srcpath.clone())?; - let freader = &mut BufReader::new(upfile); - let header = delta_update::read_delta_update_header(freader)?; + let header = delta_update::read_delta_update_header(&upfile)?; // Parse signature data from the signature containing data, version, special fields. - let mut delta_archive_manifest = delta_update::get_manifest_bytes(freader, &header)?; + let mut delta_archive_manifest = delta_update::get_manifest_bytes(&upfile, &header)?; // Extract signature from header. - let sigbytes = delta_update::get_signatures_bytes(freader, &header, &mut delta_archive_manifest)?; + let sigbytes = delta_update::get_signatures_bytes(&upfile, &header, &mut delta_archive_manifest)?; let tmpdir = tempfile::tempdir()?.into_path(); fs::create_dir_all(tmpdir.clone())?; @@ -52,7 +51,7 @@ fn main() -> Result<(), Box> { let datablobspath = tmpdir.join("ue_data_blobs"); // Extract data blobs into file path. - delta_update::get_data_blobs(freader, &header, &delta_archive_manifest, datablobspath.as_path())?; + delta_update::get_data_blobs(&upfile, &header, &delta_archive_manifest, datablobspath.as_path())?; // Parse signature data from the signature containing data, version, special fields. let sigdata = match delta_update::parse_signature_data(&sigbytes, hdhashvec.as_slice(), PUBKEY_FILE) { diff --git a/update-format-crau/src/delta_update.rs b/update-format-crau/src/delta_update.rs index 7a94a26..eb873bb 100644 --- a/update-format-crau/src/delta_update.rs +++ b/update-format-crau/src/delta_update.rs @@ -1,7 +1,9 @@ -use std::io::{BufReader, Read, Seek, SeekFrom, Write}; +use std::io::{Read, Write}; use std::fs; use std::fs::File; use std::path::Path; +use std::mem; +use std::os::unix::prelude::FileExt; use log::{debug, info}; use bzip2::read::BzDecoder; use anyhow::{Context, Result, anyhow, bail}; @@ -32,26 +34,26 @@ impl DeltaUpdateFileHeader { } // Read delta update header from the given file, return DeltaUpdateFileHeader. -pub fn read_delta_update_header(f: &mut BufReader) -> Result { +pub fn read_delta_update_header(f: &File) -> Result { let mut header = DeltaUpdateFileHeader { magic: [0; 4], file_format_version: 0, manifest_size: 0, }; - f.read_exact(&mut header.magic).context("failed to read header magic")?; + f.read_exact_at(&mut header.magic, 0).context("failed to read header magic")?; if header.magic != DELTA_UPDATE_FILE_MAGIC { bail!("bad file magic"); } let mut buf = [0u8; 8]; - f.read_exact(&mut buf).context("failed to read file format version")?; + f.read_exact_at(&mut buf, header.magic.len() as u64).context("failed to read file format version")?; header.file_format_version = u64::from_be_bytes(buf); if header.file_format_version != 1 { bail!("unsupported file format version"); } - f.read_exact(&mut buf).context("failed to read manifest size")?; + f.read_exact_at(&mut buf, (header.magic.len() + mem::size_of::()) as u64).context("failed to read manifest size")?; header.manifest_size = u64::from_be_bytes(buf); Ok(header) @@ -59,10 +61,14 @@ pub fn read_delta_update_header(f: &mut BufReader) -> Result, header: &DeltaUpdateFileHeader) -> Result { +pub fn get_manifest_bytes(f: &File, header: &DeltaUpdateFileHeader) -> Result { let manifest_bytes = { let mut buf = vec![0u8; header.manifest_size as usize]; - f.read_exact(&mut buf).context("failed to read manifest bytes")?; + f.read_exact_at( + &mut buf, + (header.magic.len() + mem::size_of::() + mem::size_of::()) as u64, + ) + .context("failed to read manifest bytes")?; buf.into_boxed_slice() }; @@ -73,7 +79,7 @@ pub fn get_manifest_bytes(f: &mut BufReader, header: &DeltaUpdateFileHeade // Take a buffer stream and DeltaUpdateFileHeader, // return a bytes slice of the actual signature data as well as its length. -pub fn get_signatures_bytes<'a>(f: &'a mut BufReader, header: &'a DeltaUpdateFileHeader, manifest: &mut proto::DeltaArchiveManifest) -> Result> { +pub fn get_signatures_bytes<'a>(f: &'a File, header: &'a DeltaUpdateFileHeader, manifest: &mut proto::DeltaArchiveManifest) -> Result> { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // !!! signature offsets are from the END of the manifest !!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -82,10 +88,8 @@ pub fn get_signatures_bytes<'a>(f: &'a mut BufReader, header: &'a DeltaUpd let signatures_bytes = match (manifest.signatures_offset, manifest.signatures_size) { (Some(sig_offset), Some(sig_size)) => { - f.seek(SeekFrom::Start(header.translate_offset(sig_offset))).context("failed to seek to start of signature")?; - let mut buf = vec![0u8; sig_size as usize]; - f.read_exact(&mut buf).context("failed to read signature")?; + f.read_exact_at(&mut buf, header.translate_offset(sig_offset)).context("failed to read signature")?; Some(buf.into_boxed_slice()) } _ => None, @@ -108,7 +112,7 @@ pub fn get_header_data_length(header: &DeltaUpdateFileHeader, manifest: &proto:: // Take a buffer reader, delta file header, manifest as input. // Return path to data blobs, without header, manifest, or signatures. -pub fn get_data_blobs<'a>(f: &'a mut BufReader, header: &'a DeltaUpdateFileHeader, manifest: &proto::DeltaArchiveManifest, tmpfile: &Path) -> Result { +pub fn get_data_blobs<'a>(f: &'a File, header: &'a DeltaUpdateFileHeader, manifest: &proto::DeltaArchiveManifest, tmpfile: &Path) -> Result<()> { let tmpdir = tmpfile.parent().ok_or(anyhow!("unable to get parent directory"))?; fs::create_dir_all(tmpdir).context(format!("failed to create directory {:?}", tmpdir))?; let mut outfile = File::create(tmpfile).context(format!("failed to create file {:?}", tmpfile))?; @@ -120,12 +124,22 @@ pub fn get_data_blobs<'a>(f: &'a mut BufReader, header: &'a DeltaUpdateFil for pop in &manifest.partition_operations { let data_offset = pop.data_offset.ok_or(anyhow!("unable to get data offset"))?; let data_length = pop.data_length.ok_or(anyhow!("unable to get data length"))?; + let block_size = manifest.block_size() as u64; + if pop.dst_extents.len() != 1 { + bail!( + "unexpected number of extents, only one can be handled: {}", + pop.dst_extents.len() + ); + } + let start_block = block_size * pop.dst_extents[0].start_block.ok_or(anyhow!("unable to get start_block"))?; let mut partdata = vec![0u8; data_length as usize]; let translated_offset = header.translate_offset(data_offset.into()); - f.seek(SeekFrom::Start(translated_offset)).context(format!("failed to seek at offset {:?}", translated_offset))?; - f.read_exact(&mut partdata).context(format!("failed to read data with length {:?}", data_length))?; + f.read_exact_at(&mut partdata, translated_offset).context(format!( + "failed to read data with length {:?} at {:?}", + data_length, translated_offset + ))?; // In case of bzip2-compressed chunks, extract. if pop.type_.ok_or(anyhow!("unable to get type_ from partition operations"))? == proto::install_operation::Type::REPLACE_BZ.into() { @@ -133,14 +147,14 @@ pub fn get_data_blobs<'a>(f: &'a mut BufReader, header: &'a DeltaUpdateFil let mut partdata_unpacked = Vec::new(); bzdecoder.read_to_end(&mut partdata_unpacked).context(format!("failed to unpack bzip2ed data at offset {:?}", translated_offset))?; - outfile.write_all(&partdata_unpacked).context(format!("failed to copy unpacked data at offset {:?}", translated_offset))?; + outfile.write_all_at(&partdata_unpacked, start_block).context(format!("failed to copy unpacked data at offset {:?}", translated_offset))?; } else { - outfile.write_all(&partdata).context(format!("failed to copy plain data at offset {:?}", translated_offset))?; + outfile.write_all_at(&partdata, start_block).context(format!("failed to copy plain data at offset {:?}", translated_offset))?; } outfile.flush().context(format!("failed to flush at offset {:?}", translated_offset))?; } - Ok(outfile) + Ok(()) } #[rustfmt::skip]