From 01097aa130b20c5b296b163a764dc70f46ae6fa3 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sun, 3 Sep 2023 12:00:30 +0100 Subject: [PATCH] block: Ensure probing reads are block size aligned This is necessary for O_DIRECT based use of raw block devices which may require access at a larger block size than that of a sector (512 bytes.) Fixes: #5722 Signed-off-by: Rob Bradford --- block/src/lib.rs | 31 ++++++++++++++++++---------- block/src/vhd.rs | 53 +++++++++++++++++++++++------------------------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/block/src/lib.rs b/block/src/lib.rs index b751e12f5..90f35de2f 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -731,24 +731,33 @@ pub enum ImageType { const QCOW_MAGIC: u32 = 0x5146_49fb; const VHDX_SIGN: u64 = 0x656C_6966_7864_6876; +/// Read a block into memory aligned by the source block size (needed for O_DIRECT) +pub fn read_aligned_block_size(f: &mut File) -> std::io::Result> { + let blocksize = DiskTopology::probe(f)?.logical_block_size as usize; + // SAFETY: We are allocating memory that is naturally aligned (size = alignment) and we meet + // requirements for safety from Vec::from_raw_parts() as we are using the global allocator + // and transferring ownership of the memory. + let mut data = unsafe { + Vec::from_raw_parts( + alloc_zeroed(Layout::from_size_align_unchecked(blocksize, blocksize)), + blocksize, + blocksize, + ) + }; + f.read_exact(&mut data)?; + Ok(data) +} + /// Determine image type through file parsing. pub fn detect_image_type(f: &mut File) -> std::io::Result { - // We must create a buffer aligned on 512 bytes with a size being a - // multiple of 512 bytes as the file might be opened with O_DIRECT flag. - #[repr(align(512))] - struct Sector { - data: [u8; 512], - } - let mut s = Sector { data: [0; 512] }; - - f.read_exact(&mut s.data)?; + let block = read_aligned_block_size(f)?; // Check 4 first bytes to get the header value and determine the image type - let image_type = if u32::from_be_bytes(s.data[0..4].try_into().unwrap()) == QCOW_MAGIC { + let image_type = if u32::from_be_bytes(block[0..4].try_into().unwrap()) == QCOW_MAGIC { ImageType::Qcow2 } else if vhd::is_fixed_vhd(f)? { ImageType::FixedVhd - } else if u64::from_le_bytes(s.data[0..8].try_into().unwrap()) == VHDX_SIGN { + } else if u64::from_le_bytes(block[0..8].try_into().unwrap()) == VHDX_SIGN { ImageType::Vhdx } else { ImageType::Raw diff --git a/block/src/vhd.rs b/block/src/vhd.rs index b47ce90bf..2bf99a05c 100644 --- a/block/src/vhd.rs +++ b/block/src/vhd.rs @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 +use crate::{read_aligned_block_size, DiskTopology}; use std::convert::TryInto; use std::fs::File; -use std::io::{Read, Seek, SeekFrom}; +use std::io::{Seek, SeekFrom}; #[derive(Clone, Copy)] pub struct VhdFooter { @@ -27,37 +28,33 @@ pub struct VhdFooter { impl VhdFooter { pub fn new(file: &mut File) -> std::io::Result { - // We must create a buffer aligned on 512 bytes with a size being a - // multiple of 512 bytes as the file might be opened with O_DIRECT flag. - #[repr(align(512))] - struct Sector { - data: [u8; 512], - } - let mut s = Sector { data: [0; 512] }; + let blocksize = DiskTopology::probe(file)?.logical_block_size as usize; - // Place the cursor 512 bytes before the end of the file, as this is - // where the footer starts. - file.seek(SeekFrom::End(-512))?; + // Place the cursor in the last block of the file + file.seek(SeekFrom::End(0 - (blocksize as i64)))?; + // Read in the last block + let data = read_aligned_block_size(file)?; - // Fill in the VhdFooter structure - file.read_exact(&mut s.data)?; + // We only care about the last sector + let offset = blocksize - 512; + let sector = &data[offset..]; Ok(VhdFooter { - cookie: u64::from_be_bytes(s.data[0..8].try_into().unwrap()), - features: u32::from_be_bytes(s.data[8..12].try_into().unwrap()), - file_format_version: u32::from_be_bytes(s.data[12..16].try_into().unwrap()), - data_offset: u64::from_be_bytes(s.data[16..24].try_into().unwrap()), - time_stamp: u32::from_be_bytes(s.data[24..28].try_into().unwrap()), - creator_application: u32::from_be_bytes(s.data[28..32].try_into().unwrap()), - creator_version: u32::from_be_bytes(s.data[32..36].try_into().unwrap()), - creator_host_os: u32::from_be_bytes(s.data[36..40].try_into().unwrap()), - original_size: u64::from_be_bytes(s.data[40..48].try_into().unwrap()), - current_size: u64::from_be_bytes(s.data[48..56].try_into().unwrap()), - disk_geometry: u32::from_be_bytes(s.data[56..60].try_into().unwrap()), - disk_type: u32::from_be_bytes(s.data[60..64].try_into().unwrap()), - checksum: u32::from_be_bytes(s.data[64..68].try_into().unwrap()), - unique_id: u128::from_be_bytes(s.data[68..84].try_into().unwrap()), - saved_state: u8::from_be_bytes(s.data[84..85].try_into().unwrap()), + cookie: u64::from_be_bytes(sector[0..8].try_into().unwrap()), + features: u32::from_be_bytes(sector[8..12].try_into().unwrap()), + file_format_version: u32::from_be_bytes(sector[12..16].try_into().unwrap()), + data_offset: u64::from_be_bytes(sector[16..24].try_into().unwrap()), + time_stamp: u32::from_be_bytes(sector[24..28].try_into().unwrap()), + creator_application: u32::from_be_bytes(sector[28..32].try_into().unwrap()), + creator_version: u32::from_be_bytes(sector[32..36].try_into().unwrap()), + creator_host_os: u32::from_be_bytes(sector[36..40].try_into().unwrap()), + original_size: u64::from_be_bytes(sector[40..48].try_into().unwrap()), + current_size: u64::from_be_bytes(sector[48..56].try_into().unwrap()), + disk_geometry: u32::from_be_bytes(sector[56..60].try_into().unwrap()), + disk_type: u32::from_be_bytes(sector[60..64].try_into().unwrap()), + checksum: u32::from_be_bytes(sector[64..68].try_into().unwrap()), + unique_id: u128::from_be_bytes(sector[68..84].try_into().unwrap()), + saved_state: u8::from_be_bytes(sector[84..85].try_into().unwrap()), }) }