qcow2.rs100.00%
1
// Copyright 2026 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use alioth_macros::Layout;16
use bitfield::bitfield;17
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};18
19
use crate::utils::endian::{Bu32, Bu64};20
use crate::{bitflags, consts};21
22
#[repr(C)]23
#[derive(Debug, Clone, Layout, KnownLayout, Immutable, FromBytes, IntoBytes)]24
/// Qcow2 Header25
///26
/// [Specification](https://qemu-project.gitlab.io/qemu/interop/qcow2.html#header)27
pub struct Qcow2Hdr {28
pub magic: [u8; 4],29
pub version: Bu32,30
pub backing_file_offset: Bu64,31
pub backing_file_size: Bu32,32
pub cluster_bits: Bu32,33
pub size: Bu64,34
pub crypt_method: Bu32,35
pub l1_size: Bu32,36
pub l1_table_offset: Bu64,37
pub refcount_table_offset: Bu64,38
pub refcount_table_clusters: Bu32,39
pub nb_snapshots: Bu32,40
pub snapshots_offset: Bu64,41
pub incompatible_features: Bu64,42
pub compatible_features: Bu64,43
pub autoclear_features: Bu64,44
pub refcount_order: Bu32,45
pub header_length: Bu32,46
pub compression_type: Qcow2Compression,47
pub padding: [u8; 7],48
}49
50
/// Qcow2 Magic Number "QFI\xfb"51
pub const QCOW2_MAGIC: [u8; 4] = *b"QFI\xfb";52
53
bitflags! {54
pub struct Qcow2IncompatibleFeatures(u64) {55
DIRTY = 1 << 0;56
CORRUPT = 1 << 1;57
EXTERNAL_DATA = 1 << 2;58
COMPRESSION = 1 << 3;59
EXTERNAL_L2 = 1 << 4;60
}61
}62
63
bitflags! {64
pub struct Qcow2CompatibleFeatures(u64) {65
LAZY_REFCOUNTS = 1 << 0;66
}67
}68
69
consts! {70
#[derive(Default, Immutable, KnownLayout, FromBytes, IntoBytes)]71
pub struct Qcow2Compression(u8) {72
DEFLATE = 0;73
ZSTD = 1;74
}75
}76
77
bitfield! {78
/// QCOW2 L1 Table Entry79
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, KnownLayout, Immutable, FromBytes, IntoBytes)]80
#[repr(transparent)]81
pub struct Qcow2L1(u64);82
impl Debug;83
pub rc1, _: 63;84
pub offset, _: 55, 9;85
}86
87
impl Qcow2L1 {88
pub fn l2_offset(&self) -> u64 {3x89
self.0 & 0xff_ffff_ffff_ff003x90
}3x91
}92
93
bitfield! {94
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, KnownLayout, Immutable, FromBytes, IntoBytes)]95
#[repr(transparent)]96
pub struct Qcow2L2(u64);97
impl Debug;98
pub desc, _: 61, 0;99
pub compressed, _: 62;100
pub rc1, _: 63;101
}102
103
bitfield! {104
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, KnownLayout, Immutable, FromBytes, IntoBytes)]105
#[repr(transparent)]106
pub struct Qcow2StdDesc(u64);107
impl Debug;108
pub offset, _: 55, 9;109
pub zero, _: 0;110
}111
112
impl Qcow2StdDesc {113
pub fn cluster_offset(&self) -> u64 {3x114
self.0 & 0xff_ffff_ffff_ff003x115
}3x116
}117
118
pub const QCOW2_CMPR_SECTOR_SIZE: u64 = 512;119
120
#[derive(Debug)]121
pub struct Qcow2CmprDesc(pub u64);122
123
impl Qcow2CmprDesc {124
pub fn offset_size(&self, cluster_bits: u32) -> (u64, u64) {6x125
let size_bits = cluster_bits - 8;6x126
let offset_bits = 62 - size_bits;6x127
let offset = self.0 & ((1 << offset_bits) - 1);6x128
let sectors = (self.0 >> offset_bits) & ((1 << size_bits) - 1);6x129
let size = (1 + sectors) * QCOW2_CMPR_SECTOR_SIZE - (offset & (QCOW2_CMPR_SECTOR_SIZE - 1));6x130
(offset, size)6x131
}6x132
}133
134
#[cfg(test)]135
#[path = "qcow2_test.rs"]136
mod tests;137