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("l1 entry table too large: {0}")]
128 TooManyL1Entries(u64),
129 #[error("ref count table too large: {0}")]
130 TooManyRefcounts(u64),
131 #[error("unsupported backing file format: {0}")]
132 UnsupportedBackingFileFormat(String),
133 #[error("unsupported refcount order")]
134 UnsupportedRefcountOrder,
135 #[error("unsupported version: {0}")]
136 UnsupportedVersion(u32),
137 #[error("failed to write header: {0}")]
138 WritingHeader(io::Error),
139}
140
141pub type Result<T> = std::result::Result<T, Error>;
142
143const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; pub const QCOW_MAGIC: u32 = 0x5146_49fb;
148const DEFAULT_CLUSTER_BITS: u32 = 16;
150const MIN_CLUSTER_BITS: u32 = 9;
153const MAX_CLUSTER_BITS: u32 = 21;
154const MAX_RAM_POINTER_TABLE_SIZE: u64 = 35_000_000;
158const DEFAULT_REFCOUNT_ORDER: u32 = 4;
160
161const V3_BARE_HEADER_SIZE: u32 = 104;
162
163const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
165const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
166const COMPRESSED_FLAG: u64 = 1 << 62;
168const CLUSTER_USED_FLAG: u64 = 1 << 63;
169const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1 << 0;
170
171const QCOW2_EXT_MAGIC_BACKING_FORMAT: u32 = 0xe279_268f;
173
174const MAX_BACKING_FILE_SIZE: u32 = 1023;
176
177#[derive(Clone, Debug)]
179pub struct QcowHeader {
180 pub magic: u32,
181 pub version: u32,
182
183 pub backing_file_offset: u64,
184 pub backing_file_size: u32,
185
186 pub cluster_bits: u32,
187 pub size: u64,
188 pub crypt_method: u32,
189
190 pub l1_size: u32,
191 pub l1_table_offset: u64,
192
193 pub refcount_table_offset: u64,
194 pub refcount_table_clusters: u32,
195
196 pub nb_snapshots: u32,
197 pub snapshots_offset: u64,
198
199 pub incompatible_features: u64,
201 pub compatible_features: u64,
202 pub autoclear_features: u64,
203 pub refcount_order: u32,
204 pub header_size: u32,
205
206 pub backing_file_path: Option<String>,
208 pub backing_file_format: Option<crate::ImageType>,
209}
210
211fn read_u16_from_file(mut f: &File) -> Result<u16> {
213 let mut value = [0u8; 2];
214 (&mut f)
215 .read_exact(&mut value)
216 .map_err(Error::ReadingHeader)?;
217 Ok(u16::from_be_bytes(value))
218}
219
220fn read_u32_from_file(mut f: &File) -> Result<u32> {
222 let mut value = [0u8; 4];
223 (&mut f)
224 .read_exact(&mut value)
225 .map_err(Error::ReadingHeader)?;
226 Ok(u32::from_be_bytes(value))
227}
228
229fn read_u64_from_file(mut f: &File) -> Result<u64> {
231 let mut value = [0u8; 8];
232 (&mut f)
233 .read_exact(&mut value)
234 .map_err(Error::ReadingHeader)?;
235 Ok(u64::from_be_bytes(value))
236}
237
238fn image_type_to_format_str(image_type: crate::ImageType) -> Result<&'static str> {
239 match image_type {
240 crate::ImageType::Raw => Ok("raw"),
241 crate::ImageType::Qcow2 => Ok("qcow2"),
242 crate::ImageType::CompositeDisk => Ok("composite"),
243 other => Err(Error::UnsupportedBackingFileFormat(format!("{other:?}"))),
244 }
245}
246
247fn format_str_to_image_type(s: &str) -> Result<crate::ImageType> {
248 match s {
249 "raw" => Ok(crate::ImageType::Raw),
250 "qcow2" => Ok(crate::ImageType::Qcow2),
251 "composite" => Ok(crate::ImageType::CompositeDisk),
252 other => Err(Error::UnsupportedBackingFileFormat(other.to_string())),
253 }
254}
255
256impl QcowHeader {
257 pub fn new(f: &mut File) -> Result<QcowHeader> {
259 f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
260
261 let magic = read_u32_from_file(f)?;
262 if magic != QCOW_MAGIC {
263 return Err(Error::InvalidMagic);
264 }
265
266 let mut header = QcowHeader {
267 magic,
268 version: read_u32_from_file(f)?,
269 backing_file_offset: read_u64_from_file(f)?,
270 backing_file_size: read_u32_from_file(f)?,
271 cluster_bits: read_u32_from_file(f)?,
272 size: read_u64_from_file(f)?,
273 crypt_method: read_u32_from_file(f)?,
274 l1_size: read_u32_from_file(f)?,
275 l1_table_offset: read_u64_from_file(f)?,
276 refcount_table_offset: read_u64_from_file(f)?,
277 refcount_table_clusters: read_u32_from_file(f)?,
278 nb_snapshots: read_u32_from_file(f)?,
279 snapshots_offset: read_u64_from_file(f)?,
280 incompatible_features: read_u64_from_file(f)?,
281 compatible_features: read_u64_from_file(f)?,
282 autoclear_features: read_u64_from_file(f)?,
283 refcount_order: read_u32_from_file(f)?,
284 header_size: read_u32_from_file(f)?,
285 backing_file_path: None,
286 backing_file_format: None,
287 };
288
289 if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&header.cluster_bits) {
290 return Err(Error::InvalidClusterSize);
291 }
292
293 let cluster_size = 1u64 << header.cluster_bits;
294
295 if header.backing_file_offset != 0 {
296 let backing_file_end = header
297 .backing_file_offset
298 .checked_add(header.backing_file_size as u64)
299 .ok_or(Error::BackingFileEscapesFirstCluster)?;
300 if backing_file_end > cluster_size {
301 return Err(Error::BackingFileEscapesFirstCluster);
302 }
303 }
304
305 let (backing_file_format, backing_file_path) = Self::parse_backing_info(
306 f,
307 header.header_size,
308 cluster_size,
309 header.backing_file_offset,
310 header.backing_file_size,
311 )?;
312 header.backing_file_format = backing_file_format;
313 header.backing_file_path = backing_file_path;
314 Ok(header)
315 }
316
317 fn parse_backing_info(
318 f: &mut File,
319 header_size: u32,
320 cluster_size: u64,
321 backing_file_offset: u64,
322 backing_file_size: u32,
323 ) -> Result<(Option<crate::ImageType>, Option<String>)> {
324 let mut backing_file_format = None;
325 let mut ext_offset = (header_size as u64 + 7) & !7;
326
327 let limit = if backing_file_offset != 0 {
329 backing_file_offset
330 } else {
331 cluster_size
332 };
333
334 loop {
335 if ext_offset >= limit {
336 break;
337 }
338
339 let next_offset = ext_offset
340 .checked_add(8)
341 .ok_or(Error::InvalidHeaderExtension)?;
342 if next_offset > limit {
343 return Err(Error::InvalidHeaderExtension);
344 }
345
346 f.seek(SeekFrom::Start(ext_offset))
347 .map_err(Error::ReadingHeader)?;
348 let ext_type = read_u32_from_file(f)?;
349 let ext_len = read_u32_from_file(f)?;
350 if ext_type == 0 {
351 break;
352 }
353
354 let ext_end = next_offset
355 .checked_add(ext_len as u64)
356 .ok_or(Error::InvalidHeaderExtension)?;
357 if ext_end > limit {
358 return Err(Error::InvalidHeaderExtension);
359 }
360
361 if ext_type == QCOW2_EXT_MAGIC_BACKING_FORMAT {
362 let mut backing_format_bytes = vec![0u8; ext_len as usize];
363 f.read_exact(&mut backing_format_bytes)
364 .map_err(Error::ReadingHeader)?;
365 let format_str = String::from_utf8(backing_format_bytes)
366 .map_err(|_| Error::InvalidHeaderExtension)?;
367 let format = format_str_to_image_type(&format_str)?;
368 backing_file_format = Some(format);
369 }
370
371 let padded_len = (ext_len as u64 + 7) & !7;
372 let increment = padded_len
373 .checked_add(8)
374 .ok_or(Error::InvalidHeaderExtension)?;
375 ext_offset = ext_offset
376 .checked_add(increment)
377 .ok_or(Error::InvalidHeaderExtension)?;
378 }
379
380 if backing_file_size > MAX_BACKING_FILE_SIZE {
381 return Err(Error::BackingFileTooLong(backing_file_size as usize));
382 }
383 let backing_file_path = if backing_file_offset != 0 {
384 f.seek(SeekFrom::Start(backing_file_offset))
385 .map_err(Error::ReadingHeader)?;
386 let mut backing_file_name_bytes = vec![0u8; backing_file_size as usize];
387 f.read_exact(&mut backing_file_name_bytes)
388 .map_err(Error::ReadingHeader)?;
389 Some(
390 String::from_utf8(backing_file_name_bytes)
391 .map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?,
392 )
393 } else {
394 None
395 };
396
397 Ok((backing_file_format, backing_file_path))
398 }
399
400 pub fn create_for_size_and_path(
401 size: u64,
402 backing: Option<(&str, crate::ImageType)>,
403 ) -> Result<QcowHeader> {
404 let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
405 let cluster_size: u32 = 0x01 << cluster_bits;
406 let mut ext_size = 8; let (backing_file, backing_file_format) = match backing {
408 Some((path, format)) => {
409 let format_str = image_type_to_format_str(format)?;
410 let format_len = format_str.len() as u32;
411 ext_size += 8 + ((format_len + 7) & !7);
412 (Some(path), Some(format))
413 }
414 None => (None, None),
415 };
416 let max_length: usize = (cluster_size - V3_BARE_HEADER_SIZE - ext_size) as usize;
417 if let Some(path) = backing_file {
418 if path.len() > max_length {
419 return Err(Error::BackingFileTooLong(path.len() - max_length));
420 }
421 }
422 if size > MAX_QCOW_FILE_SIZE {
423 return Err(Error::FileTooBig(size));
424 }
425 let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
427 let num_clusters: u32 = size.div_ceil(u64::from(cluster_size)) as u32;
428 let num_l2_clusters: u32 = num_clusters.div_ceil(l2_size);
429 let l1_clusters: u32 = num_l2_clusters.div_ceil(cluster_size);
430 let header_clusters = (size_of::<QcowHeader>() as u32).div_ceil(cluster_size);
431 Ok(QcowHeader {
432 magic: QCOW_MAGIC,
433 version: 3,
434 backing_file_offset: if backing_file.is_none() {
435 0
436 } else {
437 V3_BARE_HEADER_SIZE as u64 + ext_size as u64
438 },
439 backing_file_size: backing_file.map_or(0, |x| x.len()) as u32,
440 cluster_bits: DEFAULT_CLUSTER_BITS,
441 size,
442 crypt_method: 0,
443 l1_size: num_l2_clusters,
444 l1_table_offset: u64::from(cluster_size),
445 refcount_table_offset: u64::from(cluster_size * (l1_clusters + 1)),
447 refcount_table_clusters: {
448 let max_refcount_clusters = max_refcount_clusters(
452 DEFAULT_REFCOUNT_ORDER,
453 cluster_size,
454 num_clusters + l1_clusters + num_l2_clusters + header_clusters,
455 ) as u32;
456 (max_refcount_clusters * size_of::<u64>() as u32).div_ceil(cluster_size)
458 },
459 nb_snapshots: 0,
460 snapshots_offset: 0,
461 incompatible_features: 0,
462 compatible_features: 0,
463 autoclear_features: 0,
464 refcount_order: DEFAULT_REFCOUNT_ORDER,
465 header_size: V3_BARE_HEADER_SIZE,
466 backing_file_path: backing_file.map(String::from),
467 backing_file_format,
468 })
469 }
470
471 pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> {
473 fn write_u32_to_file<F: Write>(f: &mut F, value: u32) -> Result<()> {
475 f.write_all(&value.to_be_bytes())
476 .map_err(Error::WritingHeader)
477 }
478
479 fn write_u64_to_file<F: Write>(f: &mut F, value: u64) -> Result<()> {
481 f.write_all(&value.to_be_bytes())
482 .map_err(Error::WritingHeader)
483 }
484
485 write_u32_to_file(file, self.magic)?;
486 write_u32_to_file(file, self.version)?;
487 write_u64_to_file(file, self.backing_file_offset)?;
488 write_u32_to_file(file, self.backing_file_size)?;
489 write_u32_to_file(file, self.cluster_bits)?;
490 write_u64_to_file(file, self.size)?;
491 write_u32_to_file(file, self.crypt_method)?;
492 write_u32_to_file(file, self.l1_size)?;
493 write_u64_to_file(file, self.l1_table_offset)?;
494 write_u64_to_file(file, self.refcount_table_offset)?;
495 write_u32_to_file(file, self.refcount_table_clusters)?;
496 write_u32_to_file(file, self.nb_snapshots)?;
497 write_u64_to_file(file, self.snapshots_offset)?;
498 write_u64_to_file(file, self.incompatible_features)?;
499 write_u64_to_file(file, self.compatible_features)?;
500 write_u64_to_file(file, self.autoclear_features)?;
501 write_u32_to_file(file, self.refcount_order)?;
502 write_u32_to_file(file, self.header_size)?;
503 if let Some(format) = self.backing_file_format {
504 let format_str = image_type_to_format_str(format)?;
505 write_u32_to_file(file, QCOW2_EXT_MAGIC_BACKING_FORMAT)?;
506 write_u32_to_file(file, format_str.len() as u32)?;
507 file.write_all(format_str.as_bytes())
508 .map_err(Error::WritingHeader)?;
509 let padding = ((format_str.len() + 7) & !7) - format_str.len();
511 if padding > 0 {
512 const ZEROES: [u8; 8] = [0u8; 8];
513 file.write_all(&ZEROES[..padding])
514 .map_err(Error::WritingHeader)?;
515 }
516 }
517 write_u32_to_file(file, 0)?; write_u32_to_file(file, 0)?; if let Some(backing_file_path) = self.backing_file_path.as_ref() {
520 write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?;
521 }
522
523 let cluster_size = 0x01u64 << self.cluster_bits;
527 let refcount_blocks_size = u64::from(self.refcount_table_clusters) * cluster_size;
528 file.seek(SeekFrom::Start(
529 self.refcount_table_offset + refcount_blocks_size - 2,
530 ))
531 .map_err(Error::WritingHeader)?;
532 file.write(&[0u8]).map_err(Error::WritingHeader)?;
533
534 Ok(())
535 }
536}
537
538fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 {
539 let refcount_bytes = (0x01 << refcount_order as u64) / 8;
541 let for_data = (u64::from(num_clusters) * refcount_bytes).div_ceil(u64::from(cluster_size));
542 let for_refcounts = (for_data * refcount_bytes).div_ceil(u64::from(cluster_size));
543 for_data + for_refcounts
544}
545
546#[derive(Debug)]
569pub struct QcowFile {
570 inner: Mutex<QcowFileInner>,
571 virtual_size: u64,
573}
574
575#[derive(Debug)]
576struct QcowFileInner {
577 raw_file: QcowRawFile,
578 header: QcowHeader,
579 l1_table: VecCache<u64>,
580 l2_entries: u64,
581 l2_cache: CacheMap<VecCache<u64>>,
582 refcounts: RefCount,
583 current_offset: u64,
584 unref_clusters: Vec<u64>, avail_clusters: Vec<u64>,
588 backing_file: Option<Box<dyn DiskFile>>,
589}
590
591impl DiskFile for QcowFile {}
592
593impl DiskFlush for QcowFile {
594 fn flush(&self) -> io::Result<()> {
595 self.fsync()
598 }
599}
600
601impl QcowFile {
602 pub fn from(mut file: File, params: DiskFileParams) -> Result<QcowFile> {
604 let header = QcowHeader::new(&mut file)?;
605
606 if header.version != 3 {
608 return Err(Error::UnsupportedVersion(header.version));
609 }
610
611 if u64::from(header.l1_size) > MAX_RAM_POINTER_TABLE_SIZE {
613 return Err(Error::InvalidL1TableSize(header.l1_size));
614 }
615
616 let cluster_bits: u32 = header.cluster_bits;
617 if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
618 return Err(Error::InvalidClusterSize);
619 }
620 let cluster_size = 0x01u64 << cluster_bits;
621
622 if header.size > MAX_QCOW_FILE_SIZE {
624 return Err(Error::FileTooBig(header.size));
625 }
626
627 let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
628 let backing_params = DiskFileParams {
629 path: PathBuf::from(backing_file_path),
630 is_read_only: true,
632 is_direct: params.is_direct,
635 lock: params.lock,
636 depth: params.depth + 1,
637 ..Default::default()
638 };
639 let backing_image_type = match header.backing_file_format {
640 Some(format) => format,
641 None => return Err(Error::MissingBackingFileFormat),
642 };
643 let backing_disk_file = crate::open_disk_file_as(backing_params, backing_image_type)
644 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
645 Some(backing_disk_file)
646 } else {
647 None
648 };
649
650 let refcount_bits: u64 = 0x01u64
652 .checked_shl(header.refcount_order)
653 .ok_or(Error::UnsupportedRefcountOrder)?;
654 if refcount_bits != 16 {
655 return Err(Error::UnsupportedRefcountOrder);
656 }
657 let refcount_bytes = refcount_bits.div_ceil(8);
658
659 if header.refcount_table_clusters == 0 {
661 return Err(Error::NoRefcountClusters);
662 }
663 offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
664 offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
665 offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
667 let file_size = file.metadata().map_err(Error::GettingFileSize)?.len();
668 if header.refcount_table_offset > max(file_size, header.size) {
669 return Err(Error::RefcountTableOffEnd);
670 }
671
672 let mut refcount_rebuild_required = true;
675 file.seek(SeekFrom::Start(header.refcount_table_offset))
676 .map_err(Error::SeekingFile)?;
677 let first_refblock_addr = read_u64_from_file(&file)?;
678 if first_refblock_addr != 0 {
679 file.seek(SeekFrom::Start(first_refblock_addr))
680 .map_err(Error::SeekingFile)?;
681 let first_cluster_refcount = read_u16_from_file(&file)?;
682 if first_cluster_refcount != 0 {
683 refcount_rebuild_required = false;
684 }
685 }
686
687 if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
688 refcount_rebuild_required = true;
689 }
690
691 let mut raw_file =
692 QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
693 if refcount_rebuild_required {
694 QcowFileInner::rebuild_refcounts(&mut raw_file, header.clone())?;
695 }
696
697 let l2_size = cluster_size / size_of::<u64>() as u64;
698 let num_clusters = header.size.div_ceil(cluster_size);
699 let num_l2_clusters = num_clusters.div_ceil(l2_size);
700 let l1_clusters = num_l2_clusters.div_ceil(cluster_size);
701 let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
702 if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
703 return Err(Error::TooManyL1Entries(num_l2_clusters));
704 }
705 let l1_table = VecCache::from_vec(
706 raw_file
707 .read_pointer_table(
708 header.l1_table_offset,
709 num_l2_clusters,
710 Some(L1_TABLE_OFFSET_MASK),
711 )
712 .map_err(Error::ReadingHeader)?,
713 );
714
715 let num_clusters = header.size.div_ceil(cluster_size);
716 let refcount_clusters = max_refcount_clusters(
717 header.refcount_order,
718 cluster_size as u32,
719 (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
720 );
721 if u64::from(header.refcount_table_clusters) > 2 * refcount_clusters {
723 return Err(Error::RefcountTableTooLarge);
724 }
725 if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
726 return Err(Error::TooManyRefcounts(refcount_clusters));
727 }
728 let refcount_block_entries = cluster_size / refcount_bytes;
729 let refcounts = RefCount::new(
730 &mut raw_file,
731 header.refcount_table_offset,
732 refcount_clusters,
733 refcount_block_entries,
734 cluster_size,
735 )
736 .map_err(Error::ReadingRefCounts)?;
737
738 let l2_entries = cluster_size / size_of::<u64>() as u64;
739
740 let mut inner = QcowFileInner {
741 raw_file,
742 header,
743 l1_table,
744 l2_entries,
745 l2_cache: CacheMap::new(100),
746 refcounts,
747 current_offset: 0,
748 unref_clusters: Vec::new(),
749 avail_clusters: Vec::new(),
750 backing_file,
751 };
752
753 inner
755 .header
756 .l1_table_offset
757 .checked_add(inner.l1_address_offset(inner.virtual_size()))
758 .ok_or(Error::InvalidL1TableOffset)?;
759 inner
760 .header
761 .refcount_table_offset
762 .checked_add(u64::from(inner.header.refcount_table_clusters) * cluster_size)
763 .ok_or(Error::InvalidRefcountTableOffset)?;
764
765 inner.find_avail_clusters()?;
766
767 let virtual_size = inner.virtual_size();
768 Ok(QcowFile {
769 inner: Mutex::new(inner),
770 virtual_size,
771 })
772 }
773
774 pub fn new(file: File, params: DiskFileParams, virtual_size: u64) -> Result<QcowFile> {
776 let header = QcowHeader::create_for_size_and_path(virtual_size, None)?;
777 QcowFile::new_from_header(file, params, header)
778 }
779
780 pub fn new_from_backing(
782 file: File,
783 params: DiskFileParams,
784 backing_file_name: &str,
785 ) -> Result<QcowFile> {
786 let (size, backing_format) = {
789 let backing_params = DiskFileParams {
790 path: PathBuf::from(backing_file_name),
791 is_read_only: true,
793 is_direct: params.is_direct,
796 lock: params.lock,
797 depth: params.depth + 1,
798 ..Default::default()
799 };
800 let backing_file = crate::sys::open_raw_disk_image(&backing_params)
801 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
802 let backing_image_type = crate::detect_image_type(&backing_file, false)
803 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
804 let backing_disk_file =
805 crate::disk_file_from_file(backing_file, backing_params, backing_image_type)
806 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
807 (
808 backing_disk_file.get_len().map_err(Error::BackingFileIo)?,
809 backing_image_type,
810 )
811 };
812 let header =
813 QcowHeader::create_for_size_and_path(size, Some((backing_file_name, backing_format)))?;
814 QcowFile::new_from_header(file, params, header)
815 }
816
817 fn new_from_header(
818 mut file: File,
819 params: DiskFileParams,
820 header: QcowHeader,
821 ) -> Result<QcowFile> {
822 file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
823 header.write_to(&mut file)?;
824
825 let mut qcow = Self::from(file, params)?;
826 let inner = qcow.inner.get_mut();
827
828 let cluster_size = 0x01u64 << inner.header.cluster_bits;
830 let refcount_table_base = inner.header.refcount_table_offset;
831 let end_cluster_addr =
832 refcount_table_base + u64::from(inner.header.refcount_table_clusters) * cluster_size;
833
834 let mut cluster_addr = 0;
835 while cluster_addr < end_cluster_addr {
836 let mut unref_clusters = inner
837 .set_cluster_refcount(cluster_addr, 1)
838 .map_err(Error::SettingRefcountRefcount)?;
839 inner.unref_clusters.append(&mut unref_clusters);
840 cluster_addr += cluster_size;
841 }
842
843 Ok(qcow)
844 }
845
846 pub fn set_backing_file(&mut self, backing: Option<Box<dyn DiskFile>>) {
847 self.inner.get_mut().backing_file = backing;
848 }
849}
850
851impl QcowFileInner {
852 #[cfg(test)]
854 fn first_zero_refcount(&mut self) -> Result<Option<u64>> {
855 let file_size = self
856 .raw_file
857 .file_mut()
858 .metadata()
859 .map_err(Error::GettingFileSize)?
860 .len();
861 let cluster_size = 0x01u64 << self.header.cluster_bits;
862
863 let mut cluster_addr = 0;
864 while cluster_addr < file_size {
865 let cluster_refcount = self
866 .refcounts
867 .get_cluster_refcount(&mut self.raw_file, cluster_addr)
868 .map_err(Error::GettingRefcount)?;
869 if cluster_refcount == 0 {
870 return Ok(Some(cluster_addr));
871 }
872 cluster_addr += cluster_size;
873 }
874 Ok(None)
875 }
876
877 fn find_avail_clusters(&mut self) -> Result<()> {
878 let cluster_size = self.raw_file.cluster_size();
879
880 let file_size = self
881 .raw_file
882 .file_mut()
883 .metadata()
884 .map_err(Error::GettingFileSize)?
885 .len();
886
887 for i in (0..file_size).step_by(cluster_size as usize) {
888 let refcount = self
889 .refcounts
890 .get_cluster_refcount(&mut self.raw_file, i)
891 .map_err(Error::GettingRefcount)?;
892 if refcount == 0 {
893 self.avail_clusters.push(i);
894 }
895 }
896
897 Ok(())
898 }
899
900 fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()> {
902 fn add_ref(refcounts: &mut [u16], cluster_size: u64, cluster_address: u64) -> Result<()> {
903 let idx = (cluster_address / cluster_size) as usize;
904 if idx >= refcounts.len() {
905 return Err(Error::InvalidClusterIndex);
906 }
907 refcounts[idx] += 1;
908 Ok(())
909 }
910
911 fn set_header_refcount(refcounts: &mut [u16], cluster_size: u64) -> Result<()> {
913 add_ref(refcounts, cluster_size, 0)
914 }
915
916 fn set_l1_refcounts(
918 refcounts: &mut [u16],
919 header: QcowHeader,
920 cluster_size: u64,
921 ) -> Result<()> {
922 let l1_clusters = u64::from(header.l1_size).div_ceil(cluster_size);
923 let l1_table_offset = header.l1_table_offset;
924 for i in 0..l1_clusters {
925 add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
926 }
927 Ok(())
928 }
929
930 fn set_data_refcounts(
932 refcounts: &mut [u16],
933 header: QcowHeader,
934 cluster_size: u64,
935 raw_file: &mut QcowRawFile,
936 ) -> Result<()> {
937 let l1_table = raw_file
938 .read_pointer_table(
939 header.l1_table_offset,
940 header.l1_size as u64,
941 Some(L1_TABLE_OFFSET_MASK),
942 )
943 .map_err(Error::ReadingPointers)?;
944 for l1_index in 0..header.l1_size as usize {
945 let l2_addr_disk = *l1_table.get(l1_index).ok_or(Error::InvalidIndex)?;
946 if l2_addr_disk != 0 {
947 add_ref(refcounts, cluster_size, l2_addr_disk)?;
949
950 let l2_table = raw_file
952 .read_pointer_table(
953 l2_addr_disk,
954 cluster_size / size_of::<u64>() as u64,
955 Some(L2_TABLE_OFFSET_MASK),
956 )
957 .map_err(Error::ReadingPointers)?;
958 for data_cluster_addr in l2_table {
959 if data_cluster_addr != 0 {
960 add_ref(refcounts, cluster_size, data_cluster_addr)?;
961 }
962 }
963 }
964 }
965
966 Ok(())
967 }
968
969 fn set_refcount_table_refcounts(
971 refcounts: &mut [u16],
972 header: QcowHeader,
973 cluster_size: u64,
974 ) -> Result<()> {
975 let refcount_table_offset = header.refcount_table_offset;
976 for i in 0..header.refcount_table_clusters as u64 {
977 add_ref(
978 refcounts,
979 cluster_size,
980 refcount_table_offset + i * cluster_size,
981 )?;
982 }
983 Ok(())
984 }
985
986 fn alloc_refblocks(
990 refcounts: &mut [u16],
991 cluster_size: u64,
992 refblock_clusters: u64,
993 pointers_per_cluster: u64,
994 ) -> Result<Vec<u64>> {
995 let refcount_table_entries = refblock_clusters.div_ceil(pointers_per_cluster);
996 let mut ref_table = vec![0; refcount_table_entries as usize];
997 let mut first_free_cluster: u64 = 0;
998 for refblock_addr in &mut ref_table {
999 loop {
1000 if first_free_cluster >= refcounts.len() as u64 {
1001 return Err(Error::NotEnoughSpaceForRefcounts);
1002 }
1003 if refcounts[first_free_cluster as usize] == 0 {
1004 break;
1005 }
1006 first_free_cluster += 1;
1007 }
1008
1009 *refblock_addr = first_free_cluster * cluster_size;
1010 add_ref(refcounts, cluster_size, *refblock_addr)?;
1011
1012 first_free_cluster += 1;
1013 }
1014
1015 Ok(ref_table)
1016 }
1017
1018 fn write_refblocks(
1020 refcounts: &[u16],
1021 mut header: QcowHeader,
1022 ref_table: &[u64],
1023 raw_file: &mut QcowRawFile,
1024 refcount_block_entries: u64,
1025 ) -> Result<()> {
1026 header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
1028 raw_file
1029 .file_mut()
1030 .seek(SeekFrom::Start(0))
1031 .map_err(Error::SeekingFile)?;
1032 header.write_to(raw_file.file_mut())?;
1033
1034 for (i, refblock_addr) in ref_table.iter().enumerate() {
1035 let refblock_start = i * (refcount_block_entries as usize);
1037 let refblock_end = min(
1038 refcounts.len(),
1039 refblock_start + refcount_block_entries as usize,
1040 );
1041 let refblock = &refcounts[refblock_start..refblock_end];
1042 raw_file
1043 .write_refcount_block(*refblock_addr, refblock)
1044 .map_err(Error::WritingHeader)?;
1045
1046 if refblock.len() < refcount_block_entries as usize {
1048 let refblock_padding =
1049 vec![0u16; refcount_block_entries as usize - refblock.len()];
1050 raw_file
1051 .write_refcount_block(
1052 *refblock_addr + refblock.len() as u64 * 2,
1053 &refblock_padding,
1054 )
1055 .map_err(Error::WritingHeader)?;
1056 }
1057 }
1058
1059 raw_file
1061 .write_pointer_table(header.refcount_table_offset, ref_table, 0)
1062 .map_err(Error::WritingHeader)?;
1063
1064 header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
1066 raw_file
1067 .file_mut()
1068 .seek(SeekFrom::Start(0))
1069 .map_err(Error::SeekingFile)?;
1070 header.write_to(raw_file.file_mut())?;
1071
1072 Ok(())
1073 }
1074
1075 let cluster_size = raw_file.cluster_size();
1076
1077 let file_size = raw_file
1078 .file_mut()
1079 .metadata()
1080 .map_err(Error::GettingFileSize)?
1081 .len();
1082
1083 let refcount_bits = 1u64 << header.refcount_order;
1084 let refcount_bytes = refcount_bits.div_ceil(8);
1085 let refcount_block_entries = cluster_size / refcount_bytes;
1086 let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
1087 let data_clusters = header.size.div_ceil(cluster_size);
1088 let l2_clusters = data_clusters.div_ceil(pointers_per_cluster);
1089 let l1_clusters = l2_clusters.div_ceil(cluster_size);
1090 let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size);
1091 let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
1092 let mut max_valid_cluster_index = max_clusters;
1093 let refblock_clusters = max_valid_cluster_index.div_ceil(refcount_block_entries);
1094 let reftable_clusters = refblock_clusters.div_ceil(pointers_per_cluster);
1095 let refblocks_for_refs =
1097 (refblock_clusters + reftable_clusters).div_ceil(refcount_block_entries);
1098 let reftable_clusters_for_refs = refblocks_for_refs.div_ceil(refcount_block_entries);
1099 max_valid_cluster_index += refblock_clusters + reftable_clusters;
1100 max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs;
1101
1102 if max_valid_cluster_index > MAX_RAM_POINTER_TABLE_SIZE {
1103 return Err(Error::InvalidRefcountTableSize(max_valid_cluster_index));
1104 }
1105
1106 let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
1107 if max_valid_cluster_offset < file_size - cluster_size {
1108 return Err(Error::InvalidRefcountTableSize(max_valid_cluster_offset));
1109 }
1110
1111 let mut refcounts = vec![0; max_valid_cluster_index as usize];
1112
1113 set_header_refcount(&mut refcounts, cluster_size)?;
1115 set_l1_refcounts(&mut refcounts, header.clone(), cluster_size)?;
1116 set_data_refcounts(&mut refcounts, header.clone(), cluster_size, raw_file)?;
1117 set_refcount_table_refcounts(&mut refcounts, header.clone(), cluster_size)?;
1118
1119 let ref_table = alloc_refblocks(
1121 &mut refcounts,
1122 cluster_size,
1123 refblock_clusters,
1124 pointers_per_cluster,
1125 )?;
1126
1127 write_refblocks(
1129 &refcounts,
1130 header,
1131 &ref_table,
1132 raw_file,
1133 refcount_block_entries,
1134 )
1135 }
1136
1137 fn limit_range_file(&self, address: u64, count: usize) -> usize {
1139 if address.checked_add(count as u64).is_none() || address > self.virtual_size() {
1140 return 0;
1141 }
1142 min(count as u64, self.virtual_size() - address) as usize
1143 }
1144
1145 fn limit_range_cluster(&self, address: u64, count: usize) -> usize {
1147 let offset: u64 = self.raw_file.cluster_offset(address);
1148 let limit = self.raw_file.cluster_size() - offset;
1149 min(count as u64, limit) as usize
1150 }
1151
1152 fn virtual_size(&self) -> u64 {
1154 self.header.size
1155 }
1156
1157 fn l1_address_offset(&self, address: u64) -> u64 {
1159 let l1_index = self.l1_table_index(address);
1160 l1_index * size_of::<u64>() as u64
1161 }
1162
1163 fn l1_table_index(&self, address: u64) -> u64 {
1165 (address / self.raw_file.cluster_size()) / self.l2_entries
1166 }
1167
1168 fn l2_table_index(&self, address: u64) -> u64 {
1170 (address / self.raw_file.cluster_size()) % self.l2_entries
1171 }
1172
1173 fn file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>> {
1176 if address >= self.virtual_size() {
1177 return Err(std::io::Error::from_raw_os_error(EINVAL));
1178 }
1179
1180 let l1_index = self.l1_table_index(address) as usize;
1181 let l2_addr_disk = *self
1182 .l1_table
1183 .get(l1_index)
1184 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1185
1186 if l2_addr_disk == 0 {
1187 return Ok(None);
1189 }
1190
1191 let l2_index = self.l2_table_index(address) as usize;
1192
1193 if !self.l2_cache.contains_key(&l1_index) {
1194 let table =
1196 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
1197
1198 let l1_table = &self.l1_table;
1199 let raw_file = &mut self.raw_file;
1200 self.l2_cache.insert(l1_index, table, |index, evicted| {
1201 raw_file.write_pointer_table(
1202 l1_table[index],
1203 evicted.get_values(),
1204 CLUSTER_USED_FLAG,
1205 )
1206 })?;
1207 };
1208
1209 let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1210 if cluster_addr == 0 {
1211 return Ok(None);
1212 }
1213 Ok(Some(cluster_addr + self.raw_file.cluster_offset(address)))
1214 }
1215
1216 fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
1219 if address >= self.virtual_size() {
1220 return Err(std::io::Error::from_raw_os_error(EINVAL));
1221 }
1222
1223 let l1_index = self.l1_table_index(address) as usize;
1224 let l2_addr_disk = *self
1225 .l1_table
1226 .get(l1_index)
1227 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1228 let l2_index = self.l2_table_index(address) as usize;
1229
1230 let mut set_refcounts = Vec::new();
1231
1232 if !self.l2_cache.contains_key(&l1_index) {
1233 let l2_table = if l2_addr_disk == 0 {
1235 let new_addr: u64 = self.get_new_cluster(None)?;
1238 set_refcounts.push((new_addr, 1));
1240 self.l1_table[l1_index] = new_addr;
1241 VecCache::new(self.l2_entries as usize)
1242 } else {
1243 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
1244 };
1245 let l1_table = &self.l1_table;
1246 let raw_file = &mut self.raw_file;
1247 self.l2_cache.insert(l1_index, l2_table, |index, evicted| {
1248 raw_file.write_pointer_table(
1249 l1_table[index],
1250 evicted.get_values(),
1251 CLUSTER_USED_FLAG,
1252 )
1253 })?;
1254 }
1255
1256 let cluster_addr = match self.l2_cache.get(&l1_index).unwrap()[l2_index] {
1257 0 => {
1258 let initial_data = if let Some(backing) = self.backing_file.as_mut() {
1259 let cluster_size = self.raw_file.cluster_size();
1260 let cluster_begin = address - (address % cluster_size);
1261 let mut cluster_data = vec![0u8; cluster_size as usize];
1262 let volatile_slice = VolatileSlice::new(&mut cluster_data);
1263 backing.read_exact_at_volatile(volatile_slice, cluster_begin)?;
1264 Some(cluster_data)
1265 } else {
1266 None
1267 };
1268 let cluster_addr = self.append_data_cluster(initial_data)?;
1270 self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
1271 cluster_addr
1272 }
1273 a => a,
1274 };
1275
1276 for (addr, count) in set_refcounts {
1277 let mut newly_unref = self.set_cluster_refcount(addr, count)?;
1278 self.unref_clusters.append(&mut newly_unref);
1279 }
1280
1281 Ok(cluster_addr + self.raw_file.cluster_offset(address))
1282 }
1283
1284 fn update_cluster_addr(
1286 &mut self,
1287 l1_index: usize,
1288 l2_index: usize,
1289 cluster_addr: u64,
1290 set_refcounts: &mut Vec<(u64, u16)>,
1291 ) -> io::Result<()> {
1292 if !self.l2_cache.get(&l1_index).unwrap().dirty() {
1293 let addr = self.l1_table[l1_index];
1298 if addr != 0 {
1299 self.unref_clusters.push(addr);
1300 set_refcounts.push((addr, 0));
1301 }
1302
1303 let new_addr: u64 = self.get_new_cluster(None)?;
1307 set_refcounts.push((new_addr, 1));
1310 self.l1_table[l1_index] = new_addr;
1311 }
1312 self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = cluster_addr;
1314 Ok(())
1315 }
1316
1317 fn get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1319 if let Some(free_cluster) = self.avail_clusters.pop() {
1321 if let Some(initial_data) = initial_data {
1322 self.raw_file.write_cluster(free_cluster, initial_data)?;
1323 } else {
1324 self.raw_file.zero_cluster(free_cluster)?;
1325 }
1326 return Ok(free_cluster);
1327 }
1328
1329 let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
1330 if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
1331 if let Some(initial_data) = initial_data {
1332 self.raw_file.write_cluster(new_cluster, initial_data)?;
1333 }
1334 Ok(new_cluster)
1335 } else {
1336 error!("No free clusters in get_new_cluster()");
1337 Err(std::io::Error::from_raw_os_error(ENOSPC))
1338 }
1339 }
1340
1341 fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1344 let new_addr: u64 = self.get_new_cluster(initial_data)?;
1345 let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
1347 self.unref_clusters.append(&mut newly_unref);
1348 Ok(new_addr)
1349 }
1350
1351 fn deallocate_cluster(&mut self, address: u64) -> std::io::Result<()> {
1354 if address >= self.virtual_size() {
1355 return Err(std::io::Error::from_raw_os_error(EINVAL));
1356 }
1357
1358 let l1_index = self.l1_table_index(address) as usize;
1359 let l2_addr_disk = *self
1360 .l1_table
1361 .get(l1_index)
1362 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1363 let l2_index = self.l2_table_index(address) as usize;
1364
1365 if l2_addr_disk == 0 {
1366 return Ok(());
1369 }
1370
1371 if !self.l2_cache.contains_key(&l1_index) {
1372 let table =
1374 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
1375 let l1_table = &self.l1_table;
1376 let raw_file = &mut self.raw_file;
1377 self.l2_cache.insert(l1_index, table, |index, evicted| {
1378 raw_file.write_pointer_table(
1379 l1_table[index],
1380 evicted.get_values(),
1381 CLUSTER_USED_FLAG,
1382 )
1383 })?;
1384 }
1385
1386 let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1387 if cluster_addr == 0 {
1388 return Ok(());
1390 }
1391
1392 let refcount = self
1394 .refcounts
1395 .get_cluster_refcount(&mut self.raw_file, cluster_addr)
1396 .map_err(|_| std::io::Error::from_raw_os_error(EINVAL))?;
1397 if refcount == 0 {
1398 return Err(std::io::Error::from_raw_os_error(EINVAL));
1399 }
1400
1401 let new_refcount = refcount - 1;
1402 let mut newly_unref = self.set_cluster_refcount(cluster_addr, new_refcount)?;
1403 self.unref_clusters.append(&mut newly_unref);
1404
1405 self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = 0;
1408
1409 if new_refcount == 0 {
1410 let cluster_size = self.raw_file.cluster_size();
1411 let _ = self.raw_file.file().punch_hole(cluster_addr, cluster_size);
1415 self.unref_clusters.push(cluster_addr);
1416 }
1417 Ok(())
1418 }
1419
1420 fn zero_bytes(&mut self, address: u64, length: usize) -> std::io::Result<()> {
1424 let write_count: usize = self.limit_range_file(address, length);
1425
1426 let mut nwritten: usize = 0;
1427 while nwritten < write_count {
1428 let curr_addr = address + nwritten as u64;
1429 let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1430
1431 if self.backing_file.is_none() && count == self.raw_file.cluster_size() as usize {
1432 self.deallocate_cluster(curr_addr)?;
1434 } else {
1435 let offset = if self.backing_file.is_some() {
1437 Some(self.file_offset_write(curr_addr)?)
1441 } else {
1442 self.file_offset_read(curr_addr)?
1445 };
1446 if let Some(offset) = offset {
1447 self.raw_file.file().write_zeroes_all_at(offset, count)?;
1449 }
1450 }
1451
1452 nwritten += count;
1453 }
1454 Ok(())
1455 }
1456
1457 fn read_l2_cluster(raw_file: &mut QcowRawFile, cluster_addr: u64) -> std::io::Result<Vec<u64>> {
1460 let file_values = raw_file.read_pointer_cluster(cluster_addr, None)?;
1461 if file_values.iter().any(|entry| entry & COMPRESSED_FLAG != 0) {
1462 return Err(std::io::Error::from_raw_os_error(ENOTSUP));
1463 }
1464 Ok(file_values
1465 .iter()
1466 .map(|entry| *entry & L2_TABLE_OFFSET_MASK)
1467 .collect())
1468 }
1469
1470 fn set_cluster_refcount(&mut self, address: u64, refcount: u16) -> std::io::Result<Vec<u64>> {
1474 let mut added_clusters = Vec::new();
1475 let mut unref_clusters = Vec::new();
1476 let mut refcount_set = false;
1477 let mut new_cluster = None;
1478
1479 while !refcount_set {
1480 match self.refcounts.set_cluster_refcount(
1481 &mut self.raw_file,
1482 address,
1483 refcount,
1484 new_cluster.take(),
1485 ) {
1486 Ok(None) => {
1487 refcount_set = true;
1488 }
1489 Ok(Some(freed_cluster)) => {
1490 unref_clusters.push(freed_cluster);
1491 refcount_set = true;
1492 }
1493 Err(refcount::Error::EvictingRefCounts(e)) => {
1494 return Err(e);
1495 }
1496 Err(refcount::Error::InvalidIndex) => {
1497 return Err(std::io::Error::from_raw_os_error(EINVAL));
1498 }
1499 Err(refcount::Error::NeedCluster(addr)) => {
1500 new_cluster = Some((
1502 addr,
1503 VecCache::from_vec(self.raw_file.read_refcount_block(addr)?),
1504 ));
1505 }
1506 Err(refcount::Error::NeedNewCluster) => {
1507 let addr = self.get_new_cluster(None)?;
1509 added_clusters.push(addr);
1510 new_cluster = Some((
1511 addr,
1512 VecCache::new(self.refcounts.refcounts_per_block() as usize),
1513 ));
1514 }
1515 Err(refcount::Error::ReadingRefCounts(e)) => {
1516 return Err(e);
1517 }
1518 }
1519 }
1520
1521 for addr in added_clusters {
1522 self.set_cluster_refcount(addr, 1)?;
1523 }
1524 Ok(unref_clusters)
1525 }
1526
1527 fn sync_caches(&mut self) -> std::io::Result<()> {
1528 for (l1_index, l2_table) in self.l2_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
1530 let addr = self.l1_table[*l1_index];
1532 if addr != 0 {
1533 self.raw_file.write_pointer_table(
1534 addr,
1535 l2_table.get_values(),
1536 CLUSTER_USED_FLAG,
1537 )?;
1538 } else {
1539 return Err(std::io::Error::from_raw_os_error(EINVAL));
1540 }
1541 l2_table.mark_clean();
1542 }
1543 self.refcounts.flush_blocks(&mut self.raw_file)?;
1545 self.raw_file.file_mut().sync_all()?;
1547
1548 let mut sync_required = false;
1551 if self.l1_table.dirty() {
1552 self.raw_file.write_pointer_table(
1553 self.header.l1_table_offset,
1554 self.l1_table.get_values(),
1555 0,
1556 )?;
1557 self.l1_table.mark_clean();
1558 sync_required = true;
1559 }
1560 sync_required |= self.refcounts.flush_table(&mut self.raw_file)?;
1561 if sync_required {
1562 self.raw_file.file_mut().sync_data()?;
1563 }
1564 Ok(())
1565 }
1566
1567 fn read_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1572 where
1573 F: FnMut(Option<&mut dyn DiskFile>, usize, u64, usize) -> std::io::Result<()>,
1574 {
1575 let read_count: usize = self.limit_range_file(address, count);
1576
1577 let mut nread: usize = 0;
1578 while nread < read_count {
1579 let curr_addr = address + nread as u64;
1580 let file_offset = self.file_offset_read(curr_addr)?;
1581 let count = self.limit_range_cluster(curr_addr, read_count - nread);
1582
1583 if let Some(offset) = file_offset {
1584 cb(Some(self.raw_file.file_mut()), nread, offset, count)?;
1585 } else if let Some(backing) = self.backing_file.as_mut() {
1586 cb(Some(backing.as_mut()), nread, curr_addr, count)?;
1587 } else {
1588 cb(None, nread, 0, count)?;
1589 }
1590
1591 nread += count;
1592 }
1593 Ok(read_count)
1594 }
1595
1596 fn write_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1600 where
1601 F: FnMut(&mut File, usize, u64, usize) -> std::io::Result<()>,
1602 {
1603 let write_count: usize = self.limit_range_file(address, count);
1604
1605 let mut nwritten: usize = 0;
1606 while nwritten < write_count {
1607 let curr_addr = address + nwritten as u64;
1608 let offset = self.file_offset_write(curr_addr)?;
1609 let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1610
1611 cb(self.raw_file.file_mut(), nwritten, offset, count)?;
1612
1613 nwritten += count;
1614 }
1615 Ok(write_count)
1616 }
1617}
1618
1619impl Drop for QcowFile {
1620 fn drop(&mut self) {
1621 let _ = self.inner.get_mut().sync_caches();
1622 }
1623}
1624
1625impl AsRawDescriptors for QcowFile {
1626 fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
1627 let inner = self.inner.lock();
1630 let mut descriptors = vec![inner.raw_file.file().as_raw_descriptor()];
1631 if let Some(backing) = &inner.backing_file {
1632 descriptors.append(&mut backing.as_raw_descriptors());
1633 }
1634 descriptors
1635 }
1636}
1637
1638impl Read for QcowFile {
1639 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1640 let inner = self.inner.get_mut();
1641 let len = buf.len();
1642 let slice = VolatileSlice::new(buf);
1643 let read_count = inner.read_cb(
1644 inner.current_offset,
1645 len,
1646 |file, already_read, offset, count| {
1647 let sub_slice = slice.get_slice(already_read, count).unwrap();
1648 match file {
1649 Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1650 None => {
1651 sub_slice.write_bytes(0);
1652 Ok(())
1653 }
1654 }
1655 },
1656 )?;
1657 inner.current_offset += read_count as u64;
1658 Ok(read_count)
1659 }
1660}
1661
1662impl Seek for QcowFile {
1663 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
1664 let inner = self.inner.get_mut();
1665 let new_offset: Option<u64> = match pos {
1666 SeekFrom::Start(off) => Some(off),
1667 SeekFrom::End(off) => {
1668 if off < 0 {
1669 0i64.checked_sub(off)
1670 .and_then(|increment| inner.virtual_size().checked_sub(increment as u64))
1671 } else {
1672 inner.virtual_size().checked_add(off as u64)
1673 }
1674 }
1675 SeekFrom::Current(off) => {
1676 if off < 0 {
1677 0i64.checked_sub(off)
1678 .and_then(|increment| inner.current_offset.checked_sub(increment as u64))
1679 } else {
1680 inner.current_offset.checked_add(off as u64)
1681 }
1682 }
1683 };
1684
1685 if let Some(o) = new_offset {
1686 if o <= inner.virtual_size() {
1687 inner.current_offset = o;
1688 return Ok(o);
1689 }
1690 }
1691 Err(std::io::Error::from_raw_os_error(EINVAL))
1692 }
1693}
1694
1695impl Write for QcowFile {
1696 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1697 let inner = self.inner.get_mut();
1698 let write_count = inner.write_cb(
1699 inner.current_offset,
1700 buf.len(),
1701 |file, offset, raw_offset, count| {
1702 file.seek(SeekFrom::Start(raw_offset))?;
1703 file.write_all(&buf[offset..(offset + count)])
1704 },
1705 )?;
1706 inner.current_offset += write_count as u64;
1707 Ok(write_count)
1708 }
1709
1710 fn flush(&mut self) -> std::io::Result<()> {
1711 self.fsync()
1712 }
1713}
1714
1715impl FileReadWriteAtVolatile for QcowFile {
1716 fn read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1717 let mut inner = self.inner.lock();
1718 inner.read_cb(offset, slice.size(), |file, read, offset, count| {
1719 let sub_slice = slice.get_slice(read, count).unwrap();
1720 match file {
1721 Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1722 None => {
1723 sub_slice.write_bytes(0);
1724 Ok(())
1725 }
1726 }
1727 })
1728 }
1729
1730 fn write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1731 let mut inner = self.inner.lock();
1732 inner.write_cb(offset, slice.size(), |file, offset, raw_offset, count| {
1733 let sub_slice = slice.get_slice(offset, count).unwrap();
1734 file.write_all_at_volatile(sub_slice, raw_offset)
1735 })
1736 }
1737}
1738
1739impl FileSync for QcowFile {
1740 fn fsync(&self) -> std::io::Result<()> {
1741 let mut inner = self.inner.lock();
1742 inner.sync_caches()?;
1743 let unref_clusters = std::mem::take(&mut inner.unref_clusters);
1744 inner.avail_clusters.extend(unref_clusters);
1745 Ok(())
1746 }
1747
1748 fn fdatasync(&self) -> io::Result<()> {
1749 self.fsync()
1751 }
1752}
1753
1754impl FileSetLen for QcowFile {
1755 fn set_len(&self, _len: u64) -> std::io::Result<()> {
1756 Err(std::io::Error::other(
1757 "set_len() not supported for QcowFile",
1758 ))
1759 }
1760}
1761
1762impl DiskGetLen for QcowFile {
1763 fn get_len(&self) -> io::Result<u64> {
1764 Ok(self.virtual_size)
1765 }
1766}
1767
1768impl FileAllocate for QcowFile {
1769 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
1770 let mut inner = self.inner.lock();
1771 inner.write_cb(
1774 offset,
1775 len as usize,
1776 |_file, _offset, _raw_offset, _count| Ok(()),
1777 )?;
1778 Ok(())
1779 }
1780}
1781
1782impl PunchHole for QcowFile {
1783 fn punch_hole(&self, offset: u64, length: u64) -> std::io::Result<()> {
1784 let mut inner = self.inner.lock();
1785 let mut remaining = length;
1786 let mut offset = offset;
1787 while remaining > 0 {
1788 let chunk_length = min(remaining, usize::MAX as u64) as usize;
1789 inner.zero_bytes(offset, chunk_length)?;
1790 remaining -= chunk_length as u64;
1791 offset += chunk_length as u64;
1792 }
1793 Ok(())
1794 }
1795}
1796
1797impl WriteZeroesAt for QcowFile {
1798 fn write_zeroes_at(&self, offset: u64, length: usize) -> io::Result<usize> {
1799 self.punch_hole(offset, length as u64)?;
1800 Ok(length)
1801 }
1802}
1803
1804impl ToAsyncDisk for QcowFile {
1805 fn to_async_disk(self: Box<Self>, ex: &Executor) -> crate::Result<Box<dyn AsyncDisk>> {
1806 Ok(Box::new(AsyncDiskFileWrapper::new(*self, ex)))
1807 }
1808}
1809
1810fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
1812 if offset & ((0x01 << cluster_bits) - 1) != 0 {
1813 return Err(Error::InvalidOffset(offset));
1814 }
1815 Ok(())
1816}
1817
1818#[cfg(test)]
1819mod tests {
1820 use std::fs::OpenOptions;
1821 use std::io::Read;
1822 use std::io::Seek;
1823 use std::io::SeekFrom;
1824 use std::io::Write;
1825
1826 use tempfile::tempfile;
1827 use tempfile::TempDir;
1828
1829 use super::*;
1830
1831 fn valid_header() -> Vec<u8> {
1832 vec![
1833 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, ]
1852 }
1853
1854 fn test_huge_header() -> Vec<u8> {
1856 vec![
1857 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, ]
1876 }
1877
1878 fn test_params() -> DiskFileParams {
1879 DiskFileParams {
1880 path: PathBuf::from("/foo"),
1881 ..Default::default()
1882 }
1883 }
1884
1885 fn basic_file(header: &[u8]) -> File {
1886 let mut disk_file = tempfile().expect("failed to create temp file");
1887 disk_file.write_all(header).unwrap();
1888 disk_file.set_len(0x8000_0000).unwrap();
1889 disk_file.seek(SeekFrom::Start(0)).unwrap();
1890 disk_file
1891 }
1892
1893 fn with_basic_file<F>(header: &[u8], mut testfn: F)
1894 where
1895 F: FnMut(File),
1896 {
1897 testfn(basic_file(header)); }
1899
1900 fn with_default_file<F>(file_size: u64, mut testfn: F)
1901 where
1902 F: FnMut(QcowFile),
1903 {
1904 let file = tempfile().expect("failed to create temp file");
1905 let qcow_file = QcowFile::new(file, test_params(), file_size).unwrap();
1906
1907 testfn(qcow_file); }
1909
1910 fn write_all_at(qcow: &mut QcowFile, data: &[u8], offset: u64) -> std::io::Result<()> {
1912 let mut mem = data.to_owned();
1913 let vslice = VolatileSlice::new(&mut mem);
1914 qcow.write_all_at_volatile(vslice, offset)
1915 }
1916
1917 fn read_exact_at(qcow: &mut QcowFile, data: &mut [u8], offset: u64) -> std::io::Result<()> {
1919 let mut mem = data.to_owned();
1920 let vslice = VolatileSlice::new(&mut mem);
1921 qcow.read_exact_at_volatile(vslice, offset)?;
1922 vslice.copy_to(data);
1923 Ok(())
1924 }
1925
1926 #[test]
1927 fn default_header() {
1928 let header = QcowHeader::create_for_size_and_path(0x10_0000, None);
1929 let mut disk_file = tempfile().expect("failed to create temp file");
1930 header
1931 .expect("Failed to create header.")
1932 .write_to(&mut disk_file)
1933 .expect("Failed to write header to shm.");
1934 disk_file.seek(SeekFrom::Start(0)).unwrap();
1935 QcowFile::from(disk_file, test_params())
1936 .expect("Failed to create Qcow from default Header");
1937 }
1938
1939 #[test]
1940 fn header_read() {
1941 with_basic_file(&valid_header(), |mut disk_file: File| {
1942 QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
1943 });
1944 }
1945
1946 #[test]
1947 fn invalid_magic() {
1948 let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
1949 with_basic_file(&invalid_header, |mut disk_file: File| {
1950 QcowHeader::new(&mut disk_file).expect_err("Invalid header worked.");
1951 });
1952 }
1953
1954 #[test]
1955 fn invalid_refcount_order() {
1956 let mut header = valid_header();
1957 header[99] = 2;
1958 with_basic_file(&header, |disk_file: File| {
1959 QcowFile::from(disk_file, test_params()).expect_err("Invalid refcount order worked.");
1960 });
1961 }
1962
1963 #[test]
1964 fn invalid_cluster_bits() {
1965 let mut header = valid_header();
1966 header[23] = 3;
1967 with_basic_file(&header, |disk_file: File| {
1968 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
1969 });
1970 }
1971
1972 #[test]
1973 fn test_header_huge_file() {
1974 let header = test_huge_header();
1975 with_basic_file(&header, |disk_file: File| {
1976 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
1977 });
1978 }
1979
1980 #[test]
1981 fn test_header_excessive_file_size_rejected() {
1982 let mut header = valid_header();
1983 header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
1984 with_basic_file(&header, |disk_file: File| {
1985 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
1986 });
1987 }
1988
1989 #[test]
1990 fn test_huge_l1_table() {
1991 let mut header = valid_header();
1992 header[36] = 0x12;
1993 with_basic_file(&header, |disk_file: File| {
1994 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
1995 });
1996 }
1997
1998 #[test]
1999 fn test_header_1_tb_file_min_cluster() {
2000 let mut header = test_huge_header();
2001 header[24] = 0;
2002 header[26] = 1;
2003 header[31] = 0;
2004 with_basic_file(&header, |disk_file: File| {
2006 QcowFile::from(disk_file, test_params()).expect_err("Failed to create file.");
2007 });
2008 }
2009
2010 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2011 #[test]
2012 fn test_header_1_tb_file() {
2013 let mut header = test_huge_header();
2014 header[24] = 0;
2016 header[26] = 1;
2017 header[31] = 0;
2018 header[23] = 16;
2020 with_basic_file(&header, |disk_file: File| {
2021 let mut qcow =
2022 QcowFile::from(disk_file, test_params()).expect("Failed to create file.");
2023 let value = 0x0000_0040_3f00_ffffu64;
2024 write_all_at(&mut qcow, &value.to_le_bytes(), 0x100_0000_0000 - 8)
2025 .expect("failed to write data");
2026 });
2027 }
2028
2029 #[test]
2030 fn test_header_huge_num_refcounts() {
2031 let mut header = valid_header();
2032 header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
2033 with_basic_file(&header, |disk_file: File| {
2034 QcowFile::from(disk_file, test_params())
2035 .expect_err("Created disk with excessive refcount clusters");
2036 });
2037 }
2038
2039 #[test]
2040 fn test_header_huge_refcount_offset() {
2041 let mut header = valid_header();
2042 header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
2043 with_basic_file(&header, |disk_file: File| {
2044 QcowFile::from(disk_file, test_params())
2045 .expect_err("Created disk with excessive refcount offset");
2046 });
2047 }
2048
2049 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2050 #[test]
2051 fn write_read_start() {
2052 with_basic_file(&valid_header(), |disk_file: File| {
2053 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2054 write_all_at(&mut q, b"test first bytes", 0).expect("Failed to write test string.");
2055 let mut buf = [0u8; 4];
2056 read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
2057 assert_eq!(&buf, b"test");
2058 });
2059 }
2060
2061 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2062 #[test]
2063 fn write_read_start_backing() {
2064 let disk_file = basic_file(&valid_header());
2065 let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2066 write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
2067 let mut buf = [0u8; 4];
2068 let wrapping_disk_file = basic_file(&valid_header());
2069 let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2070 wrapping.set_backing_file(Some(Box::new(backing)));
2071 read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
2072 assert_eq!(&buf, b"test");
2073 }
2074
2075 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2076 #[test]
2077 fn write_read_start_backing_overlap() {
2078 let disk_file = basic_file(&valid_header());
2079 let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2080 write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
2081 let wrapping_disk_file = basic_file(&valid_header());
2082 let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2083 wrapping.set_backing_file(Some(Box::new(backing)));
2084 write_all_at(&mut wrapping, b"TEST", 0).expect("Failed to write second test string.");
2085 let mut buf = [0u8; 10];
2086 read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
2087 assert_eq!(&buf, b"TEST first");
2088 }
2089
2090 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2091 #[test]
2092 fn offset_write_read() {
2093 with_basic_file(&valid_header(), |disk_file: File| {
2094 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2095 let b = [0x55u8; 0x1000];
2096 write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
2097 let mut buf = [0u8; 4];
2098 read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
2099 assert_eq!(buf[0], 0x55);
2100 });
2101 }
2102
2103 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2104 #[test]
2105 fn write_zeroes_read() {
2106 with_basic_file(&valid_header(), |disk_file: File| {
2107 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2108 let b = [0x55u8; 0x1000];
2110 write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
2111 q.write_zeroes_all_at(0xfff2000, 0x200)
2113 .expect("Failed to write zeroes.");
2114 let mut buf = [0u8; 0x1000];
2116 read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
2117 assert_eq!(buf[0], 0);
2118 assert_eq!(buf[0x1FF], 0);
2119 assert_eq!(buf[0x200], 0x55);
2120 assert_eq!(buf[0xFFF], 0x55);
2121 });
2122 }
2123
2124 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2125 #[test]
2126 fn write_zeroes_full_cluster() {
2127 const CHUNK_SIZE: usize = 4096 * 2 + 512;
2130 with_basic_file(&valid_header(), |disk_file: File| {
2131 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2132 let b = [0x55u8; CHUNK_SIZE];
2134 write_all_at(&mut q, &b, 0).expect("Failed to write test string.");
2135 q.write_zeroes_all_at(0, CHUNK_SIZE)
2137 .expect("Failed to write zeroes.");
2138 let mut buf = [0u8; CHUNK_SIZE];
2140 read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
2141 assert_eq!(buf[0], 0);
2142 assert_eq!(buf[CHUNK_SIZE - 1], 0);
2143 });
2144 }
2145
2146 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2147 #[test]
2148 fn write_zeroes_backing() {
2149 let disk_file = basic_file(&valid_header());
2150 let mut backing = QcowFile::from(disk_file, test_params()).unwrap();
2151 let b = [0x55u8; 0x1000];
2153 write_all_at(&mut backing, &b, 0xfff2000).expect("Failed to write test string.");
2154 let wrapping_disk_file = basic_file(&valid_header());
2155 let mut wrapping = QcowFile::from(wrapping_disk_file, test_params()).unwrap();
2156 wrapping.set_backing_file(Some(Box::new(backing)));
2157 wrapping
2160 .write_zeroes_all_at(0xfff2000, 0x200)
2161 .expect("Failed to write zeroes.");
2162 let mut buf = [0u8; 0x1000];
2164 read_exact_at(&mut wrapping, &mut buf, 0xfff2000).expect("Failed to read.");
2165 assert_eq!(buf[0], 0);
2166 assert_eq!(buf[0x1FF], 0);
2167 assert_eq!(buf[0x200], 0x55);
2168 assert_eq!(buf[0xFFF], 0x55);
2169 }
2170 #[test]
2171 fn test_header() {
2172 with_basic_file(&valid_header(), |disk_file: File| {
2173 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2174 assert_eq!(q.inner.get_mut().virtual_size(), 0x20_0000_0000);
2175 });
2176 }
2177
2178 #[test]
2179 fn read_small_buffer() {
2180 with_basic_file(&valid_header(), |disk_file: File| {
2181 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2182 let mut b = [5u8; 16];
2183 read_exact_at(&mut q, &mut b, 1000).expect("Failed to read.");
2184 assert_eq!(0, b[0]);
2185 assert_eq!(0, b[15]);
2186 });
2187 }
2188
2189 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2190 #[test]
2191 fn replay_ext4() {
2192 with_basic_file(&valid_header(), |disk_file: File| {
2193 let mut q = QcowFile::from(disk_file, test_params()).unwrap();
2194 const BUF_SIZE: usize = 0x1000;
2195 let mut b = [0u8; BUF_SIZE];
2196
2197 struct Transfer {
2198 pub write: bool,
2199 pub addr: u64,
2200 }
2201
2202 let xfers: Vec<Transfer> = vec![
2204 Transfer {
2205 write: false,
2206 addr: 0xfff0000,
2207 },
2208 Transfer {
2209 write: false,
2210 addr: 0xfffe000,
2211 },
2212 Transfer {
2213 write: false,
2214 addr: 0x0,
2215 },
2216 Transfer {
2217 write: false,
2218 addr: 0x1000,
2219 },
2220 Transfer {
2221 write: false,
2222 addr: 0xffff000,
2223 },
2224 Transfer {
2225 write: false,
2226 addr: 0xffdf000,
2227 },
2228 Transfer {
2229 write: false,
2230 addr: 0xfff8000,
2231 },
2232 Transfer {
2233 write: false,
2234 addr: 0xffe0000,
2235 },
2236 Transfer {
2237 write: false,
2238 addr: 0xffce000,
2239 },
2240 Transfer {
2241 write: false,
2242 addr: 0xffb6000,
2243 },
2244 Transfer {
2245 write: false,
2246 addr: 0xffab000,
2247 },
2248 Transfer {
2249 write: false,
2250 addr: 0xffa4000,
2251 },
2252 Transfer {
2253 write: false,
2254 addr: 0xff8e000,
2255 },
2256 Transfer {
2257 write: false,
2258 addr: 0xff86000,
2259 },
2260 Transfer {
2261 write: false,
2262 addr: 0xff84000,
2263 },
2264 Transfer {
2265 write: false,
2266 addr: 0xff89000,
2267 },
2268 Transfer {
2269 write: false,
2270 addr: 0xfe7e000,
2271 },
2272 Transfer {
2273 write: false,
2274 addr: 0x100000,
2275 },
2276 Transfer {
2277 write: false,
2278 addr: 0x3000,
2279 },
2280 Transfer {
2281 write: false,
2282 addr: 0x7000,
2283 },
2284 Transfer {
2285 write: false,
2286 addr: 0xf000,
2287 },
2288 Transfer {
2289 write: false,
2290 addr: 0x2000,
2291 },
2292 Transfer {
2293 write: false,
2294 addr: 0x4000,
2295 },
2296 Transfer {
2297 write: false,
2298 addr: 0x5000,
2299 },
2300 Transfer {
2301 write: false,
2302 addr: 0x6000,
2303 },
2304 Transfer {
2305 write: false,
2306 addr: 0x8000,
2307 },
2308 Transfer {
2309 write: false,
2310 addr: 0x9000,
2311 },
2312 Transfer {
2313 write: false,
2314 addr: 0xa000,
2315 },
2316 Transfer {
2317 write: false,
2318 addr: 0xb000,
2319 },
2320 Transfer {
2321 write: false,
2322 addr: 0xc000,
2323 },
2324 Transfer {
2325 write: false,
2326 addr: 0xd000,
2327 },
2328 Transfer {
2329 write: false,
2330 addr: 0xe000,
2331 },
2332 Transfer {
2333 write: false,
2334 addr: 0x10000,
2335 },
2336 Transfer {
2337 write: false,
2338 addr: 0x11000,
2339 },
2340 Transfer {
2341 write: false,
2342 addr: 0x12000,
2343 },
2344 Transfer {
2345 write: false,
2346 addr: 0x13000,
2347 },
2348 Transfer {
2349 write: false,
2350 addr: 0x14000,
2351 },
2352 Transfer {
2353 write: false,
2354 addr: 0x15000,
2355 },
2356 Transfer {
2357 write: false,
2358 addr: 0x16000,
2359 },
2360 Transfer {
2361 write: false,
2362 addr: 0x17000,
2363 },
2364 Transfer {
2365 write: false,
2366 addr: 0x18000,
2367 },
2368 Transfer {
2369 write: false,
2370 addr: 0x19000,
2371 },
2372 Transfer {
2373 write: false,
2374 addr: 0x1a000,
2375 },
2376 Transfer {
2377 write: false,
2378 addr: 0x1b000,
2379 },
2380 Transfer {
2381 write: false,
2382 addr: 0x1c000,
2383 },
2384 Transfer {
2385 write: false,
2386 addr: 0x1d000,
2387 },
2388 Transfer {
2389 write: false,
2390 addr: 0x1e000,
2391 },
2392 Transfer {
2393 write: false,
2394 addr: 0x1f000,
2395 },
2396 Transfer {
2397 write: false,
2398 addr: 0x21000,
2399 },
2400 Transfer {
2401 write: false,
2402 addr: 0x22000,
2403 },
2404 Transfer {
2405 write: false,
2406 addr: 0x24000,
2407 },
2408 Transfer {
2409 write: false,
2410 addr: 0x40000,
2411 },
2412 Transfer {
2413 write: false,
2414 addr: 0x0,
2415 },
2416 Transfer {
2417 write: false,
2418 addr: 0x3000,
2419 },
2420 Transfer {
2421 write: false,
2422 addr: 0x7000,
2423 },
2424 Transfer {
2425 write: false,
2426 addr: 0x0,
2427 },
2428 Transfer {
2429 write: false,
2430 addr: 0x1000,
2431 },
2432 Transfer {
2433 write: false,
2434 addr: 0x2000,
2435 },
2436 Transfer {
2437 write: false,
2438 addr: 0x3000,
2439 },
2440 Transfer {
2441 write: false,
2442 addr: 0x0,
2443 },
2444 Transfer {
2445 write: false,
2446 addr: 0x449000,
2447 },
2448 Transfer {
2449 write: false,
2450 addr: 0x48000,
2451 },
2452 Transfer {
2453 write: false,
2454 addr: 0x48000,
2455 },
2456 Transfer {
2457 write: false,
2458 addr: 0x448000,
2459 },
2460 Transfer {
2461 write: false,
2462 addr: 0x44a000,
2463 },
2464 Transfer {
2465 write: false,
2466 addr: 0x48000,
2467 },
2468 Transfer {
2469 write: false,
2470 addr: 0x48000,
2471 },
2472 Transfer {
2473 write: true,
2474 addr: 0x0,
2475 },
2476 Transfer {
2477 write: true,
2478 addr: 0x448000,
2479 },
2480 Transfer {
2481 write: true,
2482 addr: 0x449000,
2483 },
2484 Transfer {
2485 write: true,
2486 addr: 0x44a000,
2487 },
2488 Transfer {
2489 write: true,
2490 addr: 0xfff0000,
2491 },
2492 Transfer {
2493 write: true,
2494 addr: 0xfff1000,
2495 },
2496 Transfer {
2497 write: true,
2498 addr: 0xfff2000,
2499 },
2500 Transfer {
2501 write: true,
2502 addr: 0xfff3000,
2503 },
2504 Transfer {
2505 write: true,
2506 addr: 0xfff4000,
2507 },
2508 Transfer {
2509 write: true,
2510 addr: 0xfff5000,
2511 },
2512 Transfer {
2513 write: true,
2514 addr: 0xfff6000,
2515 },
2516 Transfer {
2517 write: true,
2518 addr: 0xfff7000,
2519 },
2520 Transfer {
2521 write: true,
2522 addr: 0xfff8000,
2523 },
2524 Transfer {
2525 write: true,
2526 addr: 0xfff9000,
2527 },
2528 Transfer {
2529 write: true,
2530 addr: 0xfffa000,
2531 },
2532 Transfer {
2533 write: true,
2534 addr: 0xfffb000,
2535 },
2536 Transfer {
2537 write: true,
2538 addr: 0xfffc000,
2539 },
2540 Transfer {
2541 write: true,
2542 addr: 0xfffd000,
2543 },
2544 Transfer {
2545 write: true,
2546 addr: 0xfffe000,
2547 },
2548 Transfer {
2549 write: true,
2550 addr: 0xffff000,
2551 },
2552 ];
2553
2554 for xfer in &xfers {
2555 if xfer.write {
2556 write_all_at(&mut q, &b, xfer.addr).expect("Failed to write.");
2557 } else {
2558 read_exact_at(&mut q, &mut b, xfer.addr).expect("Failed to read.");
2559 }
2560 }
2561 });
2562 }
2563
2564 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2565 #[test]
2566 fn combo_write_read() {
2567 with_default_file(1024 * 1024 * 1024 * 256, |mut qcow_file| {
2568 const NUM_BLOCKS: usize = 55;
2569 const BLOCK_SIZE: usize = 0x1_0000;
2570 const OFFSET: u64 = 0x1_0000_0020;
2571 let data = [0x55u8; BLOCK_SIZE];
2572 let mut readback = [0u8; BLOCK_SIZE];
2573 for i in 0..NUM_BLOCKS {
2574 let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2575 write_all_at(&mut qcow_file, &data, seek_offset)
2576 .expect("Failed to write test data.");
2577 read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2579 for (orig, read) in data.iter().zip(readback.iter()) {
2580 assert_eq!(orig, read);
2581 }
2582 }
2583 read_exact_at(&mut qcow_file, &mut readback, 0).expect("Failed to read.");
2585 for read in readback.iter() {
2586 assert_eq!(*read, 0);
2587 }
2588 for i in 0..NUM_BLOCKS {
2590 let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2591 read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2592 for (orig, read) in data.iter().zip(readback.iter()) {
2593 assert_eq!(orig, read);
2594 }
2595 }
2596
2597 assert_eq!(
2598 qcow_file.inner.get_mut().first_zero_refcount().unwrap(),
2599 None
2600 );
2601 });
2602 }
2603
2604 #[test]
2605 fn rebuild_refcounts() {
2606 with_basic_file(&valid_header(), |mut disk_file: File| {
2607 let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
2608 let cluster_size = 65536;
2609 let mut raw_file =
2610 QcowRawFile::from(disk_file, cluster_size).expect("Failed to create QcowRawFile.");
2611 QcowFileInner::rebuild_refcounts(&mut raw_file, header)
2612 .expect("Failed to rebuild recounts.");
2613 });
2614 }
2615
2616 #[cfg_attr(windows, ignore = "TODO(b/257958782): Enable large test on windows")]
2617 #[test]
2618 fn nested_qcow() {
2619 let tmp_dir = TempDir::new().unwrap();
2620
2621 let backing_file_path = tmp_dir.path().join("backing");
2624 let _backing_file = OpenOptions::new()
2625 .read(true)
2626 .write(true)
2627 .create_new(true)
2628 .open(&backing_file_path)
2629 .unwrap();
2630
2631 let level1_qcow_file_path = tmp_dir.path().join("qcow.l1");
2632 let level1_qcow_file = OpenOptions::new()
2633 .read(true)
2634 .write(true)
2635 .create_new(true)
2636 .open(&level1_qcow_file_path)
2637 .unwrap();
2638 let _level1_qcow_file = QcowFile::new_from_backing(
2639 level1_qcow_file,
2640 test_params(),
2641 backing_file_path.to_str().unwrap(),
2642 )
2643 .unwrap();
2644
2645 let level2_qcow_file = tempfile().unwrap();
2646 let _level2_qcow_file = QcowFile::new_from_backing(
2647 level2_qcow_file,
2648 test_params(),
2649 level1_qcow_file_path.to_str().unwrap(),
2650 )
2651 .expect("failed to create level2 qcow file");
2652 }
2653
2654 #[test]
2655 fn io_seek() {
2656 with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2657 assert_eq!(qcow_file.stream_position().unwrap(), 0);
2659
2660 assert_eq!(
2662 qcow_file.seek(SeekFrom::Start(1024 * 1024)).unwrap(),
2663 1024 * 1024
2664 );
2665
2666 qcow_file
2669 .seek(SeekFrom::Current(-(1024 * 1024 + 1)))
2670 .expect_err("negative offset seek should fail");
2671 assert_eq!(qcow_file.stream_position().unwrap(), 1024 * 1024);
2672
2673 assert_eq!(
2675 qcow_file.seek(SeekFrom::End(-1)).unwrap(),
2676 1024 * 1024 * 10 - 1
2677 );
2678
2679 assert_eq!(qcow_file.seek(SeekFrom::End(0)).unwrap(), 1024 * 1024 * 10);
2681
2682 qcow_file
2684 .seek(SeekFrom::End(1))
2685 .expect_err("seek past EOF should fail");
2686 });
2687 }
2688
2689 #[test]
2690 fn io_write_read() {
2691 with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2692 const BLOCK_SIZE: usize = 0x1_0000;
2693 let data_55 = [0x55u8; BLOCK_SIZE];
2694 let data_aa = [0xaau8; BLOCK_SIZE];
2695 let mut readback = [0u8; BLOCK_SIZE];
2696
2697 qcow_file.write_all(&data_55).unwrap();
2698 assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64);
2699
2700 qcow_file.write_all(&data_aa).unwrap();
2701 assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64 * 2);
2702
2703 assert_eq!(
2705 qcow_file
2706 .seek(SeekFrom::Current(-(BLOCK_SIZE as i64)))
2707 .unwrap(),
2708 BLOCK_SIZE as u64
2709 );
2710 qcow_file.read_exact(&mut readback).unwrap();
2711 assert_eq!(qcow_file.stream_position().unwrap(), BLOCK_SIZE as u64 * 2);
2712 for (orig, read) in data_aa.iter().zip(readback.iter()) {
2713 assert_eq!(orig, read);
2714 }
2715
2716 qcow_file.rewind().unwrap();
2718 qcow_file.read_exact(&mut readback).unwrap();
2719 for (orig, read) in data_55.iter().zip(readback.iter()) {
2720 assert_eq!(orig, read);
2721 }
2722
2723 qcow_file
2725 .seek(SeekFrom::Start(BLOCK_SIZE as u64 / 2))
2726 .unwrap();
2727 qcow_file.read_exact(&mut readback).unwrap();
2728 for (orig, read) in data_55[BLOCK_SIZE / 2..]
2729 .iter()
2730 .chain(data_aa[..BLOCK_SIZE / 2].iter())
2731 .zip(readback.iter())
2732 {
2733 assert_eq!(orig, read);
2734 }
2735 });
2736 }
2737
2738 #[test]
2739 fn header_with_backing_format() {
2740 let header = QcowHeader::create_for_size_and_path(
2741 0x10_0000,
2742 Some(("/my/path/to/a/file", crate::ImageType::Raw)),
2743 )
2744 .expect("Failed to create header.");
2745 let mut disk_file = tempfile().expect("failed to create temp file");
2746 header
2747 .write_to(&mut disk_file)
2748 .expect("Failed to write header.");
2749 disk_file.seek(SeekFrom::Start(0)).unwrap();
2750 let read_header = QcowHeader::new(&mut disk_file).expect("Failed to create header.");
2751 assert_eq!(
2752 header.backing_file_path,
2753 Some(String::from("/my/path/to/a/file"))
2754 );
2755 assert_eq!(read_header.backing_file_path, header.backing_file_path);
2756 assert_eq!(read_header.backing_file_format, Some(crate::ImageType::Raw));
2757 }
2758
2759 #[test]
2760 fn test_backing_format_respected() {
2761 let temp_dir = TempDir::new().expect("failed to create temp dir");
2762 let backing_path = temp_dir.path().join("backing.raw");
2763 let parent_path = temp_dir.path().join("parent.qcow2");
2764
2765 let mut backing_file = File::create(&backing_path).unwrap();
2767 backing_file.write_all(&QCOW_MAGIC.to_be_bytes()).unwrap();
2768 backing_file.set_len(1024 * 1024).unwrap();
2769 drop(backing_file);
2770
2771 let header_raw = QcowHeader::create_for_size_and_path(
2773 1024 * 1024 * 10,
2774 Some((backing_path.to_str().unwrap(), crate::ImageType::Raw)),
2775 )
2776 .expect("Failed to create header.");
2777
2778 let mut parent_file = File::create(&parent_path).unwrap();
2779 header_raw.write_to(&mut parent_file).unwrap();
2780 drop(parent_file);
2781
2782 let parent_file = OpenOptions::new()
2784 .read(true)
2785 .write(true)
2786 .open(&parent_path)
2787 .unwrap();
2788 let mut params = test_params();
2789 params.path = parent_path.clone();
2790 let qcow = QcowFile::from(parent_file, params);
2791 assert!(
2792 qcow.is_ok(),
2793 "Failed to open with explicit raw backing: {:?}",
2794 qcow.err()
2795 );
2796
2797 let mut header_auto = QcowHeader::create_for_size_and_path(
2799 1024 * 1024 * 10,
2800 Some((backing_path.to_str().unwrap(), crate::ImageType::Raw)),
2801 )
2802 .expect("Failed to create header.");
2803 header_auto.backing_file_format = None;
2804 header_auto.backing_file_offset = V3_BARE_HEADER_SIZE as u64 + 8;
2805
2806 let mut parent_file_auto = File::create(&parent_path).unwrap();
2807 header_auto.write_to(&mut parent_file_auto).unwrap();
2808 drop(parent_file_auto);
2809
2810 let parent_file_auto = OpenOptions::new()
2812 .read(true)
2813 .write(true)
2814 .open(&parent_path)
2815 .unwrap();
2816 let mut params = test_params();
2817 params.path = parent_path.clone();
2818 let qcow_auto = QcowFile::from(parent_file_auto, params);
2819 assert!(
2820 matches!(
2821 qcow_auto.as_ref().err(),
2822 Some(&Error::MissingBackingFileFormat)
2823 ),
2824 "Expected Error::MissingBackingFileFormat, got: {qcow_auto:?}"
2825 );
2826 }
2827
2828 #[test]
2829 fn test_unsupported_backing_format() {
2830 let header_err = QcowHeader::create_for_size_and_path(
2832 1024 * 1024 * 10,
2833 Some(("backing.img", crate::ImageType::AndroidSparse)),
2834 );
2835 assert!(
2836 matches!(
2837 header_err.as_ref().err(),
2838 Some(&Error::UnsupportedBackingFileFormat(_))
2839 ),
2840 "Expected Error::UnsupportedBackingFileFormat, got: {header_err:?}"
2841 );
2842
2843 let header = QcowHeader::create_for_size_and_path(
2845 1024 * 1024 * 10,
2846 Some(("backing.raw", crate::ImageType::Raw)),
2847 )
2848 .unwrap();
2849
2850 let mut file = tempfile().unwrap();
2851 header.write_to(&mut file).unwrap();
2852
2853 file.seek(SeekFrom::Start(108)).unwrap();
2855 file.write_all(&7u32.to_be_bytes()).unwrap(); file.write_all(b"invalid\0").unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
2860 let read_result = QcowHeader::new(&mut file);
2861 match &read_result {
2862 Err(Error::UnsupportedBackingFileFormat(fmt)) => {
2863 assert_eq!(fmt, "invalid");
2864 }
2865 other => {
2866 panic!("Expected Error::UnsupportedBackingFileFormat(\"invalid\"), got: {other:?}")
2867 }
2868 }
2869 }
2870
2871 #[test]
2872 fn test_create_excessive_size_rejected() {
2873 let header = QcowHeader::create_for_size_and_path(MAX_QCOW_FILE_SIZE + 1, None);
2874 assert!(matches!(header.err(), Some(Error::FileTooBig(_))));
2875 }
2876
2877 #[cfg(feature = "composite-disk")]
2878 #[test]
2879 fn test_composite_backing() {
2880 use protobuf::Message;
2881 use protos::cdisk_spec;
2882
2883 use crate::composite::CDISK_MAGIC;
2884
2885 let temp_dir = TempDir::new().expect("failed to create temp dir");
2886 let component_path = temp_dir.path().join("component.raw");
2887 let spec_path = temp_dir.path().join("backing.cdisk");
2888 let parent_path = temp_dir.path().join("parent.qcow2");
2889
2890 let component_file = File::create(&component_path).unwrap();
2892 component_file.set_len(1024 * 1024).unwrap(); drop(component_file);
2894
2895 let mut proto = cdisk_spec::CompositeDisk::new();
2897 proto.version = 2;
2898 proto.length = 1024 * 1024;
2899 let mut component = cdisk_spec::ComponentDisk::new();
2900 component.file_path = String::from("component.raw");
2902 component.offset = 0;
2903 component.file_offset = 0;
2904 component.read_write_capability = cdisk_spec::ReadWriteCapability::READ_WRITE.into();
2905 proto.component_disks.push(component);
2906
2907 let mut spec_file = File::create(&spec_path).unwrap();
2908 spec_file.write_all(CDISK_MAGIC.as_bytes()).unwrap();
2909 proto.write_to_writer(&mut spec_file).unwrap();
2910 drop(spec_file);
2911
2912 let header = QcowHeader::create_for_size_and_path(
2914 1024 * 1024 * 10,
2915 Some((spec_path.to_str().unwrap(), crate::ImageType::CompositeDisk)),
2916 )
2917 .expect("Failed to create header.");
2918
2919 let mut parent_file = File::create(&parent_path).unwrap();
2920 header.write_to(&mut parent_file).unwrap();
2921 drop(parent_file);
2922
2923 let parent_file = OpenOptions::new()
2925 .read(true)
2926 .write(true)
2927 .open(&parent_path)
2928 .unwrap();
2929 let mut params = test_params();
2930 params.path = parent_path.clone();
2931 let qcow = QcowFile::from(parent_file, params);
2932 assert!(
2933 qcow.is_ok(),
2934 "Failed to open with composite backing: {:?}",
2935 qcow.err()
2936 );
2937 }
2938
2939 #[test]
2940 fn test_backing_file_escapes_first_cluster() {
2941 let mut header = QcowHeader::create_for_size_and_path(
2943 1024 * 1024 * 10,
2944 Some(("backing.raw", crate::ImageType::Raw)),
2945 )
2946 .unwrap();
2947
2948 header.backing_file_offset = 70000;
2951
2952 let mut file = tempfile().unwrap();
2953 header.write_to(&mut file).unwrap();
2954
2955 let read_result = QcowHeader::new(&mut file);
2957 match &read_result {
2958 Err(Error::BackingFileEscapesFirstCluster) => {}
2959 other => panic!("Expected Error::BackingFileEscapesFirstCluster, got: {other:?}"),
2960 }
2961 }
2962}