Skip to content

Commit

Permalink
Merge pull request #46 from flatcar/kai/pread-pwrite
Browse files Browse the repository at this point in the history
Use explicit offsets for reading and writing
  • Loading branch information
pothos authored Jan 3, 2024
2 parents 500d6c6 + cd6beb9 commit 3823606
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 33 deletions.
12 changes: 4 additions & 8 deletions src/bin/download_sysext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -136,16 +135,13 @@ impl<'a> Package<'a> {
fn verify_signature_on_disk(&mut self, from_path: &Path, pubkey_path: &str) -> Result<PathBuf> {
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");
Expand All @@ -158,7 +154,7 @@ impl<'a> Package<'a> {
let hdhashvec: Vec<u8> = 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 {
Expand Down
3 changes: 1 addition & 2 deletions src/download.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -39,7 +39,6 @@ pub fn hash_on_disk_sha256(path: &Path, maxlen: Option<usize>) -> Result<omaha::
let mut freader = BufReader::new(file);
let mut chunklen: usize;

freader.seek(SeekFrom::Start(0)).context("failed to seek(0)".to_string())?;
while maxlen_to_read > 0 {
if maxlen_to_read < CHUNKLEN {
chunklen = maxlen_to_read;
Expand Down
11 changes: 5 additions & 6 deletions test/crau_verify.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io::{BufReader, Write};
use std::io::Write;
use std::error::Error;
use std::fs;

Expand Down Expand Up @@ -30,14 +30,13 @@ fn main() -> Result<(), Box<dyn Error>> {
// 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())?;
Expand All @@ -52,7 +51,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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) {
Expand Down
48 changes: 31 additions & 17 deletions update-format-crau/src/delta_update.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -32,37 +34,41 @@ impl DeltaUpdateFileHeader {
}

// Read delta update header from the given file, return DeltaUpdateFileHeader.
pub fn read_delta_update_header(f: &mut BufReader<File>) -> Result<DeltaUpdateFileHeader> {
pub fn read_delta_update_header(f: &File) -> Result<DeltaUpdateFileHeader> {
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::<u64>()) as u64).context("failed to read manifest size")?;
header.manifest_size = u64::from_be_bytes(buf);

Ok(header)
}

// Take a buffer stream and DeltaUpdateFileHeader,
// return DeltaArchiveManifest that contains manifest.
pub fn get_manifest_bytes(f: &mut BufReader<File>, header: &DeltaUpdateFileHeader) -> Result<proto::DeltaArchiveManifest> {
pub fn get_manifest_bytes(f: &File, header: &DeltaUpdateFileHeader) -> Result<proto::DeltaArchiveManifest> {
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::<u64>() + mem::size_of::<u64>()) as u64,
)
.context("failed to read manifest bytes")?;
buf.into_boxed_slice()
};

Expand All @@ -73,7 +79,7 @@ pub fn get_manifest_bytes(f: &mut BufReader<File>, 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<File>, header: &'a DeltaUpdateFileHeader, manifest: &mut proto::DeltaArchiveManifest) -> Result<Box<[u8]>> {
pub fn get_signatures_bytes<'a>(f: &'a File, header: &'a DeltaUpdateFileHeader, manifest: &mut proto::DeltaArchiveManifest) -> Result<Box<[u8]>> {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!! signature offsets are from the END of the manifest !!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Expand All @@ -82,10 +88,8 @@ pub fn get_signatures_bytes<'a>(f: &'a mut BufReader<File>, 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,
Expand All @@ -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<File>, header: &'a DeltaUpdateFileHeader, manifest: &proto::DeltaArchiveManifest, tmpfile: &Path) -> Result<File> {
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))?;
Expand All @@ -120,27 +124,37 @@ pub fn get_data_blobs<'a>(f: &'a mut BufReader<File>, 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() {
let mut bzdecoder = BzDecoder::new(&partdata[..]);
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]
Expand Down

0 comments on commit 3823606

Please sign in to comment.