use alioth_macros::Layout;
use bitfield::bitfield;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::utils::endian::{Bu32, Bu64};
use crate::{bitflags, consts};
#[repr(C)]
#[derive(Debug, Clone, Layout, KnownLayout, Immutable, FromBytes, IntoBytes)]
pub struct Qcow2Hdr {
pub magic: [u8; 4],
pub version: Bu32,
pub backing_file_offset: Bu64,
pub backing_file_size: Bu32,
pub cluster_bits: Bu32,
pub size: Bu64,
pub crypt_method: Bu32,
pub l1_size: Bu32,
pub l1_table_offset: Bu64,
pub refcount_table_offset: Bu64,
pub refcount_table_clusters: Bu32,
pub nb_snapshots: Bu32,
pub snapshots_offset: Bu64,
pub incompatible_features: Bu64,
pub compatible_features: Bu64,
pub autoclear_features: Bu64,
pub refcount_order: Bu32,
pub header_length: Bu32,
pub compression_type: Qcow2Compression,
pub padding: [u8; 7],
}
pub const QCOW2_MAGIC: [u8; 4] = *b"QFI\xfb";
bitflags! {
pub struct Qcow2IncompatibleFeatures(u64) {
DIRTY = 1 << 0;
CORRUPT = 1 << 1;
EXTERNAL_DATA = 1 << 2;
COMPRESSION = 1 << 3;
EXTERNAL_L2 = 1 << 4;
}
}
bitflags! {
pub struct Qcow2CompatibleFeatures(u64) {
LAZY_REFCOUNTS = 1 << 0;
}
}
consts! {
#[derive(Default, Immutable, KnownLayout, FromBytes, IntoBytes)]
pub struct Qcow2Compression(u8) {
DEFLATE = 0;
ZSTD = 1;
}
}
bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, KnownLayout, Immutable, FromBytes, IntoBytes)]
#[repr(transparent)]
pub struct Qcow2L1(u64);
impl Debug;
pub rc1, _: 63;
pub offset, _: 55, 9;
}
impl Qcow2L1 {
pub fn l2_offset(&self) -> u64 {
self.0 & 0xff_ffff_ffff_ff00
}
}
bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, KnownLayout, Immutable, FromBytes, IntoBytes)]
#[repr(transparent)]
pub struct Qcow2L2(u64);
impl Debug;
pub desc, _: 61, 0;
pub compressed, _: 62;
pub rc1, _: 63;
}
bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, KnownLayout, Immutable, FromBytes, IntoBytes)]
#[repr(transparent)]
pub struct Qcow2StdDesc(u64);
impl Debug;
pub offset, _: 55, 9;
pub zero, _: 0;
}
impl Qcow2StdDesc {
pub fn cluster_offset(&self) -> u64 {
self.0 & 0xff_ffff_ffff_ff00
}
}
pub const QCOW2_CMPR_SECTOR_SIZE: u64 = 512;
#[derive(Debug)]
pub struct Qcow2CmprDesc(pub u64);
impl Qcow2CmprDesc {
pub fn offset_size(&self, cluster_bits: u32) -> (u64, u64) {
let size_bits = cluster_bits - 8;
let offset_bits = 62 - size_bits;
let offset = self.0 & ((1 << offset_bits) - 1);
let sectors = (self.0 >> offset_bits) & ((1 << size_bits) - 1);
let size = (1 + sectors) * QCOW2_CMPR_SECTOR_SIZE - (offset & (QCOW2_CMPR_SECTOR_SIZE - 1));
(offset, size)
}
}
#[cfg(test)]
#[path = "qcow2_test.rs"]
mod tests;