Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convert page_aligned_vec!() into Qcow2Iobuf #3

Merged
merged 5 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ env:
CARGO_TERM_COLOR: always

jobs:
build:
build_linux:
name: Rust project - latest
runs-on: ubuntu-latest
strategy:
Expand All @@ -23,3 +23,32 @@ jobs:
- run: cargo build --verbose
- run: cargo test -- --nocapture
- run: cargo test -r

build_win:
name: Rust project - latest
runs-on: windows-latest
strategy:
matrix:
toolchain:
- stable
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Chocolatey
run: |
$qemuZipPath = Join-Path $env:USERPROFILE 'qemu.zip'
Invoke-WebRequest -Uri 'https://cloudbase.it/downloads/qemu-img-win-x64-2_3_0.zip' -OutFile $qemuZipPath
Expand-Archive -Path $qemuZipPath -DestinationPath $env:USERPROFILE
- run: |
$env:PATH = "$env:USERPROFILE;$env:PATH"
qemu-img --help
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose
- name: Rust test(debug)
run: |
$env:PATH = "$env:USERPROFILE;$env:PATH"
cargo test -- --nocapture
- name: Rust test(release)
run: |
$env:PATH = "$env:USERPROFILE;$env:PATH"
cargo test -r
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ host cluster leak, format qcow2 image, ....
.await
.unwrap();

let mut buf = qcow2_rs::page_aligned_vec!(u8, 4096);
let mut buf = qcow2_rs::helpers::Qcow2IoBuf::<u8>::new(4096);

// read 4096 bytes to `buf` from virt offset 0 of `test.qcow2`
let _ = dev.read_at(&mut buf, 0).await.unwrap();
Expand Down
19 changes: 10 additions & 9 deletions src/dev.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::cache::AsyncLruCache;
use crate::cache::AsyncLruCacheEntry;
use crate::error::Qcow2Result;
use crate::helpers::qcow2_type_of;
use crate::helpers::{qcow2_type_of, Qcow2IoBuf};
use crate::meta::{
L1Entry, L1Table, L2Entry, L2Table, Mapping, MappingSource, Qcow2Header, RefBlock, RefTable,
RefTableEntry, SplitGuestOffset, Table, TableEntry,
Expand Down Expand Up @@ -500,10 +500,10 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
let res = self.file.fallocate(offset, len, flags).await;
match res {
Err(_) => {
let mut zero_data = crate::page_aligned_vec!(u8, len);
zero_buf!(zero_data);
let mut zero_data = Qcow2IoBuf::<u8>::new(len);

log::trace!("discard fallback off {:x} len {}", offset, len);
zero_data.zero_buf();
self.call_write(offset, &zero_data).await
}
Ok(_) => Ok(()),
Expand Down Expand Up @@ -1537,7 +1537,7 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
let pad = (compressed_offset - aligned_off) as usize;
let aligned_len = (pad + compressed_length + bs - 1) & (bs_mask as usize);

let mut _compressed_data = crate::page_aligned_vec!(u8, aligned_len);
let mut _compressed_data = Qcow2IoBuf::<u8>::new(aligned_len);
let res = self.call_read(aligned_off, &mut _compressed_data).await?;
if res != aligned_len {
return Err("do_read_compressed: short read compressed data".into());
Expand Down Expand Up @@ -1880,7 +1880,7 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
host_off: u64,
compressed_mapping: &Mapping,
) -> Qcow2Result<()> {
let mut cbuf = crate::page_aligned_vec!(u8, self.info.cluster_size());
let mut cbuf = Qcow2IoBuf::<u8>::new(self.info.cluster_size());

// copy & write
self.do_read_compressed(compressed_mapping.clone(), 0, &mut cbuf)
Expand All @@ -1899,7 +1899,7 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
) -> Qcow2Result<()> {
match self.backing_file.as_ref() {
Some(backing) => {
let mut cbuf = crate::page_aligned_vec!(u8, self.info.cluster_size());
let mut cbuf = Qcow2IoBuf::<u8>::new(self.info.cluster_size());

// copy & write
backing
Expand Down Expand Up @@ -2781,9 +2781,10 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
#[cfg(test)]
mod tests {
use crate::dev::*;
use crate::helpers::Qcow2IoBuf;
use crate::qcow2_default_params;
use crate::tokio_io::Qcow2IoTokio;
use crate::utils::qcow2_setup_dev_tokio;
use crate::{page_aligned_vec, qcow2_default_params};
use std::path::PathBuf;
use tokio::runtime::Runtime;
use utilities::*;
Expand Down Expand Up @@ -2909,7 +2910,7 @@ mod tests {
let size = 64_u64 << 20;
let img_file = make_temp_qcow2_img(size, 16, 4);
let io = Qcow2IoTokio::new(&img_file.path().to_path_buf(), true, false).await;
let mut buf = page_aligned_vec!(u8, 4096);
let mut buf = Qcow2IoBuf::<u8>::new(4096);
let _ = io.read_to(0, &mut buf).await;
let header = Qcow2Header::from_buf(&buf).unwrap();

Expand All @@ -2929,7 +2930,7 @@ mod tests {
let params = qcow2_default_params!(true, false);
let dev = qcow2_setup_dev_tokio(&path, &params).await.unwrap();

let mut buf = page_aligned_vec!(u8, 1 << cluster_bits);
let mut buf = Qcow2IoBuf::<u8>::new(1 << cluster_bits);
dev.read_at(&mut buf, 0).await.unwrap();
});
}
Expand Down
103 changes: 89 additions & 14 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#![allow(dead_code)]
use crate::error::Qcow2Result;
use core::future::Future;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;

#[macro_export]
Expand Down Expand Up @@ -88,22 +89,96 @@ impl_int_alignment_for_primitive!(usize);
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
pub type Qcow2FutureResult<'a, T> = BoxedFuture<'a, Qcow2Result<T>>;

#[macro_export]
macro_rules! page_aligned_vec {
($type:ty, $size:expr) => {{
#[repr(C, align(4096))]
#[derive(Clone)]
struct PageAlignedBuf([u8; 512]);

let sz = ($size + 511) & !511;
let nr = sz / 512;
let buf = Vec::<PageAlignedBuf>::with_capacity(nr);
/// Slice like buffer, which address is aligned with 4096.
///
pub struct Qcow2IoBuf<T> {
ptr: *mut T,
size: usize,
}

// Users of Qcow2IoBuf has to deal with Send & Sync
unsafe impl<T> Send for Qcow2IoBuf<T> {}
unsafe impl<T> Sync for Qcow2IoBuf<T> {}

impl<'a, T> Qcow2IoBuf<T> {
pub fn new(size: usize) -> Self {
let layout = std::alloc::Layout::from_size_align(size, 4096).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) } as *mut T;

assert!(size != 0);

Qcow2IoBuf { ptr, size }
}

/// how many elements in this buffer
pub fn len(&self) -> usize {
let elem_size = core::mem::size_of::<T>();
self.size / elem_size
}

/// Return raw address of this buffer
pub fn as_ptr(&self) -> *const T {
self.ptr
}

/// Return mutable raw address of this buffer
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr
}

/// slice with u8 element, only for RefBlock
pub(crate) fn as_u8_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.size) }
}

/// mutable slice with u8 element, only for RefBlock
pub(crate) fn as_u8_slice_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr as *mut u8, self.size) }
}

/// fill zero for every bits of this buffer
pub fn zero_buf(&mut self) {
unsafe {
let mut a: Vec<$type> = std::mem::transmute(buf);
a.set_len(sz / core::mem::size_of::<$type>());
a
std::ptr::write_bytes(self.as_mut_ptr(), 0, self.len());
}
}};
}
}

impl<T> std::fmt::Debug for Qcow2IoBuf<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ptr {:?} size {} element type {}",
self.ptr,
self.size,
qcow2_type_of(unsafe { &*self.ptr })
)
}
}

/// Slice reference of this buffer
impl<T> Deref for Qcow2IoBuf<T> {
type Target = [T];
fn deref(&self) -> &[T] {
let elem_size = core::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts(self.ptr, self.size / elem_size) }
}
}

/// Mutable slice reference of this buffer
impl<T> DerefMut for Qcow2IoBuf<T> {
fn deref_mut(&mut self) -> &mut [T] {
let elem_size = core::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size / elem_size) }
}
}

/// Free buffer with same alloc layout
impl<T> Drop for Qcow2IoBuf<T> {
fn drop(&mut self) {
let layout = std::alloc::Layout::from_size_align(self.size, 4096).unwrap();
unsafe { std::alloc::dealloc(self.ptr as *mut u8, layout) };
}
}

/// It is user's responsibility to not free buffer of the slice
Expand Down
6 changes: 3 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use clap::{Args, Parser, Subcommand};
use clap_num::maybe_hex;
use qcow2_rs::dev::{Qcow2DevParams, Qcow2Info};
use qcow2_rs::error::Qcow2Result;
use qcow2_rs::helpers::Qcow2IoBuf;
use qcow2_rs::meta::{
L1Table, L2Table, Qcow2FeatureType, Qcow2Header, RefBlock, RefTable, Table, TableEntry,
};
use qcow2_rs::page_aligned_vec;
use qcow2_rs::utils::qcow2_setup_dev_tokio;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf;
Expand Down Expand Up @@ -620,7 +620,7 @@ fn read_qcow2(args: ReadArgs) -> Qcow2Result<()> {
eprintln!("unaligned offset {:x} or len {:x}", args.start, len);
}

let mut buf = page_aligned_vec!(u8, len);
let mut buf = Qcow2IoBuf::<u8>::new(len);

dev.read_at(&mut buf, args.start)
.await
Expand Down Expand Up @@ -648,7 +648,7 @@ fn write_qcow2(args: WriteArgs) -> Qcow2Result<()> {
eprintln!("unaligned offset {:x} or len {:x}", args.start, len);
}

let mut buf = page_aligned_vec!(u8, len);
let mut buf = Qcow2IoBuf::<u8>::new(len);

let pattern = (0..=15).cycle().take(buf.len());
for (elem, pattern_element) in buf.iter_mut().zip(pattern) {
Expand Down
Loading