1mod qcow_raw_file;
6mod refcount;
7mod vec_cache;
8
9use std::cmp::max;
10use std::cmp::min;
11use std::fs::File;
12use std::io;
13use std::io::Read;
14use std::io::Seek;
15use std::io::SeekFrom;
16use std::io::Write;
17use std::mem::size_of;
18use std::path::PathBuf;
19use std::str;
20
21use base::error;
22use base::AsRawDescriptor;
23use base::AsRawDescriptors;
24use base::FileAllocate;
25use base::FileReadWriteAtVolatile;
26use base::FileSetLen;
27use base::FileSync;
28use base::PunchHole;
29use base::RawDescriptor;
30use base::VolatileMemory;
31use base::VolatileSlice;
32use base::WriteZeroesAt;
33use cros_async::Executor;
34use libc::EINVAL;
35use libc::ENOSPC;
36use libc::ENOTSUP;
37use remain::sorted;
38use sync::Mutex;
39use thiserror::Error;
40
41use crate::asynchronous::DiskFlush;
42use crate::qcow::qcow_raw_file::QcowRawFile;
43use crate::qcow::refcount::RefCount;
44use crate::qcow::vec_cache::CacheMap;
45use crate::qcow::vec_cache::Cacheable;
46use crate::qcow::vec_cache::VecCache;
47use crate::AsyncDisk;
48use crate::AsyncDiskFileWrapper;
49use crate::DiskFile;
50use crate::DiskFileParams;
51use crate::DiskGetLen;
52use crate::ToAsyncDisk;
53
54#[sorted]
55#[derive(Error, Debug)]
56pub enum Error {
57 #[error("backing file path escapes the first cluster")]
58 BackingFileEscapesFirstCluster,
59 #[error("backing file io error: {0}")]
60 BackingFileIo(io::Error),
61 #[error("backing file open error: {0}")]
62 BackingFileOpen(Box<crate::Error>),
63 #[error("backing file name is too long: {0} bytes over")]
64 BackingFileTooLong(usize),
65 #[error("compressed blocks not supported")]
66 CompressedBlocksNotSupported,
67 #[error("failed to evict cache: {0}")]
68 EvictingCache(io::Error),
69 #[error("file larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
70 FileTooBig(u64),
71 #[error("failed to get file size: {0}")]
72 GettingFileSize(io::Error),
73 #[error("failed to get refcount: {0}")]
74 GettingRefcount(refcount::Error),
75 #[error("failed to parse filename: {0}")]
76 InvalidBackingFileName(str::Utf8Error),
77 #[error("invalid cluster index")]
78 InvalidClusterIndex,
79 #[error("invalid cluster size")]
80 InvalidClusterSize,
81 #[error("failed to parse header extension")]
82 InvalidHeaderExtension,
83 #[error("invalid index")]
84 InvalidIndex,
85 #[error("invalid L1 table offset")]
86 InvalidL1TableOffset,
87 #[error("invalid L1 table size {0}")]
88 InvalidL1TableSize(u32),
89 #[error("invalid magic")]
90 InvalidMagic,
91 #[error("invalid offset")]
92 InvalidOffset(u64),
93 #[error("invalid refcount table offset")]
94 InvalidRefcountTableOffset,
95 #[error("invalid refcount table size: {0}")]
96 InvalidRefcountTableSize(u64),
97 #[error("backing file format extension is missing")]
98 MissingBackingFileFormat,
99 #[error("no free clusters")]
100 NoFreeClusters,
101 #[error("no refcount clusters")]
102 NoRefcountClusters,
103 #[error("not enough space for refcounts")]
104 NotEnoughSpaceForRefcounts,
105 #[error("failed to open file: {0}")]
106 OpeningFile(io::Error),
107 #[error("failed to open file: {0}")]
108 ReadingHeader(io::Error),
109 #[error("failed to read pointers: {0}")]
110 ReadingPointers(io::Error),
111 #[error("failed to read ref count block: {0}")]
112 ReadingRefCountBlock(refcount::Error),
113 #[error("failed to read ref counts: {0}")]
114 ReadingRefCounts(io::Error),
115 #[error("failed to rebuild ref counts: {0}")]
116 RebuildingRefCounts(io::Error),
117 #[error("refcount table offset past file end")]
118 RefcountTableOffEnd,
119 #[error("too many clusters specified for refcount table")]
120 RefcountTableTooLarge,
121 #[error("failed to seek file: {0}")]
122 SeekingFile(io::Error),
123 #[error("failed to set refcount refcount: {0}")]
124 SettingRefcountRefcount(io::Error),
125 #[error("size too small for number of clusters")]
126 SizeTooSmallForNumberOfClusters,
127 #[error("snapshots not supported")]
128 SnapshotsNotSupported,
129 #[error("l1 entry table too large: {0}")]
130 TooManyL1Entries(u64),
131 #[error("ref count table too large: {0}")]
132 TooManyRefcounts(u64),
133 #[error("unsupported backing file format: {0}")]
134 UnsupportedBackingFileFormat(String),
135 #[error("unsupported incompatible features: {0:x}")]
136 UnsupportedIncompatibleFeatures(u64),
137 #[error("unsupported refcount order")]
138 UnsupportedRefcountOrder,
139 #[error("unsupported version: {0}")]
140 UnsupportedVersion(u32),
141 #[error("failed to write header: {0}")]
142 WritingHeader(io::Error),
143}
144
145pub type Result<T> = std::result::Result<T, Error>;
146
147const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; pub const QCOW_MAGIC: u32 = 0x5146_49fb;
152const DEFAULT_CLUSTER_BITS: u32 = 16;
154const MIN_CLUSTER_BITS: u32 = 9;
157const MAX_CLUSTER_BITS: u32 = 21;
158const MAX_RAM_POINTER_TABLE_SIZE: u64 = 35_000_000;
162const DEFAULT_REFCOUNT_ORDER: u32 = 4;
164
165const V3_BARE_HEADER_SIZE: u32 = 104;
166
167const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
169const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
170const ZERO_CLUSTER_FLAG: u64 = 1;
172const COMPRESSED_FLAG: u64 = 1 << 62;
173const CLUSTER_USED_FLAG: u64 = 1 << 63;
174const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1 << 0;
175
176const QCOW2_EXT_MAGIC_BACKING_FORMAT: u32 = 0xe279_2aca;
178
179const MAX_BACKING_FILE_SIZE: u32 = 1023;
181
182#[derive(Clone, Debug)]
184pub struct QcowHeader {
185 pub magic: u32,
186 pub version: u32,
187
188 pub backing_file_offset: u64,
189 pub backing_file_size: u32,
190
191 pub cluster_bits: u32,
192 pub size: u64,
193 pub crypt_method: u32,
194
195 pub l1_size: u32,
196 pub l1_table_offset: u64,
197
198 pub refcount_table_offset: u64,
199 pub refcount_table_clusters: u32,
200
201 pub nb_snapshots: u32,
202 pub snapshots_offset: u64,
203
204 pub incompatible_features: u64,
206 pub compatible_features: u64,
207 pub autoclear_features: u64,
208 pub refcount_order: u32,
209 pub header_size: u32,
210
211 pub backing_file_path: Option<String>,
213 pub backing_file_format: Option<crate::ImageType>,
214}
215
216fn read_u16_from_file(mut f: &File) -> Result<u16> {
218 let mut value = [0u8; 2];
219 (&mut f)
220 .read_exact(&mut value)
221 .map_err(Error::ReadingHeader)?;
222 Ok(u16::from_be_bytes(value))
223}
224
225fn read_u32_from_file(mut f: &File) -> Result<u32> {
227 let mut value = [0u8; 4];
228 (&mut f)
229 .read_exact(&mut value)
230 .map_err(Error::ReadingHeader)?;
231 Ok(u32::from_be_bytes(value))
232}
233
234fn read_u64_from_file(mut f: &File) -> Result<u64> {
236 let mut value = [0u8; 8];
237 (&mut f)
238 .read_exact(&mut value)
239 .map_err(Error::ReadingHeader)?;
240 Ok(u64::from_be_bytes(value))
241}
242
243fn image_type_to_format_str(image_type: crate::ImageType) -> Result<&'static str> {
244 match image_type {
245 crate::ImageType::Raw => Ok("raw"),
246 crate::ImageType::Qcow2 => Ok("qcow2"),
247 crate::ImageType::CompositeDisk => Ok("composite"),
248 other => Err(Error::UnsupportedBackingFileFormat(format!("{other:?}"))),
249 }
250}
251
252fn format_str_to_image_type(s: &str) -> Result<crate::ImageType> {
253 match s {
254 "raw" => Ok(crate::ImageType::Raw),
255 "qcow2" => Ok(crate::ImageType::Qcow2),
256 "composite" => Ok(crate::ImageType::CompositeDisk),
257 other => Err(Error::UnsupportedBackingFileFormat(other.to_string())),
258 }
259}
260
261impl QcowHeader {
262 pub fn new(f: &mut File) -> Result<QcowHeader> {
264 f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
265
266 let magic = read_u32_from_file(f)?;
267 if magic != QCOW_MAGIC {
268 return Err(Error::InvalidMagic);
269 }
270
271 let mut header = QcowHeader {
272 magic,
273 version: read_u32_from_file(f)?,
274 backing_file_offset: read_u64_from_file(f)?,
275 backing_file_size: read_u32_from_file(f)?,
276 cluster_bits: read_u32_from_file(f)?,
277 size: read_u64_from_file(f)?,
278 crypt_method: read_u32_from_file(f)?,
279 l1_size: read_u32_from_file(f)?,
280 l1_table_offset: read_u64_from_file(f)?,
281 refcount_table_offset: read_u64_from_file(f)?,
282 refcount_table_clusters: read_u32_from_file(f)?,
283 nb_snapshots: read_u32_from_file(f)?,
284 snapshots_offset: read_u64_from_file(f)?,
285 incompatible_features: read_u64_from_file(f)?,
286 compatible_features: read_u64_from_file(f)?,
287 autoclear_features: read_u64_from_file(f)?,
288 refcount_order: read_u32_from_file(f)?,
289 header_size: read_u32_from_file(f)?,
290 backing_file_path: None,
291 backing_file_format: None,
292 };
293
294 if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&header.cluster_bits) {
295 return Err(Error::InvalidClusterSize);
296 }
297
298 let cluster_size = 1u64 << header.cluster_bits;
299
300 if header.backing_file_offset != 0 {
301 let backing_file_end = header
302 .backing_file_offset
303 .checked_add(header.backing_file_size as u64)
304 .ok_or(Error::BackingFileEscapesFirstCluster)?;
305 if backing_file_end > cluster_size {
306 return Err(Error::BackingFileEscapesFirstCluster);
307 }
308 }
309
310 let (backing_file_format, backing_file_path) = Self::parse_backing_info(
311 f,
312 header.header_size,
313 cluster_size,
314 header.backing_file_offset,
315 header.backing_file_size,
316 )?;
317 header.backing_file_format = backing_file_format;
318 header.backing_file_path = backing_file_path;
319 Ok(header)
320 }
321
322 fn parse_backing_info(
323 f: &mut File,
324 header_size: u32,
325 cluster_size: u64,
326 backing_file_offset: u64,
327 backing_file_size: u32,
328 ) -> Result<(Option<crate::ImageType>, Option<String>)> {
329 let mut backing_file_format = None;
330 let mut ext_offset = (header_size as u64 + 7) & !7;
331
332 let limit = if backing_file_offset != 0 {
334 backing_file_offset
335 } else {
336 cluster_size
337 };
338
339 loop {
340 if ext_offset >= limit {
341 break;
342 }
343
344 let next_offset = ext_offset
345 .checked_add(8)
346 .ok_or(Error::InvalidHeaderExtension)?;
347 if next_offset > limit {
348 return Err(Error::InvalidHeaderExtension);
349 }
350
351 f.seek(SeekFrom::Start(ext_offset))
352 .map_err(Error::ReadingHeader)?;
353 let ext_type = read_u32_from_file(f)?;
354 let ext_len = read_u32_from_file(f)?;
355 if ext_type == 0 {
356 break;
357 }
358
359 let ext_end = next_offset
360 .checked_add(ext_len as u64)
361 .ok_or(Error::InvalidHeaderExtension)?;
362 if ext_end > limit {
363 return Err(Error::InvalidHeaderExtension);
364 }
365
366 if ext_type == QCOW2_EXT_MAGIC_BACKING_FORMAT {
367 let mut backing_format_bytes = vec![0u8; ext_len as usize];
368 f.read_exact(&mut backing_format_bytes)
369 .map_err(Error::ReadingHeader)?;
370 let format_str = String::from_utf8(backing_format_bytes)
371 .map_err(|_| Error::InvalidHeaderExtension)?;
372 let format = format_str_to_image_type(&format_str)?;
373 backing_file_format = Some(format);
374 }
375
376 let padded_len = (ext_len as u64 + 7) & !7;
377 let increment = padded_len
378 .checked_add(8)
379 .ok_or(Error::InvalidHeaderExtension)?;
380 ext_offset = ext_offset
381 .checked_add(increment)
382 .ok_or(Error::InvalidHeaderExtension)?;
383 }
384
385 if backing_file_size > MAX_BACKING_FILE_SIZE {
386 return Err(Error::BackingFileTooLong(backing_file_size as usize));
387 }
388 let backing_file_path = if backing_file_offset != 0 {
389 f.seek(SeekFrom::Start(backing_file_offset))
390 .map_err(Error::ReadingHeader)?;
391 let mut backing_file_name_bytes = vec![0u8; backing_file_size as usize];
392 f.read_exact(&mut backing_file_name_bytes)
393 .map_err(Error::ReadingHeader)?;
394 Some(
395 String::from_utf8(backing_file_name_bytes)
396 .map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?,
397 )
398 } else {
399 None
400 };
401
402 Ok((backing_file_format, backing_file_path))
403 }
404
405 pub fn create_for_size_and_path(
406 size: u64,
407 backing: Option<(&str, crate::ImageType)>,
408 ) -> Result<QcowHeader> {
409 let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
410 let cluster_size: u32 = 0x01 << cluster_bits;
411 let mut ext_size = 8; let (backing_file, backing_file_format) = match backing {
413 Some((path, format)) => {
414 let format_str = image_type_to_format_str(format)?;
415 let format_len = format_str.len() as u32;
416 ext_size += 8 + ((format_len + 7) & !7);
417 (Some(path), Some(format))
418 }
419 None => (None, None),
420 };
421 let max_length: usize = (cluster_size - V3_BARE_HEADER_SIZE - ext_size) as usize;
422 if let Some(path) = backing_file {
423 if path.len() > max_length {
424 return Err(Error::BackingFileTooLong(path.len() - max_length));
425 }
426 }
427 if size > MAX_QCOW_FILE_SIZE {
428 return Err(Error::FileTooBig(size));
429 }
430 let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
432 let num_clusters: u32 = size.div_ceil(u64::from(cluster_size)) as u32;
433 let num_l2_clusters: u32 = num_clusters.div_ceil(l2_size);
434 let l1_clusters: u32 = num_l2_clusters.div_ceil(cluster_size);
435 let header_clusters = (size_of::<QcowHeader>() as u32).div_ceil(cluster_size);
436 Ok(QcowHeader {
437 magic: QCOW_MAGIC,
438 version: 3,
439 backing_file_offset: if backing_file.is_none() {
440 0
441 } else {
442 V3_BARE_HEADER_SIZE as u64 + ext_size as u64
443 },
444 backing_file_size: backing_file.map_or(0, |x| x.len()) as u32,
445 cluster_bits: DEFAULT_CLUSTER_BITS,
446 size,
447 crypt_method: 0,
448 l1_size: num_l2_clusters,
449 l1_table_offset: u64::from(cluster_size),
450 refcount_table_offset: u64::from(cluster_size * (l1_clusters + 1)),
452 refcount_table_clusters: {
453 let max_refcount_clusters = max_refcount_clusters(
457 DEFAULT_REFCOUNT_ORDER,
458 cluster_size,
459 num_clusters + l1_clusters + num_l2_clusters + header_clusters,
460 ) as u32;
461 (max_refcount_clusters * size_of::<u64>() as u32).div_ceil(cluster_size)
463 },
464 nb_snapshots: 0,
465 snapshots_offset: 0,
466 incompatible_features: 0,
467 compatible_features: 0,
468 autoclear_features: 0,
469 refcount_order: DEFAULT_REFCOUNT_ORDER,
470 header_size: V3_BARE_HEADER_SIZE,
471 backing_file_path: backing_file.map(String::from),
472 backing_file_format,
473 })
474 }
475
476 pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> {
478 fn write_u32_to_file<F: Write>(f: &mut F, value: u32) -> Result<()> {
480 f.write_all(&value.to_be_bytes())
481 .map_err(Error::WritingHeader)
482 }
483
484 fn write_u64_to_file<F: Write>(f: &mut F, value: u64) -> Result<()> {
486 f.write_all(&value.to_be_bytes())
487 .map_err(Error::WritingHeader)
488 }
489
490 write_u32_to_file(file, self.magic)?;
491 write_u32_to_file(file, self.version)?;
492 write_u64_to_file(file, self.backing_file_offset)?;
493 write_u32_to_file(file, self.backing_file_size)?;
494 write_u32_to_file(file, self.cluster_bits)?;
495 write_u64_to_file(file, self.size)?;
496 write_u32_to_file(file, self.crypt_method)?;
497 write_u32_to_file(file, self.l1_size)?;
498 write_u64_to_file(file, self.l1_table_offset)?;
499 write_u64_to_file(file, self.refcount_table_offset)?;
500 write_u32_to_file(file, self.refcount_table_clusters)?;
501 write_u32_to_file(file, self.nb_snapshots)?;
502 write_u64_to_file(file, self.snapshots_offset)?;
503 write_u64_to_file(file, self.incompatible_features)?;
504 write_u64_to_file(file, self.compatible_features)?;
505 write_u64_to_file(file, self.autoclear_features)?;
506 write_u32_to_file(file, self.refcount_order)?;
507 write_u32_to_file(file, self.header_size)?;
508 if let Some(format) = self.backing_file_format {
509 let format_str = image_type_to_format_str(format)?;
510 write_u32_to_file(file, QCOW2_EXT_MAGIC_BACKING_FORMAT)?;
511 write_u32_to_file(file, format_str.len() as u32)?;
512 file.write_all(format_str.as_bytes())
513 .map_err(Error::WritingHeader)?;
514 let padding = ((format_str.len() + 7) & !7) - format_str.len();
516 if padding > 0 {
517 const ZEROES: [u8; 8] = [0u8; 8];
518 file.write_all(&ZEROES[..padding])
519 .map_err(Error::WritingHeader)?;
520 }
521 }
522 write_u32_to_file(file, 0)?; write_u32_to_file(file, 0)?; if let Some(backing_file_path) = self.backing_file_path.as_ref() {
525 write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?;
526 }
527
528 let cluster_size = 0x01u64 << self.cluster_bits;
532 let refcount_blocks_size = u64::from(self.refcount_table_clusters) * cluster_size;
533 file.seek(SeekFrom::Start(
534 self.refcount_table_offset + refcount_blocks_size - 2,
535 ))
536 .map_err(Error::WritingHeader)?;
537 file.write(&[0u8]).map_err(Error::WritingHeader)?;
538
539 Ok(())
540 }
541}
542
543fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 {
544 let refcount_bytes = (0x01 << refcount_order as u64) / 8;
546 let for_data = (u64::from(num_clusters) * refcount_bytes).div_ceil(u64::from(cluster_size));
547 let for_refcounts = (for_data * refcount_bytes).div_ceil(u64::from(cluster_size));
548 for_data + for_refcounts
549}
550
551#[derive(Debug)]
574pub struct QcowFile {
575 inner: Mutex<QcowFileInner>,
576 virtual_size: u64,
578}
579
580#[derive(Debug)]
581struct QcowFileInner {
582 raw_file: QcowRawFile,
583 header: QcowHeader,
584 l1_table: VecCache<u64>,
585 l2_entries: u64,
586 l2_cache: CacheMap<VecCache<L2CacheEntry>>,
587 refcounts: RefCount,
588 current_offset: u64,
589 unref_clusters: Vec<u64>, avail_clusters: Vec<u64>,
593 backing_file: Option<Box<dyn DiskFile>>,
594}
595
596#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
598struct L2CacheEntry(u64);
599
600impl L2CacheEntry {
601 const UNALLOCATED: Self = Self(0);
602 const ZERO: Self = Self(ZERO_CLUSTER_FLAG);
603
604 fn new_data(cluster_addr: u64) -> Self {
605 debug_assert_eq!(cluster_addr & !L2_TABLE_OFFSET_MASK, 0);
606 Self(cluster_addr)
607 }
608
609 fn from_disk(raw: u64) -> std::io::Result<Self> {
610 if raw & COMPRESSED_FLAG != 0 {
611 return Err(std::io::Error::from_raw_os_error(ENOTSUP));
612 }
613 Ok(Self(raw & (L2_TABLE_OFFSET_MASK | ZERO_CLUSTER_FLAG)))
614 }
615
616 fn to_disk(self) -> u64 {
617 if self.cluster_addr().is_some() {
618 self.0 | CLUSTER_USED_FLAG
619 } else {
620 self.0
621 }
622 }
623
624 fn cluster_addr(self) -> Option<u64> {
625 let addr = self.0 & L2_TABLE_OFFSET_MASK;
626 (addr != 0).then_some(addr)
627 }
628
629 fn is_zero(self) -> bool {
630 self.0 & ZERO_CLUSTER_FLAG != 0
631 }
632}
633
634enum ClusterLocation {
636 Data(u64),
638 Unallocated,
640 Zero,
642}
643
644impl DiskFile for QcowFile {}
645
646impl DiskFlush for QcowFile {
647 fn flush(&self) -> io::Result<()> {
648 self.fsync()
651 }
652}
653
654impl QcowFile {
655 pub fn from(mut file: File, params: DiskFileParams) -> Result<QcowFile> {
657 let header = QcowHeader::new(&mut file)?;
658
659 if header.version != 3 {
661 return Err(Error::UnsupportedVersion(header.version));
662 }
663
664 if header.incompatible_features != 0 {
665 return Err(Error::UnsupportedIncompatibleFeatures(
666 header.incompatible_features,
667 ));
668 }
669
670 if header.nb_snapshots != 0 {
671 return Err(Error::SnapshotsNotSupported);
672 }
673
674 if u64::from(header.l1_size) > MAX_RAM_POINTER_TABLE_SIZE {
676 return Err(Error::InvalidL1TableSize(header.l1_size));
677 }
678
679 let cluster_bits: u32 = header.cluster_bits;
680 if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
681 return Err(Error::InvalidClusterSize);
682 }
683 let cluster_size = 0x01u64 << cluster_bits;
684
685 if header.size > MAX_QCOW_FILE_SIZE {
687 return Err(Error::FileTooBig(header.size));
688 }
689
690 let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
691 let backing_params = DiskFileParams {
692 path: PathBuf::from(backing_file_path),
693 is_read_only: true,
695 is_direct: params.is_direct,
698 lock: params.lock,
699 depth: params.depth + 1,
700 ..Default::default()
701 };
702 let backing_image_type = match header.backing_file_format {
703 Some(format) => format,
704 None => return Err(Error::MissingBackingFileFormat),
705 };
706 let backing_disk_file = crate::open_disk_file_as(backing_params, backing_image_type)
707 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
708 Some(backing_disk_file)
709 } else {
710 None
711 };
712
713 let refcount_bits: u64 = 0x01u64
715 .checked_shl(header.refcount_order)
716 .ok_or(Error::UnsupportedRefcountOrder)?;
717 if refcount_bits != 16 {
718 return Err(Error::UnsupportedRefcountOrder);
719 }
720 let refcount_bytes = refcount_bits.div_ceil(8);
721
722 if header.refcount_table_clusters == 0 {
724 return Err(Error::NoRefcountClusters);
725 }
726 offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
727 offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
728 offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
730 let file_size = file.metadata().map_err(Error::GettingFileSize)?.len();
731 if header.refcount_table_offset > max(file_size, header.size) {
732 return Err(Error::RefcountTableOffEnd);
733 }
734
735 let mut refcount_rebuild_required = true;
738 file.seek(SeekFrom::Start(header.refcount_table_offset))
739 .map_err(Error::SeekingFile)?;
740 let first_refblock_addr = read_u64_from_file(&file)?;
741 if first_refblock_addr != 0 {
742 file.seek(SeekFrom::Start(first_refblock_addr))
743 .map_err(Error::SeekingFile)?;
744 let first_cluster_refcount = read_u16_from_file(&file)?;
745 if first_cluster_refcount != 0 {
746 refcount_rebuild_required = false;
747 }
748 }
749
750 if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
751 refcount_rebuild_required = true;
752 }
753
754 let mut raw_file =
755 QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
756 if refcount_rebuild_required {
757 QcowFileInner::rebuild_refcounts(&mut raw_file, header.clone())?;
758 }
759
760 let l2_size = cluster_size / size_of::<u64>() as u64;
761 let num_clusters = header.size.div_ceil(cluster_size);
762 let num_l2_clusters = num_clusters.div_ceil(l2_size);
763 let l1_clusters = num_l2_clusters.div_ceil(cluster_size);
764 let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
765 if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
766 return Err(Error::TooManyL1Entries(num_l2_clusters));
767 }
768 let l1_table = VecCache::from_vec(
769 raw_file
770 .read_pointer_table(
771 header.l1_table_offset,
772 num_l2_clusters,
773 Some(L1_TABLE_OFFSET_MASK),
774 )
775 .map_err(Error::ReadingHeader)?,
776 );
777
778 let num_clusters = header.size.div_ceil(cluster_size);
779 let refcount_clusters = max_refcount_clusters(
780 header.refcount_order,
781 cluster_size as u32,
782 (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
783 );
784 if u64::from(header.refcount_table_clusters) > 2 * refcount_clusters {
786 return Err(Error::RefcountTableTooLarge);
787 }
788 if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
789 return Err(Error::TooManyRefcounts(refcount_clusters));
790 }
791 let refcount_block_entries = cluster_size / refcount_bytes;
792 let refcounts = RefCount::new(
793 &mut raw_file,
794 header.refcount_table_offset,
795 refcount_clusters,
796 refcount_block_entries,
797 cluster_size,
798 )
799 .map_err(Error::ReadingRefCounts)?;
800
801 let l2_entries = cluster_size / size_of::<u64>() as u64;
802
803 let mut inner = QcowFileInner {
804 raw_file,
805 header,
806 l1_table,
807 l2_entries,
808 l2_cache: CacheMap::new(100),
809 refcounts,
810 current_offset: 0,
811 unref_clusters: Vec::new(),
812 avail_clusters: Vec::new(),
813 backing_file,
814 };
815
816 inner
818 .header
819 .l1_table_offset
820 .checked_add(inner.l1_address_offset(inner.virtual_size()))
821 .ok_or(Error::InvalidL1TableOffset)?;
822 inner
823 .header
824 .refcount_table_offset
825 .checked_add(u64::from(inner.header.refcount_table_clusters) * cluster_size)
826 .ok_or(Error::InvalidRefcountTableOffset)?;
827
828 inner.find_avail_clusters()?;
829
830 let virtual_size = inner.virtual_size();
831 Ok(QcowFile {
832 inner: Mutex::new(inner),
833 virtual_size,
834 })
835 }
836
837 pub fn new(file: File, params: DiskFileParams, virtual_size: u64) -> Result<QcowFile> {
839 let header = QcowHeader::create_for_size_and_path(virtual_size, None)?;
840 QcowFile::new_from_header(file, params, header)
841 }
842
843 pub fn new_from_backing(
845 file: File,
846 params: DiskFileParams,
847 backing_file_name: &str,
848 ) -> Result<QcowFile> {
849 let (size, backing_format) = {
852 let backing_params = DiskFileParams {
853 path: PathBuf::from(backing_file_name),
854 is_read_only: true,
856 is_direct: params.is_direct,
859 lock: params.lock,
860 depth: params.depth + 1,
861 ..Default::default()
862 };
863 let backing_file = crate::sys::open_raw_disk_image(&backing_params)
864 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
865 let backing_image_type = crate::detect_image_type(&backing_file, false)
866 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
867 let backing_disk_file =
868 crate::disk_file_from_file(backing_file, backing_params, backing_image_type)
869 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
870 (
871 backing_disk_file.get_len().map_err(Error::BackingFileIo)?,
872 backing_image_type,
873 )
874 };
875 let header =
876 QcowHeader::create_for_size_and_path(size, Some((backing_file_name, backing_format)))?;
877 QcowFile::new_from_header(file, params, header)
878 }
879
880 fn new_from_header(
881 mut file: File,
882 params: DiskFileParams,
883 header: QcowHeader,
884 ) -> Result<QcowFile> {
885 file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
886 header.write_to(&mut file)?;
887
888 let mut qcow = Self::from(file, params)?;
889 let inner = qcow.inner.get_mut();
890
891 let cluster_size = 0x01u64 << inner.header.cluster_bits;
893 let refcount_table_base = inner.header.refcount_table_offset;
894 let end_cluster_addr =
895 refcount_table_base + u64::from(inner.header.refcount_table_clusters) * cluster_size;
896
897 let mut cluster_addr = 0;
898 while cluster_addr < end_cluster_addr {
899 let mut unref_clusters = inner
900 .set_cluster_refcount(cluster_addr, 1)
901 .map_err(Error::SettingRefcountRefcount)?;
902 inner.unref_clusters.append(&mut unref_clusters);
903 cluster_addr += cluster_size;
904 }
905
906 Ok(qcow)
907 }
908
909 pub fn set_backing_file(&mut self, backing: Option<Box<dyn DiskFile>>) {
910 self.inner.get_mut().backing_file = backing;
911 }
912}
913
914impl QcowFileInner {
915 #[cfg(test)]
917 fn first_zero_refcount(&mut self) -> Result<Option<u64>> {
918 let file_size = self
919 .raw_file
920 .file_mut()
921 .metadata()
922 .map_err(Error::GettingFileSize)?
923 .len();
924 let cluster_size = 0x01u64 << self.header.cluster_bits;
925
926 let mut cluster_addr = 0;
927 while cluster_addr < file_size {
928 let cluster_refcount = self
929 .refcounts
930 .get_cluster_refcount(&mut self.raw_file, cluster_addr)
931 .map_err(Error::GettingRefcount)?;
932 if cluster_refcount == 0 {
933 return Ok(Some(cluster_addr));
934 }
935 cluster_addr += cluster_size;
936 }
937 Ok(None)
938 }
939
940 fn find_avail_clusters(&mut self) -> Result<()> {
941 let cluster_size = self.raw_file.cluster_size();
942
943 let file_size = self
944 .raw_file
945 .file_mut()
946 .metadata()
947 .map_err(Error::GettingFileSize)?
948 .len();
949
950 for i in (0..file_size).step_by(cluster_size as usize) {
951 let refcount = self
952 .refcounts
953 .get_cluster_refcount(&mut self.raw_file, i)
954 .map_err(Error::GettingRefcount)?;
955 if refcount == 0 {
956 self.avail_clusters.push(i);
957 }
958 }
959
960 Ok(())
961 }
962
963 fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()> {
965 fn add_ref(refcounts: &mut [u16], cluster_size: u64, cluster_address: u64) -> Result<()> {
966 let idx = (cluster_address / cluster_size) as usize;
967 if idx >= refcounts.len() {
968 return Err(Error::InvalidClusterIndex);
969 }
970 refcounts[idx] += 1;
971 Ok(())
972 }
973
974 fn set_header_refcount(refcounts: &mut [u16], cluster_size: u64) -> Result<()> {
976 add_ref(refcounts, cluster_size, 0)
977 }
978
979 fn set_l1_refcounts(
981 refcounts: &mut [u16],
982 header: QcowHeader,
983 cluster_size: u64,
984 ) -> Result<()> {
985 let l1_clusters = u64::from(header.l1_size).div_ceil(cluster_size);
986 let l1_table_offset = header.l1_table_offset;
987 for i in 0..l1_clusters {
988 add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
989 }
990 Ok(())
991 }
992
993 fn set_data_refcounts(
995 refcounts: &mut [u16],
996 header: QcowHeader,
997 cluster_size: u64,
998 raw_file: &mut QcowRawFile,
999 ) -> Result<()> {
1000 let l1_table = raw_file
1001 .read_pointer_table(
1002 header.l1_table_offset,
1003 header.l1_size as u64,
1004 Some(L1_TABLE_OFFSET_MASK),
1005 )
1006 .map_err(Error::ReadingPointers)?;
1007 for l1_index in 0..header.l1_size as usize {
1008 let l2_addr_disk = *l1_table.get(l1_index).ok_or(Error::InvalidIndex)?;
1009 if l2_addr_disk != 0 {
1010 add_ref(refcounts, cluster_size, l2_addr_disk)?;
1012
1013 let l2_table = raw_file
1015 .read_pointer_table(
1016 l2_addr_disk,
1017 cluster_size / size_of::<u64>() as u64,
1018 Some(L2_TABLE_OFFSET_MASK),
1019 )
1020 .map_err(Error::ReadingPointers)?;
1021 for data_cluster_addr in l2_table {
1022 if data_cluster_addr != 0 {
1023 add_ref(refcounts, cluster_size, data_cluster_addr)?;
1024 }
1025 }
1026 }
1027 }
1028
1029 Ok(())
1030 }
1031
1032 fn set_refcount_table_refcounts(
1034 refcounts: &mut [u16],
1035 header: QcowHeader,
1036 cluster_size: u64,
1037 ) -> Result<()> {
1038 let refcount_table_offset = header.refcount_table_offset;
1039 for i in 0..header.refcount_table_clusters as u64 {
1040 add_ref(
1041 refcounts,
1042 cluster_size,
1043 refcount_table_offset + i * cluster_size,
1044 )?;
1045 }
1046 Ok(())
1047 }
1048
1049 fn alloc_refblocks(
1053 refcounts: &mut [u16],
1054 cluster_size: u64,
1055 refblock_clusters: u64,
1056 pointers_per_cluster: u64,
1057 ) -> Result<Vec<u64>> {
1058 let refcount_table_entries = refblock_clusters.div_ceil(pointers_per_cluster);
1059 let mut ref_table = vec![0; refcount_table_entries as usize];
1060 let mut first_free_cluster: u64 = 0;
1061 for refblock_addr in &mut ref_table {
1062 loop {
1063 if first_free_cluster >= refcounts.len() as u64 {
1064 return Err(Error::NotEnoughSpaceForRefcounts);
1065 }
1066 if refcounts[first_free_cluster as usize] == 0 {
1067 break;
1068 }
1069 first_free_cluster += 1;
1070 }
1071
1072 *refblock_addr = first_free_cluster * cluster_size;
1073 add_ref(refcounts, cluster_size, *refblock_addr)?;
1074
1075 first_free_cluster += 1;
1076 }
1077
1078 Ok(ref_table)
1079 }
1080
1081 fn write_refblocks(
1083 refcounts: &[u16],
1084 mut header: QcowHeader,
1085 ref_table: &[u64],
1086 raw_file: &mut QcowRawFile,
1087 refcount_block_entries: u64,
1088 ) -> Result<()> {
1089 header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
1091 raw_file
1092 .file_mut()
1093 .seek(SeekFrom::Start(0))
1094 .map_err(Error::SeekingFile)?;
1095 header.write_to(raw_file.file_mut())?;
1096
1097 for (i, refblock_addr) in ref_table.iter().enumerate() {
1098 let refblock_start = i * (refcount_block_entries as usize);
1100 let refblock_end = min(
1101 refcounts.len(),
1102 refblock_start + refcount_block_entries as usize,
1103 );
1104 let refblock = &refcounts[refblock_start..refblock_end];
1105 raw_file
1106 .write_refcount_block(*refblock_addr, refblock)
1107 .map_err(Error::WritingHeader)?;
1108
1109 if refblock.len() < refcount_block_entries as usize {
1111 let refblock_padding =
1112 vec![0u16; refcount_block_entries as usize - refblock.len()];
1113 raw_file
1114 .write_refcount_block(
1115 *refblock_addr + refblock.len() as u64 * 2,
1116 &refblock_padding,
1117 )
1118 .map_err(Error::WritingHeader)?;
1119 }
1120 }
1121
1122 raw_file
1124 .write_pointer_table(header.refcount_table_offset, ref_table, 0)
1125 .map_err(Error::WritingHeader)?;
1126
1127 header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
1129 raw_file
1130 .file_mut()
1131 .seek(SeekFrom::Start(0))
1132 .map_err(Error::SeekingFile)?;
1133 header.write_to(raw_file.file_mut())?;
1134
1135 Ok(())
1136 }
1137
1138 let cluster_size = raw_file.cluster_size();
1139
1140 let file_size = raw_file
1141 .file_mut()
1142 .metadata()
1143 .map_err(Error::GettingFileSize)?
1144 .len();
1145
1146 let refcount_bits = 1u64 << header.refcount_order;
1147 let refcount_bytes = refcount_bits.div_ceil(8);
1148 let refcount_block_entries = cluster_size / refcount_bytes;
1149 let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
1150 let data_clusters = header.size.div_ceil(cluster_size);
1151 let l2_clusters = data_clusters.div_ceil(pointers_per_cluster);
1152 let l1_clusters = l2_clusters.div_ceil(cluster_size);
1153 let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
1154 let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
1155 let mut max_valid_cluster_index = max_clusters;
1156 let refblock_clusters = max_valid_cluster_index.div_ceil(refcount_block_entries);
1157 let reftable_clusters = refblock_clusters.div_ceil(pointers_per_cluster);
1158 let refblocks_for_refs =
1160 (refblock_clusters + reftable_clusters).div_ceil(refcount_block_entries);
1161 let reftable_clusters_for_refs = refblocks_for_refs.div_ceil(refcount_block_entries);
1162 max_valid_cluster_index += refblock_clusters + reftable_clusters;
1163 max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs;
1164
1165 if max_valid_cluster_index > MAX_RAM_POINTER_TABLE_SIZE {
1166 return Err(Error::InvalidRefcountTableSize(max_valid_cluster_index));
1167 }
1168
1169 let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
1170 if max_valid_cluster_offset < file_size - cluster_size {
1171 return Err(Error::InvalidRefcountTableSize(max_valid_cluster_offset));
1172 }
1173
1174 let mut refcounts = vec![0; max_valid_cluster_index as usize];
1175
1176 set_header_refcount(&mut refcounts, cluster_size)?;
1178 set_l1_refcounts(&mut refcounts, header.clone(), cluster_size)?;
1179 set_data_refcounts(&mut refcounts, header.clone(), cluster_size, raw_file)?;
1180 set_refcount_table_refcounts(&mut refcounts, header.clone(), cluster_size)?;
1181
1182 let ref_table = alloc_refblocks(
1184 &mut refcounts,
1185 cluster_size,
1186 refblock_clusters,
1187 pointers_per_cluster,
1188 )?;
1189
1190 write_refblocks(
1192 &refcounts,
1193 header,
1194 &ref_table,
1195 raw_file,
1196 refcount_block_entries,
1197 )
1198 }
1199
1200 fn limit_range_file(&self, address: u64, count: usize) -> usize {
1202 if address.checked_add(count as u64).is_none() || address > self.virtual_size() {
1203 return 0;
1204 }
1205 min(count as u64, self.virtual_size() - address) as usize
1206 }
1207
1208 fn limit_range_cluster(&self, address: u64, count: usize) -> usize {
1210 let offset: u64 = self.raw_file.cluster_offset(address);
1211 let limit = self.raw_file.cluster_size() - offset;
1212 min(count as u64, limit) as usize
1213 }
1214
1215 fn virtual_size(&self) -> u64 {
1217 self.header.size
1218 }
1219
1220 fn l1_address_offset(&self, address: u64) -> u64 {
1222 let l1_index = self.l1_table_index(address);
1223 l1_index * size_of::<u64>() as u64
1224 }
1225
1226 fn l1_table_index(&self, address: u64) -> u64 {
1228 (address / self.raw_file.cluster_size()) / self.l2_entries
1229 }
1230
1231 fn l2_table_index(&self, address: u64) -> u64 {
1233 (address / self.raw_file.cluster_size()) % self.l2_entries
1234 }
1235
1236 fn file_offset_read(&mut self, address: u64) -> std::io::Result<ClusterLocation> {
1239 if address >= self.virtual_size() {
1240 return Err(std::io::Error::from_raw_os_error(EINVAL));
1241 }
1242
1243 let l1_index = self.l1_table_index(address) as usize;
1244 let l2_addr_disk = *self
1245 .l1_table
1246 .get(l1_index)
1247 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1248
1249 if l2_addr_disk == 0 {
1250 return Ok(ClusterLocation::Unallocated);
1251 }
1252
1253 let l2_index = self.l2_table_index(address) as usize;
1254
1255 if !self.l2_cache.contains_key(&l1_index) {
1256 let table =
1258 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
1259
1260 let l1_table = &self.l1_table;
1261 let raw_file = &mut self.raw_file;
1262 self.l2_cache.insert(l1_index, table, |index, evicted| {
1263 Self::write_l2_cluster(raw_file, l1_table[index], evicted.get_values())
1264 })?;
1265 };
1266
1267 let entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1268 if entry.is_zero() {
1269 return Ok(ClusterLocation::Zero);
1270 }
1271 match entry.cluster_addr() {
1272 Some(cluster_addr) => Ok(ClusterLocation::Data(
1273 cluster_addr + self.raw_file.cluster_offset(address),
1274 )),
1275 None => Ok(ClusterLocation::Unallocated),
1276 }
1277 }
1278
1279 fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
1282 if address >= self.virtual_size() {
1283 return Err(std::io::Error::from_raw_os_error(EINVAL));
1284 }
1285
1286 let l1_index = self.l1_table_index(address) as usize;
1287 let l2_addr_disk = *self
1288 .l1_table
1289 .get(l1_index)
1290 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1291 let l2_index = self.l2_table_index(address) as usize;
1292
1293 let mut set_refcounts = Vec::new();
1294
1295 if !self.l2_cache.contains_key(&l1_index) {
1296 let l2_table = if l2_addr_disk == 0 {
1298 let new_addr: u64 = self.get_new_cluster(None)?;
1301 set_refcounts.push((new_addr, 1));
1303 self.l1_table[l1_index] = new_addr;
1304 VecCache::new(self.l2_entries as usize)
1305 } else {
1306 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
1307 };
1308 let l1_table = &self.l1_table;
1309 let raw_file = &mut self.raw_file;
1310 self.l2_cache.insert(l1_index, l2_table, |index, evicted| {
1311 Self::write_l2_cluster(raw_file, l1_table[index], evicted.get_values())
1312 })?;
1313 }
1314
1315 let mut entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1316 if entry.is_zero() && entry.cluster_addr().is_some() {
1317 self.zero_cluster(address)?;
1321 entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1322 }
1323 let cluster_addr = match entry.cluster_addr() {
1324 None => {
1325 let initial_data = if entry.is_zero() {
1326 None
1328 } else if let Some(backing) = self.backing_file.as_mut() {
1329 let cluster_size = self.raw_file.cluster_size();
1330 let cluster_begin = address - (address % cluster_size);
1331 let mut cluster_data = vec![0u8; cluster_size as usize];
1332 let volatile_slice = VolatileSlice::new(&mut cluster_data);
1333 backing.read_exact_at_volatile(volatile_slice, cluster_begin)?;
1334 Some(cluster_data)
1335 } else {
1336 None
1337 };
1338 let cluster_addr = self.append_data_cluster(initial_data)?;
1340 self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
1341 cluster_addr
1342 }
1343 Some(a) => a,
1344 };
1345
1346 for (addr, count) in set_refcounts {
1347 let mut newly_unref = self.set_cluster_refcount(addr, count)?;
1348 self.unref_clusters.append(&mut newly_unref);
1349 }
1350
1351 Ok(cluster_addr + self.raw_file.cluster_offset(address))
1352 }
1353
1354 fn update_cluster_addr(
1356 &mut self,
1357 l1_index: usize,
1358 l2_index: usize,
1359 cluster_addr: u64,
1360 set_refcounts: &mut Vec<(u64, u16)>,
1361 ) -> io::Result<()> {
1362 if !self.l2_cache.get(&l1_index).unwrap().dirty() {
1363 let addr = self.l1_table[l1_index];
1368 if addr != 0 {
1369 self.unref_clusters.push(addr);
1370 set_refcounts.push((addr, 0));
1371 }
1372
1373 let new_addr: u64 = self.get_new_cluster(None)?;
1377 set_refcounts.push((new_addr, 1));
1380 self.l1_table[l1_index] = new_addr;
1381 }
1382 self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = L2CacheEntry::new_data(cluster_addr);
1384 Ok(())
1385 }
1386
1387 fn get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1389 if let Some(free_cluster) = self.avail_clusters.pop() {
1391 if let Some(initial_data) = initial_data {
1392 self.raw_file.write_cluster(free_cluster, initial_data)?;
1393 } else {
1394 self.raw_file.zero_cluster(free_cluster)?;
1395 }
1396 return Ok(free_cluster);
1397 }
1398
1399 let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
1400 if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
1401 if let Some(initial_data) = initial_data {
1402 self.raw_file.write_cluster(new_cluster, initial_data)?;
1403 }
1404 Ok(new_cluster)
1405 } else {
1406 error!("No free clusters in get_new_cluster()");
1407 Err(std::io::Error::from_raw_os_error(ENOSPC))
1408 }
1409 }
1410
1411 fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1414 let new_addr: u64 = self.get_new_cluster(initial_data)?;
1415 let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
1417 self.unref_clusters.append(&mut newly_unref);
1418 Ok(new_addr)
1419 }
1420
1421 fn zero_cluster(&mut self, address: u64) -> std::io::Result<()> {
1424 if address >= self.virtual_size() {
1425 return Err(std::io::Error::from_raw_os_error(EINVAL));
1426 }
1427
1428 let new_entry = if self.backing_file.is_some() {
1430 L2CacheEntry::ZERO
1431 } else {
1432 L2CacheEntry::UNALLOCATED
1433 };
1434
1435 let l1_index = self.l1_table_index(address) as usize;
1436 let l2_addr_disk = *self
1437 .l1_table
1438 .get(l1_index)
1439 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1440 let l2_index = self.l2_table_index(address) as usize;
1441
1442 if l2_addr_disk == 0 && new_entry == L2CacheEntry::UNALLOCATED {
1443 return Ok(());
1446 }
1447
1448 if !self.l2_cache.contains_key(&l1_index) {
1449 let table = if l2_addr_disk == 0 {
1451 let new_addr = self.get_new_cluster(None)?;
1453 let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
1454 self.unref_clusters.append(&mut newly_unref);
1455 self.l1_table[l1_index] = new_addr;
1456 VecCache::new(self.l2_entries as usize)
1457 } else {
1458 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
1459 };
1460 let l1_table = &self.l1_table;
1461 let raw_file = &mut self.raw_file;
1462 self.l2_cache.insert(l1_index, table, |index, evicted| {
1463 Self::write_l2_cluster(raw_file, l1_table[index], evicted.get_values())
1464 })?;
1465 }
1466
1467 let entry = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1468 if entry == new_entry {
1469 return Ok(());
1471 }
1472
1473 self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = new_entry;
1476
1477 let Some(cluster_addr) = entry.cluster_addr() else {
1478 return Ok(());
1480 };
1481
1482 let refcount = self
1484 .refcounts
1485 .get_cluster_refcount(&mut self.raw_file, cluster_addr)
1486 .map_err(|_| std::io::Error::from_raw_os_error(EINVAL))?;
1487 if refcount == 0 {
1488 return Err(std::io::Error::from_raw_os_error(EINVAL));
1489 }
1490
1491 let new_refcount = refcount - 1;
1492 let mut newly_unref = self.set_cluster_refcount(cluster_addr, new_refcount)?;
1493 self.unref_clusters.append(&mut newly_unref);
1494
1495 if new_refcount == 0 {
1496 let cluster_size = self.raw_file.cluster_size();
1497 let _ = self.raw_file.file().punch_hole(cluster_addr, cluster_size);
1501 self.unref_clusters.push(cluster_addr);
1502 }
1503 Ok(())
1504 }
1505
1506 fn zero_bytes(&mut self, address: u64, length: usize) -> std::io::Result<()> {
1510 let write_count: usize = self.limit_range_file(address, length);
1511
1512 let mut nwritten: usize = 0;
1513 while nwritten < write_count {
1514 let curr_addr = address + nwritten as u64;
1515 let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1516
1517 if count == self.raw_file.cluster_size() as usize {
1518 self.zero_cluster(curr_addr)?;
1520 } else {
1521 let offset = match self.file_offset_read(curr_addr)? {
1523 ClusterLocation::Data(offset) => Some(offset),
1524 ClusterLocation::Unallocated if self.backing_file.is_some() => {
1528 Some(self.file_offset_write(curr_addr)?)
1529 }
1530 ClusterLocation::Unallocated | ClusterLocation::Zero => None,
1533 };
1534 if let Some(offset) = offset {
1535 self.raw_file.file().write_zeroes_all_at(offset, count)?;
1537 }
1538 }
1539
1540 nwritten += count;
1541 }
1542 Ok(())
1543 }
1544
1545 fn read_l2_cluster(
1548 raw_file: &mut QcowRawFile,
1549 cluster_addr: u64,
1550 ) -> std::io::Result<Vec<L2CacheEntry>> {
1551 raw_file
1552 .read_pointer_cluster(cluster_addr, None)?
1553 .into_iter()
1554 .map(L2CacheEntry::from_disk)
1555 .collect()
1556 }
1557
1558 fn write_l2_cluster(
1560 raw_file: &mut QcowRawFile,
1561 cluster_addr: u64,
1562 table: &[L2CacheEntry],
1563 ) -> std::io::Result<()> {
1564 let file_values: Vec<u64> = table.iter().map(|entry| entry.to_disk()).collect();
1565 raw_file.write_pointer_table(cluster_addr, &file_values, 0)
1566 }
1567
1568 fn set_cluster_refcount(&mut self, address: u64, refcount: u16) -> std::io::Result<Vec<u64>> {
1572 let mut added_clusters = Vec::new();
1573 let mut unref_clusters = Vec::new();
1574 let mut refcount_set = false;
1575 let mut new_cluster = None;
1576
1577 while !refcount_set {
1578 match self.refcounts.set_cluster_refcount(
1579 &mut self.raw_file,
1580 address,
1581 refcount,
1582 new_cluster.take(),
1583 ) {
1584 Ok(None) => {
1585 refcount_set = true;
1586 }
1587 Ok(Some(freed_cluster)) => {
1588 unref_clusters.push(freed_cluster);
1589 refcount_set = true;
1590 }
1591 Err(refcount::Error::EvictingRefCounts(e)) => {
1592 return Err(e);
1593 }
1594 Err(refcount::Error::InvalidIndex) => {
1595 return Err(std::io::Error::from_raw_os_error(EINVAL));
1596 }
1597 Err(refcount::Error::NeedCluster(addr)) => {
1598 new_cluster = Some((
1600 addr,
1601 VecCache::from_vec(self.raw_file.read_refcount_block(addr)?),
1602 ));
1603 }
1604 Err(refcount::Error::NeedNewCluster) => {
1605 let addr = self.get_new_cluster(None)?;
1607 added_clusters.push(addr);
1608 new_cluster = Some((
1609 addr,
1610 VecCache::new(self.refcounts.refcounts_per_block() as usize),
1611 ));
1612 }
1613 Err(refcount::Error::ReadingRefCounts(e)) => {
1614 return Err(e);
1615 }
1616 }
1617 }
1618
1619 for addr in added_clusters {
1620 self.set_cluster_refcount(addr, 1)?;
1621 }
1622 Ok(unref_clusters)
1623 }
1624
1625 fn sync_caches(&mut self) -> std::io::Result<()> {
1626 for (l1_index, l2_table) in self.l2_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
1628 let addr = self.l1_table[*l1_index];
1630 if addr != 0 {
1631 Self::write_l2_cluster(&mut self.raw_file, addr, l2_table.get_values())?;
1632 } else {
1633 return Err(std::io::Error::from_raw_os_error(EINVAL));
1634 }
1635 l2_table.mark_clean();
1636 }
1637 self.refcounts.flush_blocks(&mut self.raw_file)?;
1639 self.raw_file.file_mut().sync_data()?;
1641
1642 let mut sync_required = false;
1645 if self.l1_table.dirty() {
1646 self.raw_file.write_pointer_table(
1647 self.header.l1_table_offset,
1648 self.l1_table.get_values(),
1649 0,
1650 )?;
1651 self.l1_table.mark_clean();
1652 sync_required = true;
1653 }
1654 sync_required |= self.refcounts.flush_table(&mut self.raw_file)?;
1655 if sync_required {
1656 self.raw_file.file_mut().sync_data()?;
1657 }
1658 Ok(())
1659 }
1660
1661 fn read_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1666 where
1667 F: FnMut(Option<&mut dyn DiskFile>, usize, u64, usize) -> std::io::Result<()>,
1668 {
1669 let read_count: usize = self.limit_range_file(address, count);
1670
1671 let mut nread: usize = 0;
1672 while nread < read_count {
1673 let curr_addr = address + nread as u64;
1674 let location = self.file_offset_read(curr_addr)?;
1675 let count = self.limit_range_cluster(curr_addr, read_count - nread);
1676
1677 match (location, self.backing_file.as_mut()) {
1678 (ClusterLocation::Data(offset), _) => {
1679 cb(Some(self.raw_file.file_mut()), nread, offset, count)?;
1680 }
1681 (ClusterLocation::Unallocated, Some(backing)) => {
1682 cb(Some(backing.as_mut()), nread, curr_addr, count)?;
1683 }
1684 _ => cb(None, nread, 0, count)?,
1685 }
1686
1687 nread += count;
1688 }
1689 Ok(read_count)
1690 }
1691
1692 fn write_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1696 where
1697 F: FnMut(&mut File, usize, u64, usize) -> std::io::Result<()>,
1698 {
1699 let write_count: usize = self.limit_range_file(address, count);
1700
1701 let mut nwritten: usize = 0;
1702 while nwritten < write_count {
1703 let curr_addr = address + nwritten as u64;
1704 let offset = self.file_offset_write(curr_addr)?;
1705 let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1706
1707 cb(self.raw_file.file_mut(), nwritten, offset, count)?;
1708
1709 nwritten += count;
1710 }
1711 Ok(write_count)
1712 }
1713}
1714
1715impl Drop for QcowFile {
1716 fn drop(&mut self) {
1717 let _ = self.inner.get_mut().sync_caches();
1718 }
1719}
1720
1721impl AsRawDescriptors for QcowFile {
1722 fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
1723 let inner = self.inner.lock();
1726 let mut descriptors = vec![inner.raw_file.file().as_raw_descriptor()];
1727 if let Some(backing) = &inner.backing_file {
1728 descriptors.append(&mut backing.as_raw_descriptors());
1729 }
1730 descriptors
1731 }
1732}
1733
1734impl Read for QcowFile {
1735 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1736 let inner = self.inner.get_mut();
1737 let len = buf.len();
1738 let slice = VolatileSlice::new(buf);
1739 let read_count = inner.read_cb(
1740 inner.current_offset,
1741 len,
1742 |file, already_read, offset, count| {
1743 let sub_slice = slice.get_slice(already_read, count).unwrap();
1744 match file {
1745 Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1746 None => {
1747 sub_slice.write_bytes(0);
1748 Ok(())
1749 }
1750 }
1751 },
1752 )?;
1753 inner.current_offset += read_count as u64;
1754 Ok(read_count)
1755 }
1756}
1757
1758impl Seek for QcowFile {
1759 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
1760 let inner = self.inner.get_mut();
1761 let new_offset: Option<u64> = match pos {
1762 SeekFrom::Start(off) => Some(off),
1763 SeekFrom::End(off) => {
1764 if off < 0 {
1765 0i64.checked_sub(off)
1766 .and_then(|increment| inner.virtual_size().checked_sub(increment as u64))
1767 } else {
1768 inner.virtual_size().checked_add(off as u64)
1769 }
1770 }
1771 SeekFrom::Current(off) => {
1772 if off < 0 {
1773 0i64.checked_sub(off)
1774 .and_then(|increment| inner.current_offset.checked_sub(increment as u64))
1775 } else {
1776 inner.current_offset.checked_add(off as u64)
1777 }
1778 }
1779 };
1780
1781 if let Some(o) = new_offset {
1782 if o <= inner.virtual_size() {
1783 inner.current_offset = o;
1784 return Ok(o);
1785 }
1786 }
1787 Err(std::io::Error::from_raw_os_error(EINVAL))
1788 }
1789}
1790
1791impl Write for QcowFile {
1792 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1793 let inner = self.inner.get_mut();
1794 let write_count = inner.write_cb(
1795 inner.current_offset,
1796 buf.len(),
1797 |file, offset, raw_offset, count| {
1798 file.seek(SeekFrom::Start(raw_offset))?;
1799 file.write_all(&buf[offset..(offset + count)])
1800 },
1801 )?;
1802 inner.current_offset += write_count as u64;
1803 Ok(write_count)
1804 }
1805
1806 fn flush(&mut self) -> std::io::Result<()> {
1807 self.fsync()
1808 }
1809}
1810
1811impl FileReadWriteAtVolatile for QcowFile {
1812 fn read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1813 let mut inner = self.inner.lock();
1814 inner.read_cb(offset, slice.size(), |file, read, offset, count| {
1815 let sub_slice = slice.get_slice(read, count).unwrap();
1816 match file {
1817 Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1818 None => {
1819 sub_slice.write_bytes(0);
1820 Ok(())
1821 }
1822 }
1823 })
1824 }
1825
1826 fn write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1827 let mut inner = self.inner.lock();
1828 inner.write_cb(offset, slice.size(), |file, offset, raw_offset, count| {
1829 let sub_slice = slice.get_slice(offset, count).unwrap();
1830 file.write_all_at_volatile(sub_slice, raw_offset)
1831 })
1832 }
1833}
1834
1835impl FileSync for QcowFile {
1836 fn fsync(&self) -> std::io::Result<()> {
1837 let mut inner = self.inner.lock();
1838 inner.sync_caches()?;
1839 let unref_clusters = std::mem::take(&mut inner.unref_clusters);
1840 inner.avail_clusters.extend(unref_clusters);
1841 Ok(())
1842 }
1843
1844 fn fdatasync(&self) -> io::Result<()> {
1845 self.fsync()
1847 }
1848}
1849
1850impl FileSetLen for QcowFile {
1851 fn set_len(&self, _len: u64) -> std::io::Result<()> {
1852 Err(std::io::Error::other(
1853 "set_len() not supported for QcowFile",
1854 ))
1855 }
1856}
1857
1858impl DiskGetLen for QcowFile {
1859 fn get_len(&self) -> io::Result<u64> {
1860 Ok(self.virtual_size)
1861 }
1862}
1863
1864impl FileAllocate for QcowFile {
1865 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
1866 let mut inner = self.inner.lock();
1867 inner.write_cb(
1870 offset,
1871 len as usize,
1872 |_file, _offset, _raw_offset, _count| Ok(()),
1873 )?;
1874 Ok(())
1875 }
1876}
1877
1878impl PunchHole for QcowFile {
1879 fn punch_hole(&self, offset: u64, length: u64) -> std::io::Result<()> {
1880 let mut inner = self.inner.lock();
1881 let mut remaining = length;
1882 let mut offset = offset;
1883 while remaining > 0 {
1884 let chunk_length = min(remaining, usize::MAX as u64) as usize;
1885 inner.zero_bytes(offset, chunk_length)?;
1886 remaining -= chunk_length as u64;
1887 offset += chunk_length as u64;
1888 }
1889 Ok(())
1890 }
1891}
1892
1893impl WriteZeroesAt for QcowFile {
1894 fn write_zeroes_at(&self, offset: u64, length: usize) -> io::Result<usize> {
1895 self.punch_hole(offset, length as u64)?;
1896 Ok(length)
1897 }
1898}
1899
1900impl ToAsyncDisk for QcowFile {
1901 fn to_async_disk(self: Box<Self>, ex: &Executor) -> crate::Result<Box<dyn AsyncDisk>> {
1902 Ok(Box::new(AsyncDiskFileWrapper::new(*self, ex)))
1903 }
1904}
1905
1906fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
1908 if offset & ((0x01 << cluster_bits) - 1) != 0 {
1909 return Err(Error::InvalidOffset(offset));
1910 }
1911 Ok(())
1912}
1913
1914#[cfg(test)]
1915mod tests {
1916 use std::fs::OpenOptions;
1917 use std::io::Read;
1918 use std::io::Seek;
1919 use std::io::SeekFrom;
1920 use std::io::Write;
1921
1922 use tempfile::tempfile;
1923 use tempfile::TempDir;
1924
1925 use super::*;
1926
1927 fn valid_header() -> Vec<u8> {
1928 vec![
1929 0x51u8, 0x46, 0x49, 0xfb, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, ]
1948 }
1949
1950 fn test_huge_header() -> Vec<u8> {
1952 vec![
1953 0x51, 0x46, 0x49, 0xfb, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, ]
1972 }
1973
1974 fn test_params() -> DiskFileParams {
1975 DiskFileParams {
1976 path: PathBuf::from("/foo"),
1977 ..Default::default()
1978 }
1979 }
1980
1981 fn basic_file(header: &[u8]) -> File {
1982 let mut disk_file = tempfile().expect("failed to create temp file");
1983 disk_file.write_all(header).unwrap();
1984 disk_file.set_len(0x8000_0000).unwrap();
1985 disk_file.seek(SeekFrom::Start(0)).unwrap();
1986 disk_file
1987 }
1988
1989 fn with_basic_file<F>(header: &[u8], mut testfn: F)
1990 where
1991 F: FnMut(File),
1992 {
1993 testfn(basic_file(header)); }
1995
1996 fn with_default_file<F>(file_size: u64, mut testfn: F)
1997 where
1998 F: FnMut(QcowFile),
1999 {
2000 let file = tempfile().expect("failed to create temp file");
2001 let qcow_file = QcowFile::new(file, test_params(), file_size).unwrap();
2002
2003 testfn(qcow_file); }
2005
2006 fn write_all_at(qcow: &mut QcowFile, data: &[u8], offset: u64) -> std::io::Result<()> {
2008 let mut mem = data.to_owned();
2009 let vslice = VolatileSlice::new(&mut mem);
2010 qcow.write_all_at_volatile(vslice, offset)
2011 }
2012
2013 fn read_exact_at(qcow: &mut QcowFile, data: &mut [u8], offset: u64) -> std::io::Result<()> {
2015 let mut mem = data.to_owned();
2016 let vslice = VolatileSlice::new(&mut mem);
2017 qcow.read_exact_at_volatile(vslice, offset)?;
2018 vslice.copy_to(data);
2019 Ok(())
2020 }
2021
2022 #[test]
2023 fn default_header() {
2024 let header = QcowHeader::create_for_size_and_path(0x10_0000, None);
2025 let mut disk_file = tempfile().expect("failed to create temp file");
2026 header
2027 .expect("Failed to create header.")
2028 .write_to(&mut disk_file)
2029 .expect("Failed to write header to shm.");
2030 disk_file.seek(SeekFrom::Start(0)).unwrap();
2031 QcowFile::from(disk_file, test_params())
2032 .expect("Failed to create Qcow from default Header");
2033 }
2034
2035 #[test]
2036 fn header_read() {
2037 with_basic_file(&valid_header(), |mut disk_file: File| {
2038 QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
2039 });
2040 }
2041
2042 #[test]
2043 fn invalid_magic() {
2044 let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
2045 with_basic_file(&invalid_header, |mut disk_file: File| {
2046 QcowHeader::new(&mut disk_file).expect_err("Invalid header worked.");
2047 });
2048 }
2049
2050 #[test]
2051 fn invalid_refcount_order() {
2052 let mut header = valid_header();
2053 header[99] = 2;
2054 with_basic_file(&header, |disk_file: File| {
2055 QcowFile::from(disk_file, test_params()).expect_err("Invalid refcount order worked.");
2056 });
2057 }
2058
2059 #[test]
2060 fn invalid_incompatible_features() {
2061 let mut header = valid_header();
2062 header[79] = 1;
2063 with_basic_file(&header, |disk_file: File| {
2064 assert!(matches!(
2065 QcowFile::from(disk_file, test_params()),
2066 Err(Error::UnsupportedIncompatibleFeatures(1))
2067 ));
2068 });
2069 }
2070
2071 #[test]
2072 fn invalid_nb_snapshots() {
2073 let mut header = valid_header();
2074 header[63] = 1;
2075 with_basic_file(&header, |disk_file: File| {
2076 assert!(matches!(
2077 QcowFile::from(disk_file, test_params()),
2078 Err(Error::SnapshotsNotSupported)
2079 ));
2080 });
2081 }
2082
2083 #[test]
2084 fn invalid_cluster_bits() {
2085 let mut header = valid_header();
2086 header[23] = 3;
2087 with_basic_file(&header, |disk_file: File| {
2088 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2089 });
2090 }
2091
2092 #[test]
2093 fn test_header_huge_file() {
2094 let header = test_huge_header();
2095 with_basic_file(&header, |disk_file: File| {
2096 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2097 });
2098 }
2099
2100 #[test]
2101 fn test_header_excessive_file_size_rejected() {
2102 let mut header = valid_header();
2103 header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
2104 with_basic_file(&header, |disk_file: File| {
2105 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2106 });
2107 }
2108
2109 #[test]
2110 fn test_huge_l1_table() {
2111 let mut header = valid_header();
2112 header[36] = 0x12;
2113 with_basic_file(&header, |disk_file: File| {
2114 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2115 });
2116 }
2117
2118 #[test]
2119 fn test_header_1_tb_file_min_cluster() {
2120 let mut header = test_huge_header();
2121 header[24] = 0;
2122 header[26] = 1;
2123 header[31] = 0;
2124 with_basic_file(&header, |disk_file: File| {
2126 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2127 });
2128 }
2129
2130 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2131 #[test]
2132 fn test_header_1_tb_file() {
2133 let mut header = test_huge_header();
2134 header[24] = 0;
2136 header[26] = 1;
2137 header[31] = 0;
2138 header[23] = 16;
2140 with_basic_file(&header, |disk_file: File| {
2141 let mut qcow =
2142 QcowFile::from(disk_file, test_params()).expect("Failed to create file.");
2143 let value = 0x0000_0040_3f00_ffffu64;
2144 write_all_at(&mut qcow, &value.to_le_bytes(), 0x100_0000_0000 - 8)
2145 .expect("failed to write data");
2146 });
2147 }
2148
2149 #[test]
2150 fn test_header_huge_num_refcounts() {
2151 let mut header = valid_header();
2152 header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
2153 with_basic_file(&header, |disk_file: File| {
2154 QcowFile::from(disk_file, test_params())
2155 .expect_err("Created disk with excessive refcount clusters");
2156 });
2157 }
2158
2159 #[test]
2160 fn test_header_huge_refcount_offset() {
2161 let mut header = valid_header();
2162 header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
2163 with_basic_file(&header, |disk_file: File| {
2164 QcowFile::from(disk_file, test_params())
2165 .expect_err("Created disk with excessive refcount offset");
2166 });
2167 }
2168
2169 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2170 #[test]
2171 fn write_read_start() {
2172 with_basic_file(&valid_header(), |disk_file: File| {
2173 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2174 write_all_at(&mut q, b"test first bytes", 0).expect("Failed to write test string.");
2175 let mut buf = [0u8; 4];
2176 read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
2177 assert_eq!(&buf, b"test");
2178 });
2179 }
2180
2181 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2182 #[test]
2183 fn write_read_start_backing() {
2184 let disk_file = basic_file(&valid_header());
2185 let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2186 write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
2187 let mut buf = [0u8; 4];
2188 let wrapping_disk_file = basic_file(&valid_header());
2189 let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2190 wrapping.set_backing_file(Some(Box::new(backing)));
2191 read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
2192 assert_eq!(&buf, b"test");
2193 }
2194
2195 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2196 #[test]
2197 fn write_read_start_backing_overlap() {
2198 let disk_file = basic_file(&valid_header());
2199 let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2200 write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
2201 let wrapping_disk_file = basic_file(&valid_header());
2202 let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2203 wrapping.set_backing_file(Some(Box::new(backing)));
2204 write_all_at(&mut wrapping, b"TEST", 0).expect("Failed to write second test string.");
2205 let mut buf = [0u8; 10];
2206 read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
2207 assert_eq!(&buf, b"TEST first");
2208 }
2209
2210 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2211 #[test]
2212 fn offset_write_read() {
2213 with_basic_file(&valid_header(), |disk_file: File| {
2214 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2215 let b = [0x55u8; 0x1000];
2216 write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
2217 let mut buf = [0u8; 4];
2218 read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
2219 assert_eq!(buf[0], 0x55);
2220 });
2221 }
2222
2223 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2224 #[test]
2225 fn write_zeroes_read() {
2226 with_basic_file(&valid_header(), |disk_file: File| {
2227 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2228 let b = [0x55u8; 0x1000];
2230 write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
2231 q.write_zeroes_all_at(0xfff2000, 0x200)
2233 .expect("Failed to write zeroes.");
2234 let mut buf = [0u8; 0x1000];
2236 read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
2237 assert_eq!(buf[0], 0);
2238 assert_eq!(buf[0x1FF], 0);
2239 assert_eq!(buf[0x200], 0x55);
2240 assert_eq!(buf[0xFFF], 0x55);
2241 });
2242 }
2243
2244 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2245 #[test]
2246 fn write_zeroes_full_cluster() {
2247 const CHUNK_SIZE: usize = 4096 * 2 + 512;
2250 with_basic_file(&valid_header(), |disk_file: File| {
2251 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2252 let b = [0x55u8; CHUNK_SIZE];
2254 write_all_at(&mut q, &b, 0).expect("Failed to write test string.");
2255 q.write_zeroes_all_at(0, CHUNK_SIZE)
2257 .expect("Failed to write zeroes.");
2258 let mut buf = [0u8; CHUNK_SIZE];
2260 read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
2261 assert_eq!(buf[0], 0);
2262 assert_eq!(buf[CHUNK_SIZE - 1], 0);
2263 });
2264 }
2265
2266 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2267 #[test]
2268 fn write_zeroes_backing() {
2269 let disk_file = basic_file(&valid_header());
2270 let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2271 let b = [0x55u8; 0x1000];
2273 write_all_at(&mut backing, &b, 0xfff2000).expect("Failed to write test string.");
2274 let wrapping_disk_file = basic_file(&valid_header());
2275 let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2276 wrapping.set_backing_file(Some(Box::new(backing)));
2277 wrapping
2280 .write_zeroes_all_at(0xfff2000, 0x200)
2281 .expect("Failed to write zeroes.");
2282 let mut buf = [0u8; 0x1000];
2284 read_exact_at(&mut wrapping, &mut buf, 0xfff2000).expect("Failed to read.");
2285 assert_eq!(buf[0], 0);
2286 assert_eq!(buf[0x1FF], 0);
2287 assert_eq!(buf[0x200], 0x55);
2288 assert_eq!(buf[0xFFF], 0x55);
2289 }
2290
2291 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2292 #[test]
2293 fn write_zeroes_backing_full_cluster() {
2294 const CLUSTER_SIZE: usize = 0x10000;
2295 let mut backing = tempfile().unwrap();
2296 backing.write_all(&[0x55; 2 * CLUSTER_SIZE]).unwrap();
2297 let disk_file = basic_file(&valid_header());
2298 let mut q = QcowFile::from(disk_file.try_clone().unwrap(), test_params()).unwrap();
2299 q.set_backing_file(Some(Box::new(backing.try_clone().unwrap())));
2300
2301 q.write_zeroes_all_at(0, CLUSTER_SIZE).unwrap();
2303 drop(q);
2304 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2305 let inner = q.inner.get_mut();
2306 let l2_addr = inner.l1_table[0];
2307 let l2_table = inner.raw_file.read_pointer_cluster(l2_addr, None).unwrap();
2308 assert_eq!(l2_table[0], ZERO_CLUSTER_FLAG);
2309
2310 q.set_backing_file(Some(Box::new(backing)));
2311 let mut buf = vec![0u8; 2 * CLUSTER_SIZE];
2312 read_exact_at(&mut q, &mut buf, 0).unwrap();
2313 assert!(buf[..CLUSTER_SIZE].iter().all(|&b| b == 0));
2314 assert!(buf[CLUSTER_SIZE..].iter().all(|&b| b == 0x55));
2315
2316 write_all_at(&mut q, &[0xaa], 0).unwrap();
2318 read_exact_at(&mut q, &mut buf, 0).unwrap();
2319 assert_eq!(buf[0], 0xaa);
2320 assert!(buf[1..CLUSTER_SIZE].iter().all(|&b| b == 0));
2321
2322 let inner = q.inner.get_mut();
2324 let ClusterLocation::Data(data_addr) = inner.file_offset_read(0).unwrap() else {
2325 panic!("cluster should be allocated");
2326 };
2327 q.write_zeroes_all_at(0, CLUSTER_SIZE).unwrap();
2328 read_exact_at(&mut q, &mut buf, 0).unwrap();
2329 assert!(buf[..CLUSTER_SIZE].iter().all(|&b| b == 0));
2330 let inner = q.inner.get_mut();
2331 let refcount = inner
2332 .refcounts
2333 .get_cluster_refcount(&mut inner.raw_file, data_addr)
2334 .unwrap();
2335 assert_eq!(refcount, 0);
2336 }
2337
2338 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2339 #[test]
2340 fn write_preallocated_zero_cluster() {
2341 const CLUSTER_SIZE: usize = 0x10000;
2342 with_basic_file(&valid_header(), |disk_file: File| {
2343 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2344 write_all_at(&mut q, &[0x55; CLUSTER_SIZE], 0).unwrap();
2345
2346 let inner = q.inner.get_mut();
2349 let ClusterLocation::Data(preallocated) = inner.file_offset_read(0).unwrap() else {
2350 panic!("cluster should be allocated");
2351 };
2352 inner.l2_cache.get_mut(&0).unwrap()[0] = L2CacheEntry(preallocated | ZERO_CLUSTER_FLAG);
2353
2354 write_all_at(&mut q, &[0xaa], 0).unwrap();
2357 let mut buf = vec![0u8; CLUSTER_SIZE];
2358 read_exact_at(&mut q, &mut buf, 0).unwrap();
2359 assert_eq!(buf[0], 0xaa);
2360 assert!(buf[1..].iter().all(|&b| b == 0));
2361 let inner = q.inner.get_mut();
2362 let refcount = inner
2363 .refcounts
2364 .get_cluster_refcount(&mut inner.raw_file, preallocated)
2365 .unwrap();
2366 assert_eq!(refcount, 0);
2367 });
2368 }
2369 #[test]
2370 fn test_header() {
2371 with_basic_file(&valid_header(), |disk_file: File| {
2372 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2373 assert_eq!(q.inner.get_mut().virtual_size(), 0x20_0000_0000);
2374 });
2375 }
2376
2377 #[test]
2378 fn read_small_buffer() {
2379 with_basic_file(&valid_header(), |disk_file: File| {
2380 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2381 let mut b = [5u8; 16];
2382 read_exact_at(&mut q, &mut b, 1000).expect("Failed to read.");
2383 assert_eq!(0, b[0]);
2384 assert_eq!(0, b[15]);
2385 });
2386 }
2387
2388 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2389 #[test]
2390 fn replay_ext4() {
2391 with_basic_file(&valid_header(), |disk_file: File| {
2392 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2393 const BUF_SIZE: usize = 0x1000;
2394 let mut b = [0u8; BUF_SIZE];
2395
2396 struct Transfer {
2397 pub write: bool,
2398 pub addr: u64,
2399 }
2400
2401 let xfers: Vec<Transfer> = vec![
2403 Transfer {
2404 write: false,
2405 addr: 0xfff0000,
2406 },
2407 Transfer {
2408 write: false,
2409 addr: 0xfffe000,
2410 },
2411 Transfer {
2412 write: false,
2413 addr: 0x0,
2414 },
2415 Transfer {
2416 write: false,
2417 addr: 0x1000,
2418 },
2419 Transfer {
2420 write: false,
2421 addr: 0xffff000,
2422 },
2423 Transfer {
2424 write: false,
2425 addr: 0xffdf000,
2426 },
2427 Transfer {
2428 write: false,
2429 addr: 0xfff8000,
2430 },
2431 Transfer {
2432 write: false,
2433 addr: 0xffe0000,
2434 },
2435 Transfer {
2436 write: false,
2437 addr: 0xffce000,
2438 },
2439 Transfer {
2440 write: false,
2441 addr: 0xffb6000,
2442 },
2443 Transfer {
2444 write: false,
2445 addr: 0xffab000,
2446 },
2447 Transfer {
2448 write: false,
2449 addr: 0xffa4000,
2450 },
2451 Transfer {
2452 write: false,
2453 addr: 0xff8e000,
2454 },
2455 Transfer {
2456 write: false,
2457 addr: 0xff86000,
2458 },
2459 Transfer {
2460 write: false,
2461 addr: 0xff84000,
2462 },
2463 Transfer {
2464 write: false,
2465 addr: 0xff89000,
2466 },
2467 Transfer {
2468 write: false,
2469 addr: 0xfe7e000,
2470 },
2471 Transfer {
2472 write: false,
2473 addr: 0x100000,
2474 },
2475 Transfer {
2476 write: false,
2477 addr: 0x3000,
2478 },
2479 Transfer {
2480 write: false,
2481 addr: 0x7000,
2482 },
2483 Transfer {
2484 write: false,
2485 addr: 0xf000,
2486 },
2487 Transfer {
2488 write: false,
2489 addr: 0x2000,
2490 },
2491 Transfer {
2492 write: false,
2493 addr: 0x4000,
2494 },
2495 Transfer {
2496 write: false,
2497 addr: 0x5000,
2498 },
2499 Transfer {
2500 write: false,
2501 addr: 0x6000,
2502 },
2503 Transfer {
2504 write: false,
2505 addr: 0x8000,
2506 },
2507 Transfer {
2508 write: false,
2509 addr: 0x9000,
2510 },
2511 Transfer {
2512 write: false,
2513 addr: 0xa000,
2514 },
2515 Transfer {
2516 write: false,
2517 addr: 0xb000,
2518 },
2519 Transfer {
2520 write: false,
2521 addr: 0xc000,
2522 },
2523 Transfer {
2524 write: false,
2525 addr: 0xd000,
2526 },
2527 Transfer {
2528 write: false,
2529 addr: 0xe000,
2530 },
2531 Transfer {
2532 write: false,
2533 addr: 0x10000,
2534 },
2535 Transfer {
2536 write: false,
2537 addr: 0x11000,
2538 },
2539 Transfer {
2540 write: false,
2541 addr: 0x12000,
2542 },
2543 Transfer {
2544 write: false,
2545 addr: 0x13000,
2546 },
2547 Transfer {
2548 write: false,
2549 addr: 0x14000,
2550 },
2551 Transfer {
2552 write: false,
2553 addr: 0x15000,
2554 },
2555 Transfer {
2556 write: false,
2557 addr: 0x16000,
2558 },
2559 Transfer {
2560 write: false,
2561 addr: 0x17000,
2562 },
2563 Transfer {
2564 write: false,
2565 addr: 0x18000,
2566 },
2567 Transfer {
2568 write: false,
2569 addr: 0x19000,
2570 },
2571 Transfer {
2572 write: false,
2573 addr: 0x1a000,
2574 },
2575 Transfer {
2576 write: false,
2577 addr: 0x1b000,
2578 },
2579 Transfer {
2580 write: false,
2581 addr: 0x1c000,
2582 },
2583 Transfer {
2584 write: false,
2585 addr: 0x1d000,
2586 },
2587 Transfer {
2588 write: false,
2589 addr: 0x1e000,
2590 },
2591 Transfer {
2592 write: false,
2593 addr: 0x1f000,
2594 },
2595 Transfer {
2596 write: false,
2597 addr: 0x21000,
2598 },
2599 Transfer {
2600 write: false,
2601 addr: 0x22000,
2602 },
2603 Transfer {
2604 write: false,
2605 addr: 0x24000,
2606 },
2607 Transfer {
2608 write: false,
2609 addr: 0x40000,
2610 },
2611 Transfer {
2612 write: false,
2613 addr: 0x0,
2614 },
2615 Transfer {
2616 write: false,
2617 addr: 0x3000,
2618 },
2619 Transfer {
2620 write: false,
2621 addr: 0x7000,
2622 },
2623 Transfer {
2624 write: false,
2625 addr: 0x0,
2626 },
2627 Transfer {
2628 write: false,
2629 addr: 0x1000,
2630 },
2631 Transfer {
2632 write: false,
2633 addr: 0x2000,
2634 },
2635 Transfer {
2636 write: false,
2637 addr: 0x3000,
2638 },
2639 Transfer {
2640 write: false,
2641 addr: 0x0,
2642 },
2643 Transfer {
2644 write: false,
2645 addr: 0x449000,
2646 },
2647 Transfer {
2648 write: false,
2649 addr: 0x48000,
2650 },
2651 Transfer {
2652 write: false,
2653 addr: 0x48000,
2654 },
2655 Transfer {
2656 write: false,
2657 addr: 0x448000,
2658 },
2659 Transfer {
2660 write: false,
2661 addr: 0x44a000,
2662 },
2663 Transfer {
2664 write: false,
2665 addr: 0x48000,
2666 },
2667 Transfer {
2668 write: false,
2669 addr: 0x48000,
2670 },
2671 Transfer {
2672 write: true,
2673 addr: 0x0,
2674 },
2675 Transfer {
2676 write: true,
2677 addr: 0x448000,
2678 },
2679 Transfer {
2680 write: true,
2681 addr: 0x449000,
2682 },
2683 Transfer {
2684 write: true,
2685 addr: 0x44a000,
2686 },
2687 Transfer {
2688 write: true,
2689 addr: 0xfff0000,
2690 },
2691 Transfer {
2692 write: true,
2693 addr: 0xfff1000,
2694 },
2695 Transfer {
2696 write: true,
2697 addr: 0xfff2000,
2698 },
2699 Transfer {
2700 write: true,
2701 addr: 0xfff3000,
2702 },
2703 Transfer {
2704 write: true,
2705 addr: 0xfff4000,
2706 },
2707 Transfer {
2708 write: true,
2709 addr: 0xfff5000,
2710 },
2711 Transfer {
2712 write: true,
2713 addr: 0xfff6000,
2714 },
2715 Transfer {
2716 write: true,
2717 addr: 0xfff7000,
2718 },
2719 Transfer {
2720 write: true,
2721 addr: 0xfff8000,
2722 },
2723 Transfer {
2724 write: true,
2725 addr: 0xfff9000,
2726 },
2727 Transfer {
2728 write: true,
2729 addr: 0xfffa000,
2730 },
2731 Transfer {
2732 write: true,
2733 addr: 0xfffb000,
2734 },
2735 Transfer {
2736 write: true,
2737 addr: 0xfffc000,
2738 },
2739 Transfer {
2740 write: true,
2741 addr: 0xfffd000,
2742 },
2743 Transfer {
2744 write: true,
2745 addr: 0xfffe000,
2746 },
2747 Transfer {
2748 write: true,
2749 addr: 0xffff000,
2750 },
2751 ];
2752
2753 for xfer in &xfers {
2754 if xfer.write {
2755 write_all_at(&mut q, &b, xfer.addr).expect("Failed to write.");
2756 } else {
2757 read_exact_at(&mut q, &mut b, xfer.addr).expect("Failed to read.");
2758 }
2759 }
2760 });
2761 }
2762
2763 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2764 #[test]
2765 fn combo_write_read() {
2766 with_default_file(1024 * 1024 * 1024 * 256, |mut qcow_file| {
2767 const NUM_BLOCKS: usize = 55;
2768 const BLOCK_SIZE: usize = 0x1_0000;
2769 const OFFSET: u64 = 0x1_0000_0020;
2770 let data = [0x55u8; BLOCK_SIZE];
2771 let mut readback = [0u8; BLOCK_SIZE];
2772 for i in 0..NUM_BLOCKS {
2773 let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2774 write_all_at(&mut qcow_file, &data, seek_offset)
2775 .expect("Failed to write test data.");
2776 read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2778 for (orig, read) in data.iter().zip(readback.iter()) {
2779 assert_eq!(orig, read);
2780 }
2781 }
2782 read_exact_at(&mut qcow_file, &mut readback, 0).expect("Failed to read.");
2784 for read in readback.iter() {
2785 assert_eq!(*read, 0);
2786 }
2787 for i in 0..NUM_BLOCKS {
2789 let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2790 read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2791 for (orig, read) in data.iter().zip(readback.iter()) {
2792 assert_eq!(orig, read);
2793 }
2794 }
2795
2796 assert_eq!(
2797 qcow_file.inner.get_mut().first_zero_refcount().unwrap(),
2798 None
2799 );
2800 });
2801 }
2802
2803 #[test]
2804 fn rebuild_refcounts() {
2805 with_basic_file(&valid_header(), |mut disk_file: File| {
2806 let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
2807 let cluster_size = 65536;
2808 let mut raw_file =
2809 QcowRawFile::from(disk_file, cluster_size).expect("Failed to create QcowRawFile.");
2810 QcowFileInner::rebuild_refcounts(&mut raw_file, header)
2811 .expect("Failed to rebuild recounts.");
2812 });
2813 }
2814
2815 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2816 #[test]
2817 fn nested_qcow() {
2818 let tmp_dir = TempDir::new().unwrap();
2819
2820 let backing_file_path = tmp_dir.path().join("backing");
2823 let _backing_file = OpenOptions::new()
2824 .read(true)
2825 .write(true)
2826 .create_new(true)
2827 .open(&backing_file_path)
2828 .unwrap();
2829
2830 let level1_qcow_file_path = tmp_dir.path().join("qcow.l1");
2831 let level1_qcow_file = OpenOptions::new()
2832 .read(true)
2833 .write(true)
2834 .create_new(true)
2835 .open(&level1_qcow_file_path)
2836 .unwrap();
2837 let _level1_qcow_file = QcowFile::new_from_backing(
2838 level1_qcow_file,
2839 test_params(),
2840 backing_file_path.to_str().unwrap(),
2841 )
2842 .unwrap();
2843
2844 let level2_qcow_file = tempfile().unwrap();
2845 let _level2_qcow_file = QcowFile::new_from_backing(
2846 level2_qcow_file,
2847 test_params(),
2848 level1_qcow_file_path.to_str().unwrap(),
2849 )
2850 .expect("failed to create level2 qcow file");
2851 }
2852
2853 #[test]
2854 fn io_seek() {
2855 with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2856 assert_eq!(qcow_file.stream_position().unwrap(), 0);
2858
2859 assert_eq!(
2861 qcow_file.seek(SeekFrom::Start(1024 * 1024)).unwrap(),
2862 1024 * 1024
2863 );
2864
2865 qcow_file
2868 .seek(SeekFrom::Current(-(1024 * 1024 + 1)))
2869 .expect_err("negative offset seek should fail");
2870 assert_eq!(qcow_file.stream_position().unwrap(), 1024 * 1024);
2871
2872 assert_eq!(
2874 qcow_file.seek(SeekFrom::End(-1)).unwrap(),
2875 1024 * 1024 * 10 - 1
2876 );
2877
2878 assert_eq!(qcow_file.seek(SeekFrom::End(0)).unwrap(), 1024 * 1024 * 10);
2880
2881 qcow_file
2883 .seek(SeekFrom::End(1))
2884 .expect_err("seek past EOF should fail");
2885 });
2886 }
2887
2888 #[test]
2889 fn io_write_read() {
2890 with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2891 const BLOCK_SIZE: usize = 0x1_0000;
2892 let data_55 = [0x55u8; BLOCK_SIZE];
2893 let data_aa = [0xaau8; BLOCK_SIZE];
2894 let mut readback = [0u8; BLOCK_SIZE];
2895
2896 qcow_file.write_all(&data_55).unwrap();
2897 assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64);
2898
2899 qcow_file.write_all(&data_aa).unwrap();
2900 assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64 * 2);
2901
2902 assert_eq!(
2904 qcow_file
2905 .seek(SeekFrom::Current(-(BLOCK_SIZE as i64)))
2906 .unwrap(),
2907 BLOCK_SIZE as u64
2908 );
2909 qcow_file.read_exact(&mut readback).unwrap();
2910 assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64 * 2);
2911 for (orig, read) in data_aa.iter().zip(readback.iter()) {
2912 assert_eq!(orig, read);
2913 }
2914
2915 qcow_file.rewind().unwrap();
2917 qcow_file.read_exact(&mut readback).unwrap();
2918 for (orig, read) in data_55.iter().zip(readback.iter()) {
2919 assert_eq!(orig, read);
2920 }
2921
2922 qcow_file
2924 .seek(SeekFrom::Start(BLOCK_SIZE as u64 / 2))
2925 .unwrap();
2926 qcow_file.read_exact(&mut readback).unwrap();
2927 for (orig, read) in data_55[BLOCK_SIZE / 2..]
2928 .iter()
2929 .chain(data_aa[..BLOCK_SIZE / 2].iter())
2930 .zip(readback.iter())
2931 {
2932 assert_eq!(orig, read);
2933 }
2934 });
2935 }
2936
2937 #[test]
2938 fn header_with_backing_format() {
2939 let header = QcowHeader::create_for_size_and_path(
2940 0x10_0000,
2941 Some(("/my/path/to/a/file", crate::ImageType::Raw)),
2942 )
2943 .expect("Failed to create header.");
2944 let mut disk_file = tempfile().expect("failed to create temp file");
2945 header
2946 .write_to(&mut disk_file)
2947 .expect("Failed to write header.");
2948 disk_file.seek(SeekFrom::Start(0)).unwrap();
2949 let read_header = QcowHeader::new(&mut disk_file).expect("Failed to create header.");
2950 assert_eq!(
2951 header.backing_file_path,
2952 Some(String::from("/my/path/to/a/file"))
2953 );
2954 assert_eq!(read_header.backing_file_path, header.backing_file_path);
2955 assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
2956 }
2957
2958 #[test]
2959 fn test_backing_format_extension_magic() {
2960 const EXPECTED_MAGIC: [u8; 4] = [0xe2, 0x79, 0x2a, 0xca];
2961
2962 let header = QcowHeader::create_for_size_and_path(
2963 0x10_0000,
2964 Some(("/my/path/to/a/file", crate::ImageType::Raw)),
2965 )
2966 .expect("Failed to create header.");
2967 let mut disk_file = tempfile().expect("failed to create temp file");
2968 header
2969 .write_to(&mut disk_file)
2970 .expect("Failed to write header.");
2971
2972 let mut magic_bytes = [0u8; 4];
2975 disk_file
2976 .seek(SeekFrom::Start(V3_BARE_HEADER_SIZE as u64))
2977 .unwrap();
2978 disk_file.read_exact(&mut magic_bytes).unwrap();
2979 assert_eq!(magic_bytes, EXPECTED_MAGIC);
2980
2981 disk_file.seek(SeekFrom::Start(0)).unwrap();
2983 let read_header = QcowHeader::new(&mut disk_file).expect("Failed to parse header.");
2984 assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
2985
2986 disk_file
2989 .seek(SeekFrom::Start(V3_BARE_HEADER_SIZE as u64))
2990 .unwrap();
2991 disk_file.write_all(&[0xe2, 0x79, 0x26, 0x8f]).unwrap();
2992 disk_file.seek(SeekFrom::Start(0)).unwrap();
2993 let header_with_unknown_ext =
2994 QcowHeader::new(&mut disk_file).expect("Failed to parse header.");
2995 assert_eq!(header_with_unknown_ext.backing_file_format, None);
2996
2997 disk_file
2999 .seek(SeekFrom::Start(V3_BARE_HEADER_SIZE as u64))
3000 .unwrap();
3001 disk_file.write_all(&EXPECTED_MAGIC).unwrap();
3002 disk_file.seek(SeekFrom::Start(0)).unwrap();
3003 let read_header = QcowHeader::new(&mut disk_file).expect("Failed to parse header.");
3004 assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
3005 }
3006
3007 #[test]
3008 fn test_backing_format_respected() {
3009 let temp_dir = TempDir::new().expect("failed to create temp dir");
3010 let backing_path = temp_dir.path().join("backing.raw");
3011 let parent_path = temp_dir.path().join("parent.qcow2");
3012
3013 let mut backing_file = File::create(&backing_path).unwrap();
3015 backing_file.write_all(&QCOW_MAGIC.to_be_bytes()).unwrap();
3016 backing_file.set_len(1024 * 1024).unwrap();
3017 drop(backing_file);
3018
3019 let header_raw = QcowHeader::create_for_size_and_path(
3021 1024 * 1024 * 10,
3022 Some((backing_path.to_str().unwrap(), crate::ImageType::Raw)),
3023 )
3024 .expect("Failed to create header.");
3025
3026 let mut parent_file = File::create(&parent_path).unwrap();
3027 header_raw.write_to(&mut parent_file).unwrap();
3028 drop(parent_file);
3029
3030 let parent_file = OpenOptions::new()
3032 .read(true)
3033 .write(true)
3034 .open(&parent_path)
3035 .unwrap();
3036 let mut params = test_params();
3037 params.path = parent_path.clone();
3038 let qcow = QcowFile::from(parent_file, params);
3039 assert!(
3040 qcow.is_ok(),
3041 "Failed to open with explicit raw backing: {:?}",
3042 qcow.err()
3043 );
3044
3045 let mut header_auto = QcowHeader::create_for_size_and_path(
3047 1024 * 1024 * 10,
3048 Some((backing_path.to_str().unwrap(), crate::ImageType::Raw)),
3049 )
3050 .expect("Failed to create header.");
3051 header_auto.backing_file_format = None;
3052 header_auto.backing_file_offset = V3_BARE_HEADER_SIZE as u64 + 8;
3053
3054 let mut parent_file_auto = File::create(&parent_path).unwrap();
3055 header_auto.write_to(&mut parent_file_auto).unwrap();
3056 drop(parent_file_auto);
3057
3058 let parent_file_auto = OpenOptions::new()
3060 .read(true)
3061 .write(true)
3062 .open(&parent_path)
3063 .unwrap();
3064 let mut params = test_params();
3065 params.path = parent_path.clone();
3066 let qcow_auto = QcowFile::from(parent_file_auto, params);
3067 assert!(
3068 matches!(
3069 qcow_auto.as_ref().err(),
3070 Some(&Error::MissingBackingFileFormat)
3071 ),
3072 "Expected Error::MissingBackingFileFormat, got: {qcow_auto:?}"
3073 );
3074 }
3075
3076 #[test]
3077 fn test_unsupported_backing_format() {
3078 let header_err = QcowHeader::create_for_size_and_path(
3080 1024 * 1024 * 10,
3081 Some(("backing.img", crate::ImageType::AndroidSparse)),
3082 );
3083 assert!(
3084 matches!(
3085 header_err.as_ref().err(),
3086 Some(&Error::UnsupportedBackingFileFormat(_))
3087 ),
3088 "Expected Error::UnsupportedBackingFileFormat, got: {header_err:?}"
3089 );
3090
3091 let header = QcowHeader::create_for_size_and_path(
3093 1024 * 1024 * 10,
3094 Some(("backing.raw", crate::ImageType::Raw)),
3095 )
3096 .unwrap();
3097
3098 let mut file = tempfile().unwrap();
3099 header.write_to(&mut file).unwrap();
3100
3101 file.seek(SeekFrom::Start(108)).unwrap();
3103 file.write_all(&7u32.to_be_bytes()).unwrap(); file.write_all(b"invalid\0").unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
3108 let read_result = QcowHeader::new(&mut file);
3109 match &read_result {
3110 Err(Error::UnsupportedBackingFileFormat(fmt)) => {
3111 assert_eq!(fmt, "invalid");
3112 }
3113 other => {
3114 panic!("Expected Error::UnsupportedBackingFileFormat(\"invalid\"), got: {other:?}")
3115 }
3116 }
3117 }
3118
3119 #[test]
3120 fn test_create_excessive_size_rejected() {
3121 let header = QcowHeader::create_for_size_and_path(MAX_QCOW_FILE_SIZE + 1, None);
3122 assert!(matches!(header.err(), Some(Error::FileTooBig(_))));
3123 }
3124
3125 #[cfg(feature = "composite-disk")]
3126 #[test]
3127 fn test_composite_backing() {
3128 use protobuf::Message;
3129 use protos::cdisk_spec;
3130
3131 use crate::composite::CDISK_MAGIC;
3132
3133 let temp_dir = TempDir::new().expect("failed to create temp dir");
3134 let component_path = temp_dir.path().join("component.raw");
3135 let spec_path = temp_dir.path().join("backing.cdisk");
3136 let parent_path = temp_dir.path().join("parent.qcow2");
3137
3138 let component_file = File::create(&component_path).unwrap();
3140 component_file.set_len(1024 * 1024).unwrap(); drop(component_file);
3142
3143 let mut proto = cdisk_spec::CompositeDisk::new();
3145 proto.version = 2;
3146 proto.length = 1024 * 1024;
3147 let mut component = cdisk_spec::ComponentDisk::new();
3148 component.file_path = String::from("component.raw");
3150 component.offset = 0;
3151 component.file_offset = 0;
3152 component.read_write_capability = cdisk_spec::ReadWriteCapability::READ_WRITE.into();
3153 proto.component_disks.push(component);
3154
3155 let mut spec_file = File::create(&spec_path).unwrap();
3156 spec_file.write_all(CDISK_MAGIC.as_bytes()).unwrap();
3157 proto.write_to_writer(&mut spec_file).unwrap();
3158 drop(spec_file);
3159
3160 let header = QcowHeader::create_for_size_and_path(
3162 1024 * 1024 * 10,
3163 Some((spec_path.to_str().unwrap(), crate::ImageType::CompositeDisk)),
3164 )
3165 .expect("Failed to create header.");
3166
3167 let mut parent_file = File::create(&parent_path).unwrap();
3168 header.write_to(&mut parent_file).unwrap();
3169 drop(parent_file);
3170
3171 let parent_file = OpenOptions::new()
3173 .read(true)
3174 .write(true)
3175 .open(&parent_path)
3176 .unwrap();
3177 let mut params = test_params();
3178 params.path = parent_path.clone();
3179 let qcow = QcowFile::from(parent_file, params);
3180 assert!(
3181 qcow.is_ok(),
3182 "Failed to open with composite backing: {:?}",
3183 qcow.err()
3184 );
3185 }
3186
3187 #[test]
3188 fn test_backing_file_escapes_first_cluster() {
3189 let mut header = QcowHeader::create_for_size_and_path(
3191 1024 * 1024 * 10,
3192 Some(("backing.raw", crate::ImageType::Raw)),
3193 )
3194 .unwrap();
3195
3196 header.backing_file_offset = 70000;
3199
3200 let mut file = tempfile().unwrap();
3201 header.write_to(&mut file).unwrap();
3202
3203 let read_result = QcowHeader::new(&mut file);
3205 match &read_result {
3206 Err(Error::BackingFileEscapesFirstCluster) => {}
3207 other => panic!("Expected Error::BackingFileEscapesFirstCluster, got: {other:?}"),
3208 }
3209 }
3210}