Alioth Code Coverage

qcow2.rs100.00%

1// Copyright 2026 Google LLC
2//
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 at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15use alioth_macros::Layout;
16use bitfield::bitfield;
17use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
18
19use crate::utils::endian::{Bu32, Bu64};
20use crate::{bitflags, consts};
21
22#[repr(C)]
23#[derive(Debug, Clone, Layout, KnownLayout, Immutable, FromBytes, IntoBytes)]
24/// Qcow2 Header
25///
26/// [Specification](https://qemu-project.gitlab.io/qemu/interop/qcow2.html#header)
27pub 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"
51pub const QCOW2_MAGIC: [u8; 4] = *b"QFI\xfb";
52
53bitflags! {
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
63bitflags! {
64 pub struct Qcow2CompatibleFeatures(u64) {
65 LAZY_REFCOUNTS = 1 << 0;
66 }
67}
68
69consts! {
70 #[derive(Default, Immutable, KnownLayout, FromBytes, IntoBytes)]
71 pub struct Qcow2Compression(u8) {
72 DEFLATE = 0;
73 ZSTD = 1;
74 }
75}
76
77bitfield! {
78 /// QCOW2 L1 Table Entry
79 #[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
87impl Qcow2L1 {
88 pub fn l2_offset(&self) -> u64 {3x
89 self.0 & 0xff_ffff_ffff_ff003x
90 }3x
91}
92
93bitfield! {
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
103bitfield! {
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
112impl Qcow2StdDesc {
113 pub fn cluster_offset(&self) -> u64 {3x
114 self.0 & 0xff_ffff_ffff_ff003x
115 }3x
116}
117
118pub const QCOW2_CMPR_SECTOR_SIZE: u64 = 512;
119
120#[derive(Debug)]
121pub struct Qcow2CmprDesc(pub u64);
122
123impl Qcow2CmprDesc {
124 pub fn offset_size(&self, cluster_bits: u32) -> (u64, u64) {6x
125 let size_bits = cluster_bits - 8;6x
126 let offset_bits = 62 - size_bits;6x
127 let offset = self.0 & ((1 << offset_bits) - 1);6x
128 let sectors = (self.0 >> offset_bits) & ((1 << size_bits) - 1);6x
129 let size = (1 + sectors) * QCOW2_CMPR_SECTOR_SIZE - (offset & (QCOW2_CMPR_SECTOR_SIZE - 1));6x
130 (offset, size)6x
131 }6x
132}
133
134#[cfg(test)]
135#[path = "qcow2_test.rs"]
136mod tests;
137