1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 mod qcow_raw_file;
6 mod refcount;
7 mod vec_cache;
8
9 use base::{
10 error, open_file, AsRawDescriptor, AsRawDescriptors, FileAllocate, FileReadWriteAtVolatile,
11 FileSetLen, FileSync, PunchHole, RawDescriptor, WriteZeroesAt,
12 };
13 use data_model::{VolatileMemory, VolatileSlice};
14 use libc::{EINVAL, ENOSPC, ENOTSUP};
15 use remain::sorted;
16 use thiserror::Error;
17
18 use std::cmp::{max, min};
19 use std::fs::{File, OpenOptions};
20 use std::io::{self, Read, Seek, SeekFrom, Write};
21 use std::mem::size_of;
22 use std::path::Path;
23 use std::str;
24
25 use crate::qcow::qcow_raw_file::QcowRawFile;
26 use crate::qcow::refcount::RefCount;
27 use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
28 use crate::{create_disk_file, DiskFile, DiskGetLen};
29
30 #[sorted]
31 #[derive(Error, Debug)]
32 pub enum Error {
33 #[error("backing file io error: {0}")]
34 BackingFileIo(io::Error),
35 #[error("backing file open error: {0}")]
36 BackingFileOpen(Box<crate::Error>),
37 #[error("backing file name is too long: {0} bytes over")]
38 BackingFileTooLong(usize),
39 #[error("compressed blocks not supported")]
40 CompressedBlocksNotSupported,
41 #[error("failed to evict cache: {0}")]
42 EvictingCache(io::Error),
43 #[error("file larger than max of {}: {0}", MAX_QCOW_FILE_SIZE)]
44 FileTooBig(u64),
45 #[error("failed to get file size: {0}")]
46 GettingFileSize(io::Error),
47 #[error("failed to get refcount: {0}")]
48 GettingRefcount(refcount::Error),
49 #[error("failed to parse filename: {0}")]
50 InvalidBackingFileName(str::Utf8Error),
51 #[error("invalid cluster index")]
52 InvalidClusterIndex,
53 #[error("invalid cluster size")]
54 InvalidClusterSize,
55 #[error("invalid index")]
56 InvalidIndex,
57 #[error("invalid L1 table offset")]
58 InvalidL1TableOffset,
59 #[error("invalid L1 table size {0}")]
60 InvalidL1TableSize(u32),
61 #[error("invalid magic")]
62 InvalidMagic,
63 #[error("invalid offset")]
64 InvalidOffset(u64),
65 #[error("invalid refcount table offset")]
66 InvalidRefcountTableOffset,
67 #[error("invalid refcount table size: {0}")]
68 InvalidRefcountTableSize(u64),
69 #[error("no free clusters")]
70 NoFreeClusters,
71 #[error("no refcount clusters")]
72 NoRefcountClusters,
73 #[error("not enough space for refcounts")]
74 NotEnoughSpaceForRefcounts,
75 #[error("failed to open file: {0}")]
76 OpeningFile(io::Error),
77 #[error("failed to open file: {0}")]
78 ReadingHeader(io::Error),
79 #[error("failed to read pointers: {0}")]
80 ReadingPointers(io::Error),
81 #[error("failed to read ref count block: {0}")]
82 ReadingRefCountBlock(refcount::Error),
83 #[error("failed to read ref counts: {0}")]
84 ReadingRefCounts(io::Error),
85 #[error("failed to rebuild ref counts: {0}")]
86 RebuildingRefCounts(io::Error),
87 #[error("refcount table offset past file end")]
88 RefcountTableOffEnd,
89 #[error("too many clusters specified for refcount table")]
90 RefcountTableTooLarge,
91 #[error("failed to seek file: {0}")]
92 SeekingFile(io::Error),
93 #[error("failed to set refcount refcount: {0}")]
94 SettingRefcountRefcount(io::Error),
95 #[error("size too small for number of clusters")]
96 SizeTooSmallForNumberOfClusters,
97 #[error("l1 entry table too large: {0}")]
98 TooManyL1Entries(u64),
99 #[error("ref count table too large: {0}")]
100 TooManyRefcounts(u64),
101 #[error("unsupported refcount order")]
102 UnsupportedRefcountOrder,
103 #[error("unsupported version: {0}")]
104 UnsupportedVersion(u32),
105 #[error("failed to write header: {0}")]
106 WritingHeader(io::Error),
107 }
108
109 pub type Result<T> = std::result::Result<T, Error>;
110
111 // Maximum data size supported.
112 const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; // 16 TB.
113
114 // QCOW magic constant that starts the header.
115 pub const QCOW_MAGIC: u32 = 0x5146_49fb;
116 // Default to a cluster size of 2^DEFAULT_CLUSTER_BITS
117 const DEFAULT_CLUSTER_BITS: u32 = 16;
118 // Limit clusters to reasonable sizes. Choose the same limits as qemu. Making the clusters smaller
119 // increases the amount of overhead for book keeping.
120 const MIN_CLUSTER_BITS: u32 = 9;
121 const MAX_CLUSTER_BITS: u32 = 21;
122 // The L1 and RefCount table are kept in RAM, only handle files that require less than 35M entries.
123 // This easily covers 1 TB files. When support for bigger files is needed the assumptions made to
124 // keep these tables in RAM needs to be thrown out.
125 const MAX_RAM_POINTER_TABLE_SIZE: u64 = 35_000_000;
126 // Only support 2 byte refcounts, 2^refcount_order bits.
127 const DEFAULT_REFCOUNT_ORDER: u32 = 4;
128
129 const V3_BARE_HEADER_SIZE: u32 = 104;
130
131 // bits 0-8 and 56-63 are reserved.
132 const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
133 const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
134 // Flags
135 const COMPRESSED_FLAG: u64 = 1 << 62;
136 const CLUSTER_USED_FLAG: u64 = 1 << 63;
137 const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1 << 0;
138
139 // The format supports a "header extension area", that crosvm does not use.
140 const QCOW_EMPTY_HEADER_EXTENSION_SIZE: u32 = 8;
141
142 // Defined by the specification
143 const MAX_BACKING_FILE_SIZE: u32 = 1023;
144
145 /// Contains the information from the header of a qcow file.
146 #[derive(Clone, Debug)]
147 pub struct QcowHeader {
148 pub magic: u32,
149 pub version: u32,
150
151 pub backing_file_offset: u64,
152 pub backing_file_size: u32,
153
154 pub cluster_bits: u32,
155 pub size: u64,
156 pub crypt_method: u32,
157
158 pub l1_size: u32,
159 pub l1_table_offset: u64,
160
161 pub refcount_table_offset: u64,
162 pub refcount_table_clusters: u32,
163
164 pub nb_snapshots: u32,
165 pub snapshots_offset: u64,
166
167 // v3 entries
168 pub incompatible_features: u64,
169 pub compatible_features: u64,
170 pub autoclear_features: u64,
171 pub refcount_order: u32,
172 pub header_size: u32,
173
174 // Post-header entries
175 pub backing_file_path: Option<String>,
176 }
177
178 // Reads the next u16 from the file.
read_u16_from_file(mut f: &File) -> Result<u16>179 fn read_u16_from_file(mut f: &File) -> Result<u16> {
180 let mut value = [0u8; 2];
181 (&mut f)
182 .read_exact(&mut value)
183 .map_err(Error::ReadingHeader)?;
184 Ok(u16::from_be_bytes(value))
185 }
186
187 // Reads the next u32 from the file.
read_u32_from_file(mut f: &File) -> Result<u32>188 fn read_u32_from_file(mut f: &File) -> Result<u32> {
189 let mut value = [0u8; 4];
190 (&mut f)
191 .read_exact(&mut value)
192 .map_err(Error::ReadingHeader)?;
193 Ok(u32::from_be_bytes(value))
194 }
195
196 // Reads the next u64 from the file.
read_u64_from_file(mut f: &File) -> Result<u64>197 fn read_u64_from_file(mut f: &File) -> Result<u64> {
198 let mut value = [0u8; 8];
199 (&mut f)
200 .read_exact(&mut value)
201 .map_err(Error::ReadingHeader)?;
202 Ok(u64::from_be_bytes(value))
203 }
204
205 impl QcowHeader {
206 /// Creates a QcowHeader from a reference to a file.
new(f: &mut File) -> Result<QcowHeader>207 pub fn new(f: &mut File) -> Result<QcowHeader> {
208 f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
209
210 let magic = read_u32_from_file(f)?;
211 if magic != QCOW_MAGIC {
212 return Err(Error::InvalidMagic);
213 }
214
215 let mut header = QcowHeader {
216 magic,
217 version: read_u32_from_file(f)?,
218 backing_file_offset: read_u64_from_file(f)?,
219 backing_file_size: read_u32_from_file(f)?,
220 cluster_bits: read_u32_from_file(f)?,
221 size: read_u64_from_file(f)?,
222 crypt_method: read_u32_from_file(f)?,
223 l1_size: read_u32_from_file(f)?,
224 l1_table_offset: read_u64_from_file(f)?,
225 refcount_table_offset: read_u64_from_file(f)?,
226 refcount_table_clusters: read_u32_from_file(f)?,
227 nb_snapshots: read_u32_from_file(f)?,
228 snapshots_offset: read_u64_from_file(f)?,
229 incompatible_features: read_u64_from_file(f)?,
230 compatible_features: read_u64_from_file(f)?,
231 autoclear_features: read_u64_from_file(f)?,
232 refcount_order: read_u32_from_file(f)?,
233 header_size: read_u32_from_file(f)?,
234 backing_file_path: None,
235 };
236 if header.backing_file_size > MAX_BACKING_FILE_SIZE {
237 return Err(Error::BackingFileTooLong(header.backing_file_size as usize));
238 }
239 if header.backing_file_offset != 0 {
240 f.seek(SeekFrom::Start(header.backing_file_offset))
241 .map_err(Error::ReadingHeader)?;
242 let mut backing_file_name_bytes = vec![0u8; header.backing_file_size as usize];
243 f.read_exact(&mut backing_file_name_bytes)
244 .map_err(Error::ReadingHeader)?;
245 header.backing_file_path = Some(
246 String::from_utf8(backing_file_name_bytes)
247 .map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?,
248 );
249 }
250 Ok(header)
251 }
252
create_for_size_and_path(size: u64, backing_file: Option<&str>) -> Result<QcowHeader>253 pub fn create_for_size_and_path(size: u64, backing_file: Option<&str>) -> Result<QcowHeader> {
254 let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
255 let cluster_size: u32 = 0x01 << cluster_bits;
256 let max_length: usize =
257 (cluster_size - V3_BARE_HEADER_SIZE - QCOW_EMPTY_HEADER_EXTENSION_SIZE) as usize;
258 if let Some(path) = backing_file {
259 if path.len() > max_length {
260 return Err(Error::BackingFileTooLong(path.len() - max_length));
261 }
262 }
263 // L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses.
264 let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
265 let num_clusters: u32 = div_round_up_u64(size, u64::from(cluster_size)) as u32;
266 let num_l2_clusters: u32 = div_round_up_u32(num_clusters, l2_size);
267 let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, cluster_size);
268 let header_clusters = div_round_up_u32(size_of::<QcowHeader>() as u32, cluster_size);
269 Ok(QcowHeader {
270 magic: QCOW_MAGIC,
271 version: 3,
272 backing_file_offset: (if backing_file.is_none() {
273 0
274 } else {
275 V3_BARE_HEADER_SIZE + QCOW_EMPTY_HEADER_EXTENSION_SIZE
276 }) as u64,
277 backing_file_size: backing_file.map_or(0, |x| x.len()) as u32,
278 cluster_bits: DEFAULT_CLUSTER_BITS,
279 size,
280 crypt_method: 0,
281 l1_size: num_l2_clusters,
282 l1_table_offset: u64::from(cluster_size),
283 // The refcount table is after l1 + header.
284 refcount_table_offset: u64::from(cluster_size * (l1_clusters + 1)),
285 refcount_table_clusters: {
286 // Pre-allocate enough clusters for the entire refcount table as it must be
287 // continuous in the file. Allocate enough space to refcount all clusters, including
288 // the refcount clusters.
289 let max_refcount_clusters = max_refcount_clusters(
290 DEFAULT_REFCOUNT_ORDER,
291 cluster_size,
292 num_clusters + l1_clusters + num_l2_clusters + header_clusters,
293 ) as u32;
294 // The refcount table needs to store the offset of each refcount cluster.
295 div_round_up_u32(
296 max_refcount_clusters * size_of::<u64>() as u32,
297 cluster_size,
298 )
299 },
300 nb_snapshots: 0,
301 snapshots_offset: 0,
302 incompatible_features: 0,
303 compatible_features: 0,
304 autoclear_features: 0,
305 refcount_order: DEFAULT_REFCOUNT_ORDER,
306 header_size: V3_BARE_HEADER_SIZE,
307 backing_file_path: backing_file.map(String::from),
308 })
309 }
310
311 /// Write the header to `file`.
write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()>312 pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> {
313 // Writes the next u32 to the file.
314 fn write_u32_to_file<F: Write>(f: &mut F, value: u32) -> Result<()> {
315 f.write_all(&value.to_be_bytes())
316 .map_err(Error::WritingHeader)
317 }
318
319 // Writes the next u64 to the file.
320 fn write_u64_to_file<F: Write>(f: &mut F, value: u64) -> Result<()> {
321 f.write_all(&value.to_be_bytes())
322 .map_err(Error::WritingHeader)
323 }
324
325 write_u32_to_file(file, self.magic)?;
326 write_u32_to_file(file, self.version)?;
327 write_u64_to_file(file, self.backing_file_offset)?;
328 write_u32_to_file(file, self.backing_file_size)?;
329 write_u32_to_file(file, self.cluster_bits)?;
330 write_u64_to_file(file, self.size)?;
331 write_u32_to_file(file, self.crypt_method)?;
332 write_u32_to_file(file, self.l1_size)?;
333 write_u64_to_file(file, self.l1_table_offset)?;
334 write_u64_to_file(file, self.refcount_table_offset)?;
335 write_u32_to_file(file, self.refcount_table_clusters)?;
336 write_u32_to_file(file, self.nb_snapshots)?;
337 write_u64_to_file(file, self.snapshots_offset)?;
338 write_u64_to_file(file, self.incompatible_features)?;
339 write_u64_to_file(file, self.compatible_features)?;
340 write_u64_to_file(file, self.autoclear_features)?;
341 write_u32_to_file(file, self.refcount_order)?;
342 write_u32_to_file(file, self.header_size)?;
343 write_u32_to_file(file, 0)?; // header extension type: end of header extension area
344 write_u32_to_file(file, 0)?; // length of header extension data: 0
345 if let Some(backing_file_path) = self.backing_file_path.as_ref() {
346 write!(file, "{}", backing_file_path).map_err(Error::WritingHeader)?;
347 }
348
349 // Set the file length by seeking and writing a zero to the last byte. This avoids needing
350 // a `File` instead of anything that implements seek as the `file` argument.
351 // Zeros out the l1 and refcount table clusters.
352 let cluster_size = 0x01u64 << self.cluster_bits;
353 let refcount_blocks_size = u64::from(self.refcount_table_clusters) * cluster_size;
354 file.seek(SeekFrom::Start(
355 self.refcount_table_offset + refcount_blocks_size - 2,
356 ))
357 .map_err(Error::WritingHeader)?;
358 file.write(&[0u8]).map_err(Error::WritingHeader)?;
359
360 Ok(())
361 }
362 }
363
max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64364 fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 {
365 // Use u64 as the product of the u32 inputs can overflow.
366 let refcount_bytes = (0x01 << refcount_order as u64) / 8;
367 let for_data = div_round_up_u64(num_clusters as u64 * refcount_bytes, cluster_size as u64);
368 let for_refcounts = div_round_up_u64(for_data * refcount_bytes, cluster_size as u64);
369 for_data + for_refcounts
370 }
371
372 /// Represents a qcow2 file. This is a sparse file format maintained by the qemu project.
373 /// Full documentation of the format can be found in the qemu repository.
374 ///
375 /// # Example
376 ///
377 /// ```
378 /// # use base::FileReadWriteAtVolatile;
379 /// # use data_model::VolatileSlice;
380 /// # use disk::QcowFile;
381 /// # fn test(file: std::fs::File) -> std::io::Result<()> {
382 /// let mut q = QcowFile::from(file, disk::MAX_NESTING_DEPTH).expect("Can't open qcow file");
383 /// let mut buf = [0u8; 12];
384 /// let mut vslice = VolatileSlice::new(&mut buf);
385 /// q.read_at_volatile(vslice, 10)?;
386 /// # Ok(())
387 /// # }
388 /// ```
389 #[derive(Debug)]
390 pub struct QcowFile {
391 raw_file: QcowRawFile,
392 header: QcowHeader,
393 l1_table: VecCache<u64>,
394 l2_entries: u64,
395 l2_cache: CacheMap<VecCache<u64>>,
396 refcounts: RefCount,
397 current_offset: u64,
398 unref_clusters: Vec<u64>, // List of freshly unreferenced clusters.
399 // List of unreferenced clusters available to be used. unref clusters become available once the
400 // removal of references to them have been synced to disk.
401 avail_clusters: Vec<u64>,
402 backing_file: Option<Box<dyn DiskFile>>,
403 }
404
405 impl QcowFile {
406 /// Creates a QcowFile from `file`. File must be a valid qcow2 image.
from(mut file: File, max_nesting_depth: u32) -> Result<QcowFile>407 pub fn from(mut file: File, max_nesting_depth: u32) -> Result<QcowFile> {
408 let header = QcowHeader::new(&mut file)?;
409
410 // Only v3 files are supported.
411 if header.version != 3 {
412 return Err(Error::UnsupportedVersion(header.version));
413 }
414
415 // Make sure that the L1 table fits in RAM.
416 if u64::from(header.l1_size) > MAX_RAM_POINTER_TABLE_SIZE {
417 return Err(Error::InvalidL1TableSize(header.l1_size));
418 }
419
420 let cluster_bits: u32 = header.cluster_bits;
421 if !(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits) {
422 return Err(Error::InvalidClusterSize);
423 }
424 let cluster_size = 0x01u64 << cluster_bits;
425
426 // Limit the total size of the disk.
427 if header.size > MAX_QCOW_FILE_SIZE {
428 return Err(Error::FileTooBig(header.size));
429 }
430
431 let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
432 let path = backing_file_path.clone();
433 let backing_raw_file = open_file(
434 Path::new(&path),
435 OpenOptions::new().read(true), // TODO(b/190435784): Add support for O_DIRECT.
436 )
437 .map_err(|e| Error::BackingFileIo(e.into()))?;
438 let backing_file =
439 create_disk_file(backing_raw_file, max_nesting_depth, Path::new(&path))
440 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
441 Some(backing_file)
442 } else {
443 None
444 };
445
446 // Only support two byte refcounts.
447 let refcount_bits: u64 = 0x01u64
448 .checked_shl(header.refcount_order)
449 .ok_or(Error::UnsupportedRefcountOrder)?;
450 if refcount_bits != 16 {
451 return Err(Error::UnsupportedRefcountOrder);
452 }
453 let refcount_bytes = (refcount_bits + 7) / 8;
454
455 // Need at least one refcount cluster
456 if header.refcount_table_clusters == 0 {
457 return Err(Error::NoRefcountClusters);
458 }
459 offset_is_cluster_boundary(header.l1_table_offset, header.cluster_bits)?;
460 offset_is_cluster_boundary(header.snapshots_offset, header.cluster_bits)?;
461 // refcount table must be a cluster boundary, and within the file's virtual or actual size.
462 offset_is_cluster_boundary(header.refcount_table_offset, header.cluster_bits)?;
463 let file_size = file.metadata().map_err(Error::GettingFileSize)?.len();
464 if header.refcount_table_offset > max(file_size, header.size) {
465 return Err(Error::RefcountTableOffEnd);
466 }
467
468 // The first cluster should always have a non-zero refcount, so if it is 0,
469 // this is an old file with broken refcounts, which requires a rebuild.
470 let mut refcount_rebuild_required = true;
471 file.seek(SeekFrom::Start(header.refcount_table_offset))
472 .map_err(Error::SeekingFile)?;
473 let first_refblock_addr = read_u64_from_file(&file)?;
474 if first_refblock_addr != 0 {
475 file.seek(SeekFrom::Start(first_refblock_addr))
476 .map_err(Error::SeekingFile)?;
477 let first_cluster_refcount = read_u16_from_file(&file)?;
478 if first_cluster_refcount != 0 {
479 refcount_rebuild_required = false;
480 }
481 }
482
483 if (header.compatible_features & COMPATIBLE_FEATURES_LAZY_REFCOUNTS) != 0 {
484 refcount_rebuild_required = true;
485 }
486
487 let mut raw_file =
488 QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
489 if refcount_rebuild_required {
490 QcowFile::rebuild_refcounts(&mut raw_file, header.clone())?;
491 }
492
493 let l2_size = cluster_size / size_of::<u64>() as u64;
494 let num_clusters = div_round_up_u64(header.size, cluster_size);
495 let num_l2_clusters = div_round_up_u64(num_clusters, l2_size);
496 let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size);
497 let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
498 if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE {
499 return Err(Error::TooManyL1Entries(num_l2_clusters));
500 }
501 let l1_table = VecCache::from_vec(
502 raw_file
503 .read_pointer_table(
504 header.l1_table_offset,
505 num_l2_clusters,
506 Some(L1_TABLE_OFFSET_MASK),
507 )
508 .map_err(Error::ReadingHeader)?,
509 );
510
511 let num_clusters = div_round_up_u64(header.size, cluster_size);
512 let refcount_clusters = max_refcount_clusters(
513 header.refcount_order,
514 cluster_size as u32,
515 (num_clusters + l1_clusters + num_l2_clusters + header_clusters) as u32,
516 );
517 // Check that the given header doesn't have a suspiciously sized refcount table.
518 if u64::from(header.refcount_table_clusters) > 2 * refcount_clusters {
519 return Err(Error::RefcountTableTooLarge);
520 }
521 if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
522 return Err(Error::TooManyRefcounts(refcount_clusters));
523 }
524 let refcount_block_entries = cluster_size / refcount_bytes;
525 let refcounts = RefCount::new(
526 &mut raw_file,
527 header.refcount_table_offset,
528 refcount_clusters,
529 refcount_block_entries,
530 cluster_size,
531 )
532 .map_err(Error::ReadingRefCounts)?;
533
534 let l2_entries = cluster_size / size_of::<u64>() as u64;
535
536 let mut qcow = QcowFile {
537 raw_file,
538 header,
539 l1_table,
540 l2_entries,
541 l2_cache: CacheMap::new(100),
542 refcounts,
543 current_offset: 0,
544 unref_clusters: Vec::new(),
545 avail_clusters: Vec::new(),
546 backing_file,
547 };
548
549 // Check that the L1 and refcount tables fit in a 64bit address space.
550 qcow.header
551 .l1_table_offset
552 .checked_add(qcow.l1_address_offset(qcow.virtual_size()))
553 .ok_or(Error::InvalidL1TableOffset)?;
554 qcow.header
555 .refcount_table_offset
556 .checked_add(u64::from(qcow.header.refcount_table_clusters) * cluster_size)
557 .ok_or(Error::InvalidRefcountTableOffset)?;
558
559 qcow.find_avail_clusters()?;
560
561 Ok(qcow)
562 }
563
564 /// Creates a new QcowFile at the given path.
new(file: File, virtual_size: u64) -> Result<QcowFile>565 pub fn new(file: File, virtual_size: u64) -> Result<QcowFile> {
566 let header = QcowHeader::create_for_size_and_path(virtual_size, None)?;
567 QcowFile::new_from_header(file, header, 1)
568 }
569
570 /// Creates a new QcowFile at the given path.
new_from_backing( file: File, backing_file_name: &str, backing_file_max_nesting_depth: u32, ) -> Result<QcowFile>571 pub fn new_from_backing(
572 file: File,
573 backing_file_name: &str,
574 backing_file_max_nesting_depth: u32,
575 ) -> Result<QcowFile> {
576 let backing_path = Path::new(backing_file_name);
577 let backing_raw_file = open_file(
578 backing_path,
579 OpenOptions::new().read(true), // TODO(b/190435784): add support for O_DIRECT.
580 )
581 .map_err(|e| Error::BackingFileIo(e.into()))?;
582 let backing_file = create_disk_file(
583 backing_raw_file,
584 backing_file_max_nesting_depth,
585 backing_path,
586 )
587 .map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
588 let size = backing_file.get_len().map_err(Error::BackingFileIo)?;
589 let header = QcowHeader::create_for_size_and_path(size, Some(backing_file_name))?;
590 let mut result = QcowFile::new_from_header(file, header, backing_file_max_nesting_depth)?;
591 result.backing_file = Some(backing_file);
592 Ok(result)
593 }
594
new_from_header( mut file: File, header: QcowHeader, max_nesting_depth: u32, ) -> Result<QcowFile>595 fn new_from_header(
596 mut file: File,
597 header: QcowHeader,
598 max_nesting_depth: u32,
599 ) -> Result<QcowFile> {
600 file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
601 header.write_to(&mut file)?;
602
603 let mut qcow = Self::from(file, max_nesting_depth)?;
604
605 // Set the refcount for each refcount table cluster.
606 let cluster_size = 0x01u64 << qcow.header.cluster_bits;
607 let refcount_table_base = qcow.header.refcount_table_offset as u64;
608 let end_cluster_addr =
609 refcount_table_base + u64::from(qcow.header.refcount_table_clusters) * cluster_size;
610
611 let mut cluster_addr = 0;
612 while cluster_addr < end_cluster_addr {
613 let mut unref_clusters = qcow
614 .set_cluster_refcount(cluster_addr, 1)
615 .map_err(Error::SettingRefcountRefcount)?;
616 qcow.unref_clusters.append(&mut unref_clusters);
617 cluster_addr += cluster_size;
618 }
619
620 Ok(qcow)
621 }
622
set_backing_file(&mut self, backing: Option<Box<dyn DiskFile>>)623 pub fn set_backing_file(&mut self, backing: Option<Box<dyn DiskFile>>) {
624 self.backing_file = backing;
625 }
626
627 /// Returns the first cluster in the file with a 0 refcount. Used for testing.
first_zero_refcount(&mut self) -> Result<Option<u64>>628 pub fn first_zero_refcount(&mut self) -> Result<Option<u64>> {
629 let file_size = self
630 .raw_file
631 .file_mut()
632 .metadata()
633 .map_err(Error::GettingFileSize)?
634 .len();
635 let cluster_size = 0x01u64 << self.header.cluster_bits;
636
637 let mut cluster_addr = 0;
638 while cluster_addr < file_size {
639 let cluster_refcount = self
640 .refcounts
641 .get_cluster_refcount(&mut self.raw_file, cluster_addr)
642 .map_err(Error::GettingRefcount)?;
643 if cluster_refcount == 0 {
644 return Ok(Some(cluster_addr));
645 }
646 cluster_addr += cluster_size;
647 }
648 Ok(None)
649 }
650
find_avail_clusters(&mut self) -> Result<()>651 fn find_avail_clusters(&mut self) -> Result<()> {
652 let cluster_size = self.raw_file.cluster_size();
653
654 let file_size = self
655 .raw_file
656 .file_mut()
657 .metadata()
658 .map_err(Error::GettingFileSize)?
659 .len();
660
661 for i in (0..file_size).step_by(cluster_size as usize) {
662 let refcount = self
663 .refcounts
664 .get_cluster_refcount(&mut self.raw_file, i)
665 .map_err(Error::GettingRefcount)?;
666 if refcount == 0 {
667 self.avail_clusters.push(i);
668 }
669 }
670
671 Ok(())
672 }
673
674 /// Rebuild the reference count tables.
rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()>675 fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()> {
676 fn add_ref(refcounts: &mut [u16], cluster_size: u64, cluster_address: u64) -> Result<()> {
677 let idx = (cluster_address / cluster_size) as usize;
678 if idx >= refcounts.len() {
679 return Err(Error::InvalidClusterIndex);
680 }
681 refcounts[idx] += 1;
682 Ok(())
683 }
684
685 // Add a reference to the first cluster (header plus extensions).
686 fn set_header_refcount(refcounts: &mut [u16], cluster_size: u64) -> Result<()> {
687 add_ref(refcounts, cluster_size, 0)
688 }
689
690 // Add references to the L1 table clusters.
691 fn set_l1_refcounts(
692 refcounts: &mut [u16],
693 header: QcowHeader,
694 cluster_size: u64,
695 ) -> Result<()> {
696 let l1_clusters = div_round_up_u64(header.l1_size as u64, cluster_size);
697 let l1_table_offset = header.l1_table_offset;
698 for i in 0..l1_clusters {
699 add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?;
700 }
701 Ok(())
702 }
703
704 // Traverse the L1 and L2 tables to find all reachable data clusters.
705 fn set_data_refcounts(
706 refcounts: &mut [u16],
707 header: QcowHeader,
708 cluster_size: u64,
709 raw_file: &mut QcowRawFile,
710 ) -> Result<()> {
711 let l1_table = raw_file
712 .read_pointer_table(
713 header.l1_table_offset,
714 header.l1_size as u64,
715 Some(L1_TABLE_OFFSET_MASK),
716 )
717 .map_err(Error::ReadingPointers)?;
718 for l1_index in 0..header.l1_size as usize {
719 let l2_addr_disk = *l1_table.get(l1_index).ok_or(Error::InvalidIndex)?;
720 if l2_addr_disk != 0 {
721 // Add a reference to the L2 table cluster itself.
722 add_ref(refcounts, cluster_size, l2_addr_disk)?;
723
724 // Read the L2 table and find all referenced data clusters.
725 let l2_table = raw_file
726 .read_pointer_table(
727 l2_addr_disk,
728 cluster_size / size_of::<u64>() as u64,
729 Some(L2_TABLE_OFFSET_MASK),
730 )
731 .map_err(Error::ReadingPointers)?;
732 for data_cluster_addr in l2_table {
733 if data_cluster_addr != 0 {
734 add_ref(refcounts, cluster_size, data_cluster_addr)?;
735 }
736 }
737 }
738 }
739
740 Ok(())
741 }
742
743 // Add references to the top-level refcount table clusters.
744 fn set_refcount_table_refcounts(
745 refcounts: &mut [u16],
746 header: QcowHeader,
747 cluster_size: u64,
748 ) -> Result<()> {
749 let refcount_table_offset = header.refcount_table_offset;
750 for i in 0..header.refcount_table_clusters as u64 {
751 add_ref(
752 refcounts,
753 cluster_size,
754 refcount_table_offset + i * cluster_size,
755 )?;
756 }
757 Ok(())
758 }
759
760 // Allocate clusters for refblocks.
761 // This needs to be done last so that we have the correct refcounts for all other
762 // clusters.
763 fn alloc_refblocks(
764 refcounts: &mut [u16],
765 cluster_size: u64,
766 refblock_clusters: u64,
767 pointers_per_cluster: u64,
768 ) -> Result<Vec<u64>> {
769 let refcount_table_entries = div_round_up_u64(refblock_clusters, pointers_per_cluster);
770 let mut ref_table = vec![0; refcount_table_entries as usize];
771 let mut first_free_cluster: u64 = 0;
772 for refblock_addr in &mut ref_table {
773 loop {
774 if first_free_cluster >= refcounts.len() as u64 {
775 return Err(Error::NotEnoughSpaceForRefcounts);
776 }
777 if refcounts[first_free_cluster as usize] == 0 {
778 break;
779 }
780 first_free_cluster += 1;
781 }
782
783 *refblock_addr = first_free_cluster * cluster_size;
784 add_ref(refcounts, cluster_size, *refblock_addr)?;
785
786 first_free_cluster += 1;
787 }
788
789 Ok(ref_table)
790 }
791
792 // Write the updated reference count blocks and reftable.
793 fn write_refblocks(
794 refcounts: &[u16],
795 mut header: QcowHeader,
796 ref_table: &[u64],
797 raw_file: &mut QcowRawFile,
798 refcount_block_entries: u64,
799 ) -> Result<()> {
800 // Rewrite the header with lazy refcounts enabled while we are rebuilding the tables.
801 header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
802 raw_file
803 .file_mut()
804 .seek(SeekFrom::Start(0))
805 .map_err(Error::SeekingFile)?;
806 header.write_to(raw_file.file_mut())?;
807
808 for (i, refblock_addr) in ref_table.iter().enumerate() {
809 // Write a block of refcounts to the location indicated by refblock_addr.
810 let refblock_start = i * (refcount_block_entries as usize);
811 let refblock_end = min(
812 refcounts.len(),
813 refblock_start + refcount_block_entries as usize,
814 );
815 let refblock = &refcounts[refblock_start..refblock_end];
816 raw_file
817 .write_refcount_block(*refblock_addr, refblock)
818 .map_err(Error::WritingHeader)?;
819
820 // If this is the last (partial) cluster, pad it out to a full refblock cluster.
821 if refblock.len() < refcount_block_entries as usize {
822 let refblock_padding =
823 vec![0u16; refcount_block_entries as usize - refblock.len()];
824 raw_file
825 .write_refcount_block(
826 *refblock_addr + refblock.len() as u64 * 2,
827 &refblock_padding,
828 )
829 .map_err(Error::WritingHeader)?;
830 }
831 }
832
833 // Rewrite the top-level refcount table.
834 raw_file
835 .write_pointer_table(header.refcount_table_offset, ref_table, 0)
836 .map_err(Error::WritingHeader)?;
837
838 // Rewrite the header again, now with lazy refcounts disabled.
839 header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS;
840 raw_file
841 .file_mut()
842 .seek(SeekFrom::Start(0))
843 .map_err(Error::SeekingFile)?;
844 header.write_to(raw_file.file_mut())?;
845
846 Ok(())
847 }
848
849 let cluster_size = raw_file.cluster_size();
850
851 let file_size = raw_file
852 .file_mut()
853 .metadata()
854 .map_err(Error::GettingFileSize)?
855 .len();
856
857 let refcount_bits = 1u64 << header.refcount_order;
858 let refcount_bytes = div_round_up_u64(refcount_bits, 8);
859 let refcount_block_entries = cluster_size / refcount_bytes;
860 let pointers_per_cluster = cluster_size / size_of::<u64>() as u64;
861 let data_clusters = div_round_up_u64(header.size, cluster_size);
862 let l2_clusters = div_round_up_u64(data_clusters, pointers_per_cluster);
863 let l1_clusters = div_round_up_u64(l2_clusters, cluster_size);
864 let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size);
865 let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters;
866 let mut max_valid_cluster_index = max_clusters;
867 let refblock_clusters = div_round_up_u64(max_valid_cluster_index, refcount_block_entries);
868 let reftable_clusters = div_round_up_u64(refblock_clusters, pointers_per_cluster);
869 // Account for refblocks and the ref table size needed to address them.
870 let refblocks_for_refs = div_round_up_u64(
871 refblock_clusters + reftable_clusters,
872 refcount_block_entries,
873 );
874 let reftable_clusters_for_refs =
875 div_round_up_u64(refblocks_for_refs, refcount_block_entries);
876 max_valid_cluster_index += refblock_clusters + reftable_clusters;
877 max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs;
878
879 if max_valid_cluster_index > MAX_RAM_POINTER_TABLE_SIZE {
880 return Err(Error::InvalidRefcountTableSize(max_valid_cluster_index));
881 }
882
883 let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
884 if max_valid_cluster_offset < file_size - cluster_size {
885 return Err(Error::InvalidRefcountTableSize(max_valid_cluster_offset));
886 }
887
888 let mut refcounts = vec![0; max_valid_cluster_index as usize];
889
890 // Find all references clusters and rebuild refcounts.
891 set_header_refcount(&mut refcounts, cluster_size)?;
892 set_l1_refcounts(&mut refcounts, header.clone(), cluster_size)?;
893 set_data_refcounts(&mut refcounts, header.clone(), cluster_size, raw_file)?;
894 set_refcount_table_refcounts(&mut refcounts, header.clone(), cluster_size)?;
895
896 // Allocate clusters to store the new reference count blocks.
897 let ref_table = alloc_refblocks(
898 &mut refcounts,
899 cluster_size,
900 refblock_clusters,
901 pointers_per_cluster,
902 )?;
903
904 // Write updated reference counts and point the reftable at them.
905 write_refblocks(
906 &refcounts,
907 header,
908 &ref_table,
909 raw_file,
910 refcount_block_entries,
911 )
912 }
913
914 // Limits the range so that it doesn't exceed the virtual size of the file.
limit_range_file(&self, address: u64, count: usize) -> usize915 fn limit_range_file(&self, address: u64, count: usize) -> usize {
916 if address.checked_add(count as u64).is_none() || address > self.virtual_size() {
917 return 0;
918 }
919 min(count as u64, self.virtual_size() - address) as usize
920 }
921
922 // Limits the range so that it doesn't overflow the end of a cluster.
limit_range_cluster(&self, address: u64, count: usize) -> usize923 fn limit_range_cluster(&self, address: u64, count: usize) -> usize {
924 let offset: u64 = self.raw_file.cluster_offset(address);
925 let limit = self.raw_file.cluster_size() - offset;
926 min(count as u64, limit) as usize
927 }
928
929 // Gets the maximum virtual size of this image.
virtual_size(&self) -> u64930 fn virtual_size(&self) -> u64 {
931 self.header.size
932 }
933
934 // Gets the offset of `address` in the L1 table.
l1_address_offset(&self, address: u64) -> u64935 fn l1_address_offset(&self, address: u64) -> u64 {
936 let l1_index = self.l1_table_index(address);
937 l1_index * size_of::<u64>() as u64
938 }
939
940 // Gets the offset of `address` in the L1 table.
l1_table_index(&self, address: u64) -> u64941 fn l1_table_index(&self, address: u64) -> u64 {
942 (address / self.raw_file.cluster_size()) / self.l2_entries
943 }
944
945 // Gets the offset of `address` in the L2 table.
l2_table_index(&self, address: u64) -> u64946 fn l2_table_index(&self, address: u64) -> u64 {
947 (address / self.raw_file.cluster_size()) % self.l2_entries
948 }
949
950 // Gets the offset of the given guest address in the host file. If L1, L2, or data clusters have
951 // yet to be allocated, return None.
file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>>952 fn file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>> {
953 if address >= self.virtual_size() as u64 {
954 return Err(std::io::Error::from_raw_os_error(EINVAL));
955 }
956
957 let l1_index = self.l1_table_index(address) as usize;
958 let l2_addr_disk = *self
959 .l1_table
960 .get(l1_index)
961 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
962
963 if l2_addr_disk == 0 {
964 // Reading from an unallocated cluster will return zeros.
965 return Ok(None);
966 }
967
968 let l2_index = self.l2_table_index(address) as usize;
969
970 if !self.l2_cache.contains_key(&l1_index) {
971 // Not in the cache.
972 let table =
973 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
974
975 let l1_table = &self.l1_table;
976 let raw_file = &mut self.raw_file;
977 self.l2_cache.insert(l1_index, table, |index, evicted| {
978 raw_file.write_pointer_table(
979 l1_table[index],
980 evicted.get_values(),
981 CLUSTER_USED_FLAG,
982 )
983 })?;
984 };
985
986 let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
987 if cluster_addr == 0 {
988 return Ok(None);
989 }
990 Ok(Some(cluster_addr + self.raw_file.cluster_offset(address)))
991 }
992
993 // Gets the offset of the given guest address in the host file. If L1, L2, or data clusters need
994 // to be allocated, they will be.
file_offset_write(&mut self, address: u64) -> std::io::Result<u64>995 fn file_offset_write(&mut self, address: u64) -> std::io::Result<u64> {
996 if address >= self.virtual_size() as u64 {
997 return Err(std::io::Error::from_raw_os_error(EINVAL));
998 }
999
1000 let l1_index = self.l1_table_index(address) as usize;
1001 let l2_addr_disk = *self
1002 .l1_table
1003 .get(l1_index)
1004 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1005 let l2_index = self.l2_table_index(address) as usize;
1006
1007 let mut set_refcounts = Vec::new();
1008
1009 if !self.l2_cache.contains_key(&l1_index) {
1010 // Not in the cache.
1011 let l2_table = if l2_addr_disk == 0 {
1012 // Allocate a new cluster to store the L2 table and update the L1 table to point
1013 // to the new table.
1014 let new_addr: u64 = self.get_new_cluster(None)?;
1015 // The cluster refcount starts at one meaning it is used but doesn't need COW.
1016 set_refcounts.push((new_addr, 1));
1017 self.l1_table[l1_index] = new_addr;
1018 VecCache::new(self.l2_entries as usize)
1019 } else {
1020 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?)
1021 };
1022 let l1_table = &self.l1_table;
1023 let raw_file = &mut self.raw_file;
1024 self.l2_cache.insert(l1_index, l2_table, |index, evicted| {
1025 raw_file.write_pointer_table(
1026 l1_table[index],
1027 evicted.get_values(),
1028 CLUSTER_USED_FLAG,
1029 )
1030 })?;
1031 }
1032
1033 let cluster_addr = match self.l2_cache.get(&l1_index).unwrap()[l2_index] {
1034 0 => {
1035 let initial_data = if let Some(backing) = self.backing_file.as_mut() {
1036 let cluster_size = self.raw_file.cluster_size();
1037 let cluster_begin = address - (address % cluster_size);
1038 let mut cluster_data = vec![0u8; cluster_size as usize];
1039 let volatile_slice = VolatileSlice::new(&mut cluster_data);
1040 backing.read_exact_at_volatile(volatile_slice, cluster_begin)?;
1041 Some(cluster_data)
1042 } else {
1043 None
1044 };
1045 // Need to allocate a data cluster
1046 let cluster_addr = self.append_data_cluster(initial_data)?;
1047 self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
1048 cluster_addr
1049 }
1050 a => a,
1051 };
1052
1053 for (addr, count) in set_refcounts {
1054 let mut newly_unref = self.set_cluster_refcount(addr, count)?;
1055 self.unref_clusters.append(&mut newly_unref);
1056 }
1057
1058 Ok(cluster_addr + self.raw_file.cluster_offset(address))
1059 }
1060
1061 // Updates the l1 and l2 tables to point to the new `cluster_addr`.
update_cluster_addr( &mut self, l1_index: usize, l2_index: usize, cluster_addr: u64, set_refcounts: &mut Vec<(u64, u16)>, ) -> io::Result<()>1062 fn update_cluster_addr(
1063 &mut self,
1064 l1_index: usize,
1065 l2_index: usize,
1066 cluster_addr: u64,
1067 set_refcounts: &mut Vec<(u64, u16)>,
1068 ) -> io::Result<()> {
1069 if !self.l2_cache.get(&l1_index).unwrap().dirty() {
1070 // Free the previously used cluster if one exists. Modified tables are always
1071 // witten to new clusters so the L1 table can be committed to disk after they
1072 // are and L1 never points at an invalid table.
1073 // The index must be valid from when it was insterted.
1074 let addr = self.l1_table[l1_index];
1075 if addr != 0 {
1076 self.unref_clusters.push(addr);
1077 set_refcounts.push((addr, 0));
1078 }
1079
1080 // Allocate a new cluster to store the L2 table and update the L1 table to point
1081 // to the new table. The cluster will be written when the cache is flushed, no
1082 // need to copy the data now.
1083 let new_addr: u64 = self.get_new_cluster(None)?;
1084 // The cluster refcount starts at one indicating it is used but doesn't need
1085 // COW.
1086 set_refcounts.push((new_addr, 1));
1087 self.l1_table[l1_index] = new_addr;
1088 }
1089 // 'unwrap' is OK because it was just added.
1090 self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = cluster_addr;
1091 Ok(())
1092 }
1093
1094 // Allocate a new cluster and return its offset within the raw file.
get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64>1095 fn get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1096 // First use a pre allocated cluster if one is available.
1097 if let Some(free_cluster) = self.avail_clusters.pop() {
1098 if let Some(initial_data) = initial_data {
1099 self.raw_file.write_cluster(free_cluster, initial_data)?;
1100 } else {
1101 self.raw_file.zero_cluster(free_cluster)?;
1102 }
1103 return Ok(free_cluster);
1104 }
1105
1106 let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
1107 if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
1108 if let Some(initial_data) = initial_data {
1109 self.raw_file.write_cluster(new_cluster, initial_data)?;
1110 }
1111 Ok(new_cluster)
1112 } else {
1113 error!("No free clusters in get_new_cluster()");
1114 Err(std::io::Error::from_raw_os_error(ENOSPC))
1115 }
1116 }
1117
1118 // Allocate and initialize a new data cluster. Returns the offset of the
1119 // cluster in to the file on success.
append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64>1120 fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
1121 let new_addr: u64 = self.get_new_cluster(initial_data)?;
1122 // The cluster refcount starts at one indicating it is used but doesn't need COW.
1123 let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
1124 self.unref_clusters.append(&mut newly_unref);
1125 Ok(new_addr)
1126 }
1127
1128 // Deallocate the storage for the cluster starting at `address`.
1129 // Any future reads of this cluster will return all zeroes (or the backing file, if in use).
deallocate_cluster(&mut self, address: u64) -> std::io::Result<()>1130 fn deallocate_cluster(&mut self, address: u64) -> std::io::Result<()> {
1131 if address >= self.virtual_size() as u64 {
1132 return Err(std::io::Error::from_raw_os_error(EINVAL));
1133 }
1134
1135 let l1_index = self.l1_table_index(address) as usize;
1136 let l2_addr_disk = *self
1137 .l1_table
1138 .get(l1_index)
1139 .ok_or_else(|| std::io::Error::from_raw_os_error(EINVAL))?;
1140 let l2_index = self.l2_table_index(address) as usize;
1141
1142 if l2_addr_disk == 0 {
1143 // The whole L2 table for this address is not allocated yet,
1144 // so the cluster must also be unallocated.
1145 return Ok(());
1146 }
1147
1148 if !self.l2_cache.contains_key(&l1_index) {
1149 // Not in the cache.
1150 let table =
1151 VecCache::from_vec(Self::read_l2_cluster(&mut self.raw_file, l2_addr_disk)?);
1152 let l1_table = &self.l1_table;
1153 let raw_file = &mut self.raw_file;
1154 self.l2_cache.insert(l1_index, table, |index, evicted| {
1155 raw_file.write_pointer_table(
1156 l1_table[index],
1157 evicted.get_values(),
1158 CLUSTER_USED_FLAG,
1159 )
1160 })?;
1161 }
1162
1163 let cluster_addr = self.l2_cache.get(&l1_index).unwrap()[l2_index];
1164 if cluster_addr == 0 {
1165 // This cluster is already unallocated; nothing to do.
1166 return Ok(());
1167 }
1168
1169 // Decrement the refcount.
1170 let refcount = self
1171 .refcounts
1172 .get_cluster_refcount(&mut self.raw_file, cluster_addr)
1173 .map_err(|_| std::io::Error::from_raw_os_error(EINVAL))?;
1174 if refcount == 0 {
1175 return Err(std::io::Error::from_raw_os_error(EINVAL));
1176 }
1177
1178 let new_refcount = refcount - 1;
1179 let mut newly_unref = self.set_cluster_refcount(cluster_addr, new_refcount)?;
1180 self.unref_clusters.append(&mut newly_unref);
1181
1182 // Rewrite the L2 entry to remove the cluster mapping.
1183 // unwrap is safe as we just checked/inserted this entry.
1184 self.l2_cache.get_mut(&l1_index).unwrap()[l2_index] = 0;
1185
1186 if new_refcount == 0 {
1187 let cluster_size = self.raw_file.cluster_size();
1188 // This cluster is no longer in use; deallocate the storage.
1189 // The underlying FS may not support FALLOC_FL_PUNCH_HOLE,
1190 // so don't treat an error as fatal. Future reads will return zeros anyways.
1191 let _ = self
1192 .raw_file
1193 .file_mut()
1194 .punch_hole(cluster_addr, cluster_size);
1195 self.unref_clusters.push(cluster_addr);
1196 }
1197 Ok(())
1198 }
1199
1200 // Fill a range of `length` bytes starting at `address` with zeroes.
1201 // Any future reads of this range will return all zeroes.
1202 // If there is no backing file, this will deallocate cluster storage when possible.
zero_bytes(&mut self, address: u64, length: usize) -> std::io::Result<()>1203 fn zero_bytes(&mut self, address: u64, length: usize) -> std::io::Result<()> {
1204 let write_count: usize = self.limit_range_file(address, length);
1205
1206 let mut nwritten: usize = 0;
1207 while nwritten < write_count {
1208 let curr_addr = address + nwritten as u64;
1209 let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1210
1211 if self.backing_file.is_none() && count == self.raw_file.cluster_size() as usize {
1212 // Full cluster and no backing file in use - deallocate the storage.
1213 self.deallocate_cluster(curr_addr)?;
1214 } else {
1215 // Partial cluster - zero out the relevant bytes.
1216 let offset = if self.backing_file.is_some() {
1217 // There is a backing file, so we need to allocate a cluster in order to
1218 // zero out the hole-punched bytes such that the backing file contents do not
1219 // show through.
1220 Some(self.file_offset_write(curr_addr)?)
1221 } else {
1222 // Any space in unallocated clusters can be left alone, since
1223 // unallocated clusters already read back as zeroes.
1224 self.file_offset_read(curr_addr)?
1225 };
1226 if let Some(offset) = offset {
1227 // Partial cluster - zero it out.
1228 self.raw_file
1229 .file_mut()
1230 .write_zeroes_all_at(offset, count)?;
1231 }
1232 }
1233
1234 nwritten += count;
1235 }
1236 Ok(())
1237 }
1238
1239 // Reads an L2 cluster from the disk, returning an error if the file can't be read or if any
1240 // cluster is compressed.
read_l2_cluster(raw_file: &mut QcowRawFile, cluster_addr: u64) -> std::io::Result<Vec<u64>>1241 fn read_l2_cluster(raw_file: &mut QcowRawFile, cluster_addr: u64) -> std::io::Result<Vec<u64>> {
1242 let file_values = raw_file.read_pointer_cluster(cluster_addr, None)?;
1243 if file_values.iter().any(|entry| entry & COMPRESSED_FLAG != 0) {
1244 return Err(std::io::Error::from_raw_os_error(ENOTSUP));
1245 }
1246 Ok(file_values
1247 .iter()
1248 .map(|entry| *entry & L2_TABLE_OFFSET_MASK)
1249 .collect())
1250 }
1251
1252 // Set the refcount for a cluster with the given address.
1253 // Returns a list of any refblocks that can be reused, this happens when a refblock is moved,
1254 // the old location can be reused.
set_cluster_refcount(&mut self, address: u64, refcount: u16) -> std::io::Result<Vec<u64>>1255 fn set_cluster_refcount(&mut self, address: u64, refcount: u16) -> std::io::Result<Vec<u64>> {
1256 let mut added_clusters = Vec::new();
1257 let mut unref_clusters = Vec::new();
1258 let mut refcount_set = false;
1259 let mut new_cluster = None;
1260
1261 while !refcount_set {
1262 match self.refcounts.set_cluster_refcount(
1263 &mut self.raw_file,
1264 address,
1265 refcount,
1266 new_cluster.take(),
1267 ) {
1268 Ok(None) => {
1269 refcount_set = true;
1270 }
1271 Ok(Some(freed_cluster)) => {
1272 unref_clusters.push(freed_cluster);
1273 refcount_set = true;
1274 }
1275 Err(refcount::Error::EvictingRefCounts(e)) => {
1276 return Err(e);
1277 }
1278 Err(refcount::Error::InvalidIndex) => {
1279 return Err(std::io::Error::from_raw_os_error(EINVAL));
1280 }
1281 Err(refcount::Error::NeedCluster(addr)) => {
1282 // Read the address and call set_cluster_refcount again.
1283 new_cluster = Some((
1284 addr,
1285 VecCache::from_vec(self.raw_file.read_refcount_block(addr)?),
1286 ));
1287 }
1288 Err(refcount::Error::NeedNewCluster) => {
1289 // Allocate the cluster and call set_cluster_refcount again.
1290 let addr = self.get_new_cluster(None)?;
1291 added_clusters.push(addr);
1292 new_cluster = Some((
1293 addr,
1294 VecCache::new(self.refcounts.refcounts_per_block() as usize),
1295 ));
1296 }
1297 Err(refcount::Error::ReadingRefCounts(e)) => {
1298 return Err(e);
1299 }
1300 }
1301 }
1302
1303 for addr in added_clusters {
1304 self.set_cluster_refcount(addr, 1)?;
1305 }
1306 Ok(unref_clusters)
1307 }
1308
sync_caches(&mut self) -> std::io::Result<()>1309 fn sync_caches(&mut self) -> std::io::Result<()> {
1310 // Write out all dirty L2 tables.
1311 for (l1_index, l2_table) in self.l2_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
1312 // The index must be valid from when we insterted it.
1313 let addr = self.l1_table[*l1_index];
1314 if addr != 0 {
1315 self.raw_file.write_pointer_table(
1316 addr,
1317 l2_table.get_values(),
1318 CLUSTER_USED_FLAG,
1319 )?;
1320 } else {
1321 return Err(std::io::Error::from_raw_os_error(EINVAL));
1322 }
1323 l2_table.mark_clean();
1324 }
1325 // Write the modified refcount blocks.
1326 self.refcounts.flush_blocks(&mut self.raw_file)?;
1327 // Make sure metadata(file len) and all data clusters are written.
1328 self.raw_file.file_mut().sync_all()?;
1329
1330 // Push L1 table and refcount table last as all the clusters they point to are now
1331 // guaranteed to be valid.
1332 let mut sync_required = false;
1333 if self.l1_table.dirty() {
1334 self.raw_file.write_pointer_table(
1335 self.header.l1_table_offset,
1336 self.l1_table.get_values(),
1337 0,
1338 )?;
1339 self.l1_table.mark_clean();
1340 sync_required = true;
1341 }
1342 sync_required |= self.refcounts.flush_table(&mut self.raw_file)?;
1343 if sync_required {
1344 self.raw_file.file_mut().sync_data()?;
1345 }
1346 Ok(())
1347 }
1348
1349 // Reads `count` bytes starting at `address`, calling `cb` repeatedly with the data source,
1350 // number of bytes read so far, offset to read from, and number of bytes to read from the file
1351 // in that invocation. If None is given to `cb` in place of the backing file, the `cb` should
1352 // infer zeros would have been read.
read_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize> where F: FnMut(Option<&mut dyn DiskFile>, usize, u64, usize) -> std::io::Result<()>,1353 fn read_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1354 where
1355 F: FnMut(Option<&mut dyn DiskFile>, usize, u64, usize) -> std::io::Result<()>,
1356 {
1357 let read_count: usize = self.limit_range_file(address, count);
1358
1359 let mut nread: usize = 0;
1360 while nread < read_count {
1361 let curr_addr = address + nread as u64;
1362 let file_offset = self.file_offset_read(curr_addr)?;
1363 let count = self.limit_range_cluster(curr_addr, read_count - nread);
1364
1365 if let Some(offset) = file_offset {
1366 cb(Some(self.raw_file.file_mut()), nread, offset, count)?;
1367 } else if let Some(backing) = self.backing_file.as_mut() {
1368 cb(Some(backing.as_mut()), nread, curr_addr, count)?;
1369 } else {
1370 cb(None, nread, 0, count)?;
1371 }
1372
1373 nread += count;
1374 }
1375 Ok(read_count)
1376 }
1377
1378 // Writes `count` bytes starting at `address`, calling `cb` repeatedly with the backing file,
1379 // number of bytes written so far, raw file offset, and number of bytes to write to the file in
1380 // that invocation.
write_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize> where F: FnMut(&mut File, usize, u64, usize) -> std::io::Result<()>,1381 fn write_cb<F>(&mut self, address: u64, count: usize, mut cb: F) -> std::io::Result<usize>
1382 where
1383 F: FnMut(&mut File, usize, u64, usize) -> std::io::Result<()>,
1384 {
1385 let write_count: usize = self.limit_range_file(address, count);
1386
1387 let mut nwritten: usize = 0;
1388 while nwritten < write_count {
1389 let curr_addr = address + nwritten as u64;
1390 let offset = self.file_offset_write(curr_addr)?;
1391 let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
1392
1393 if let Err(e) = cb(self.raw_file.file_mut(), nwritten, offset, count) {
1394 return Err(e);
1395 }
1396
1397 nwritten += count;
1398 }
1399 Ok(write_count)
1400 }
1401 }
1402
1403 impl Drop for QcowFile {
drop(&mut self)1404 fn drop(&mut self) {
1405 let _ = self.sync_caches();
1406 }
1407 }
1408
1409 impl AsRawDescriptors for QcowFile {
as_raw_descriptors(&self) -> Vec<RawDescriptor>1410 fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
1411 let mut descriptors = vec![self.raw_file.file().as_raw_descriptor()];
1412 if let Some(backing) = &self.backing_file {
1413 descriptors.append(&mut backing.as_raw_descriptors());
1414 }
1415 descriptors
1416 }
1417 }
1418
1419 impl Read for QcowFile {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>1420 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1421 let len = buf.len();
1422 let slice = VolatileSlice::new(buf);
1423 let read_count = self.read_cb(
1424 self.current_offset,
1425 len,
1426 |file, already_read, offset, count| {
1427 let sub_slice = slice.get_slice(already_read, count).unwrap();
1428 match file {
1429 Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1430 None => {
1431 sub_slice.write_bytes(0);
1432 Ok(())
1433 }
1434 }
1435 },
1436 )?;
1437 self.current_offset += read_count as u64;
1438 Ok(read_count)
1439 }
1440 }
1441
1442 impl Seek for QcowFile {
seek(&mut self, pos: SeekFrom) -> std::io::Result<u64>1443 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
1444 let new_offset: Option<u64> = match pos {
1445 SeekFrom::Start(off) => Some(off),
1446 SeekFrom::End(off) => {
1447 if off < 0 {
1448 0i64.checked_sub(off)
1449 .and_then(|increment| self.virtual_size().checked_sub(increment as u64))
1450 } else {
1451 self.virtual_size().checked_add(off as u64)
1452 }
1453 }
1454 SeekFrom::Current(off) => {
1455 if off < 0 {
1456 0i64.checked_sub(off)
1457 .and_then(|increment| self.current_offset.checked_sub(increment as u64))
1458 } else {
1459 self.current_offset.checked_add(off as u64)
1460 }
1461 }
1462 };
1463
1464 if let Some(o) = new_offset {
1465 if o <= self.virtual_size() {
1466 self.current_offset = o;
1467 return Ok(o);
1468 }
1469 }
1470 Err(std::io::Error::from_raw_os_error(EINVAL))
1471 }
1472 }
1473
1474 impl Write for QcowFile {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>1475 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1476 let write_count = self.write_cb(
1477 self.current_offset,
1478 buf.len(),
1479 |file, offset, raw_offset, count| {
1480 file.seek(SeekFrom::Start(raw_offset))?;
1481 file.write_all(&buf[offset..(offset + count)])
1482 },
1483 )?;
1484 self.current_offset += write_count as u64;
1485 Ok(write_count)
1486 }
1487
flush(&mut self) -> std::io::Result<()>1488 fn flush(&mut self) -> std::io::Result<()> {
1489 self.fsync()
1490 }
1491 }
1492
1493 impl FileReadWriteAtVolatile for QcowFile {
read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> io::Result<usize>1494 fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1495 self.read_cb(offset, slice.size(), |file, read, offset, count| {
1496 let sub_slice = slice.get_slice(read, count).unwrap();
1497 match file {
1498 Some(f) => f.read_exact_at_volatile(sub_slice, offset),
1499 None => {
1500 sub_slice.write_bytes(0);
1501 Ok(())
1502 }
1503 }
1504 })
1505 }
1506
write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> io::Result<usize>1507 fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
1508 self.write_cb(offset, slice.size(), |file, offset, raw_offset, count| {
1509 let sub_slice = slice.get_slice(offset, count).unwrap();
1510 file.write_all_at_volatile(sub_slice, raw_offset)
1511 })
1512 }
1513 }
1514
1515 impl FileSync for QcowFile {
fsync(&mut self) -> std::io::Result<()>1516 fn fsync(&mut self) -> std::io::Result<()> {
1517 self.sync_caches()?;
1518 self.avail_clusters.append(&mut self.unref_clusters);
1519 Ok(())
1520 }
1521 }
1522
1523 impl FileSetLen for QcowFile {
set_len(&self, _len: u64) -> std::io::Result<()>1524 fn set_len(&self, _len: u64) -> std::io::Result<()> {
1525 Err(std::io::Error::new(
1526 std::io::ErrorKind::Other,
1527 "set_len() not supported for QcowFile",
1528 ))
1529 }
1530 }
1531
1532 impl DiskGetLen for QcowFile {
get_len(&self) -> io::Result<u64>1533 fn get_len(&self) -> io::Result<u64> {
1534 Ok(self.virtual_size())
1535 }
1536 }
1537
1538 impl FileAllocate for QcowFile {
allocate(&mut self, offset: u64, len: u64) -> io::Result<()>1539 fn allocate(&mut self, offset: u64, len: u64) -> io::Result<()> {
1540 // Call write_cb with a do-nothing callback, which will have the effect
1541 // of allocating all clusters in the specified range.
1542 self.write_cb(
1543 offset,
1544 len as usize,
1545 |_file, _offset, _raw_offset, _count| Ok(()),
1546 )?;
1547 Ok(())
1548 }
1549 }
1550
1551 impl PunchHole for QcowFile {
punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()>1552 fn punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()> {
1553 let mut remaining = length;
1554 let mut offset = offset;
1555 while remaining > 0 {
1556 let chunk_length = min(remaining, std::usize::MAX as u64) as usize;
1557 self.zero_bytes(offset, chunk_length)?;
1558 remaining -= chunk_length as u64;
1559 offset += chunk_length as u64;
1560 }
1561 Ok(())
1562 }
1563 }
1564
1565 impl WriteZeroesAt for QcowFile {
write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize>1566 fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize> {
1567 self.punch_hole(offset, length as u64)?;
1568 Ok(length)
1569 }
1570 }
1571
1572 // Returns an Error if the given offset doesn't align to a cluster boundary.
offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()>1573 fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
1574 if offset & ((0x01 << cluster_bits) - 1) != 0 {
1575 return Err(Error::InvalidOffset(offset));
1576 }
1577 Ok(())
1578 }
1579
1580 // Ceiling of the division of `dividend`/`divisor`.
div_round_up_u64(dividend: u64, divisor: u64) -> u641581 fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
1582 dividend / divisor + if dividend % divisor != 0 { 1 } else { 0 }
1583 }
1584
1585 // Ceiling of the division of `dividend`/`divisor`.
div_round_up_u32(dividend: u32, divisor: u32) -> u321586 fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
1587 dividend / divisor + if dividend % divisor != 0 { 1 } else { 0 }
1588 }
1589
1590 #[cfg(test)]
1591 mod tests {
1592 use super::*;
1593 use crate::MAX_NESTING_DEPTH;
1594 use std::fs::OpenOptions;
1595 use std::io::{Read, Seek, SeekFrom, Write};
1596 use tempfile::{tempfile, TempDir};
1597
valid_header() -> Vec<u8>1598 fn valid_header() -> Vec<u8> {
1599 vec![
1600 0x51u8, 0x46, 0x49, 0xfb, // magic
1601 0x00, 0x00, 0x00, 0x03, // version
1602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset
1603 0x00, 0x00, 0x00, 0x00, // backing file size
1604 0x00, 0x00, 0x00, 0x10, // cluster_bits
1605 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, // size
1606 0x00, 0x00, 0x00, 0x00, // crypt method
1607 0x00, 0x00, 0x01, 0x00, // L1 size
1608 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // L1 table offset
1609 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset
1610 0x00, 0x00, 0x00, 0x03, // refcount table clusters
1611 0x00, 0x00, 0x00, 0x00, // nb snapshots
1612 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // snapshots offset
1613 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible_features
1614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible_features
1615 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear_features
1616 0x00, 0x00, 0x00, 0x04, // refcount_order
1617 0x00, 0x00, 0x00, 0x68, // header_length
1618 ]
1619 }
1620
1621 // Test case found by clusterfuzz to allocate excessive memory.
test_huge_header() -> Vec<u8>1622 fn test_huge_header() -> Vec<u8> {
1623 vec![
1624 0x51, 0x46, 0x49, 0xfb, // magic
1625 0x00, 0x00, 0x00, 0x03, // version
1626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset
1627 0x00, 0x00, 0x00, 0x00, // backing file size
1628 0x00, 0x00, 0x00, 0x09, // cluster_bits
1629 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // size
1630 0x00, 0x00, 0x00, 0x00, // crypt method
1631 0x00, 0x00, 0x01, 0x00, // L1 size
1632 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // L1 table offset
1633 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset
1634 0x00, 0x00, 0x00, 0x03, // refcount table clusters
1635 0x00, 0x00, 0x00, 0x00, // nb snapshots
1636 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // snapshots offset
1637 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible_features
1638 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible_features
1639 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear_features
1640 0x00, 0x00, 0x00, 0x04, // refcount_order
1641 0x00, 0x00, 0x00, 0x68, // header_length
1642 ]
1643 }
1644
basic_file(header: &[u8]) -> File1645 fn basic_file(header: &[u8]) -> File {
1646 let mut disk_file = tempfile().expect("failed to create tempfile");
1647 disk_file.write_all(header).unwrap();
1648 disk_file.set_len(0x8000_0000).unwrap();
1649 disk_file.seek(SeekFrom::Start(0)).unwrap();
1650 disk_file
1651 }
1652
with_basic_file<F>(header: &[u8], mut testfn: F) where F: FnMut(File),1653 fn with_basic_file<F>(header: &[u8], mut testfn: F)
1654 where
1655 F: FnMut(File),
1656 {
1657 testfn(basic_file(header)); // File closed when the function exits.
1658 }
1659
with_default_file<F>(file_size: u64, mut testfn: F) where F: FnMut(QcowFile),1660 fn with_default_file<F>(file_size: u64, mut testfn: F)
1661 where
1662 F: FnMut(QcowFile),
1663 {
1664 let file = tempfile().expect("failed to create tempfile");
1665 let qcow_file = QcowFile::new(file, file_size).unwrap();
1666
1667 testfn(qcow_file); // File closed when the function exits.
1668 }
1669
1670 // Test helper function to convert a normal slice to a VolatileSlice and write it.
write_all_at(qcow: &mut QcowFile, data: &[u8], offset: u64) -> std::io::Result<()>1671 fn write_all_at(qcow: &mut QcowFile, data: &[u8], offset: u64) -> std::io::Result<()> {
1672 let mut mem = data.to_owned();
1673 let vslice = VolatileSlice::new(&mut mem);
1674 qcow.write_all_at_volatile(vslice, offset)
1675 }
1676
1677 // Test helper function to read to a VolatileSlice and copy it to a normal slice.
read_exact_at(qcow: &mut QcowFile, data: &mut [u8], offset: u64) -> std::io::Result<()>1678 fn read_exact_at(qcow: &mut QcowFile, data: &mut [u8], offset: u64) -> std::io::Result<()> {
1679 let mut mem = data.to_owned();
1680 let vslice = VolatileSlice::new(&mut mem);
1681 qcow.read_exact_at_volatile(vslice, offset)?;
1682 vslice.copy_to(data);
1683 Ok(())
1684 }
1685
1686 #[test]
default_header()1687 fn default_header() {
1688 let header = QcowHeader::create_for_size_and_path(0x10_0000, None);
1689 let mut disk_file = tempfile().expect("failed to create tempfile");
1690 header
1691 .expect("Failed to create header.")
1692 .write_to(&mut disk_file)
1693 .expect("Failed to write header to shm.");
1694 disk_file.seek(SeekFrom::Start(0)).unwrap();
1695 QcowFile::from(disk_file, MAX_NESTING_DEPTH)
1696 .expect("Failed to create Qcow from default Header");
1697 }
1698
1699 #[test]
header_read()1700 fn header_read() {
1701 with_basic_file(&valid_header(), |mut disk_file: File| {
1702 QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
1703 });
1704 }
1705
1706 #[test]
header_with_backing()1707 fn header_with_backing() {
1708 let header = QcowHeader::create_for_size_and_path(0x10_0000, Some("/my/path/to/a/file"))
1709 .expect("Failed to create header.");
1710 let mut disk_file = tempfile().expect("failed to create tempfile");
1711 header
1712 .write_to(&mut disk_file)
1713 .expect("Failed to write header to shm.");
1714 disk_file.seek(SeekFrom::Start(0)).unwrap();
1715 let read_header = QcowHeader::new(&mut disk_file).expect("Failed to create header.");
1716 assert_eq!(
1717 header.backing_file_path,
1718 Some(String::from("/my/path/to/a/file"))
1719 );
1720 assert_eq!(read_header.backing_file_path, header.backing_file_path);
1721 }
1722
1723 #[test]
invalid_magic()1724 fn invalid_magic() {
1725 let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
1726 with_basic_file(&invalid_header, |mut disk_file: File| {
1727 QcowHeader::new(&mut disk_file).expect_err("Invalid header worked.");
1728 });
1729 }
1730
1731 #[test]
invalid_refcount_order()1732 fn invalid_refcount_order() {
1733 let mut header = valid_header();
1734 header[99] = 2;
1735 with_basic_file(&header, |disk_file: File| {
1736 QcowFile::from(disk_file, MAX_NESTING_DEPTH)
1737 .expect_err("Invalid refcount order worked.");
1738 });
1739 }
1740
1741 #[test]
invalid_cluster_bits()1742 fn invalid_cluster_bits() {
1743 let mut header = valid_header();
1744 header[23] = 3;
1745 with_basic_file(&header, |disk_file: File| {
1746 QcowFile::from(disk_file, MAX_NESTING_DEPTH).expect_err("Failed to create file.");
1747 });
1748 }
1749
1750 #[test]
test_header_huge_file()1751 fn test_header_huge_file() {
1752 let header = test_huge_header();
1753 with_basic_file(&header, |disk_file: File| {
1754 QcowFile::from(disk_file, MAX_NESTING_DEPTH).expect_err("Failed to create file.");
1755 });
1756 }
1757
1758 #[test]
test_header_excessive_file_size_rejected()1759 fn test_header_excessive_file_size_rejected() {
1760 let mut header = valid_header();
1761 header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
1762 with_basic_file(&header, |disk_file: File| {
1763 QcowFile::from(disk_file, MAX_NESTING_DEPTH).expect_err("Failed to create file.");
1764 });
1765 }
1766
1767 #[test]
test_huge_l1_table()1768 fn test_huge_l1_table() {
1769 let mut header = valid_header();
1770 header[36] = 0x12;
1771 with_basic_file(&header, |disk_file: File| {
1772 QcowFile::from(disk_file, MAX_NESTING_DEPTH).expect_err("Failed to create file.");
1773 });
1774 }
1775
1776 #[test]
test_header_1_tb_file_min_cluster()1777 fn test_header_1_tb_file_min_cluster() {
1778 let mut header = test_huge_header();
1779 header[24] = 0;
1780 header[26] = 1;
1781 header[31] = 0;
1782 // 1 TB with the min cluster size makes the arrays too big, it should fail.
1783 with_basic_file(&header, |disk_file: File| {
1784 QcowFile::from(disk_file, MAX_NESTING_DEPTH).expect_err("Failed to create file.");
1785 });
1786 }
1787
1788 #[test]
test_header_1_tb_file()1789 fn test_header_1_tb_file() {
1790 let mut header = test_huge_header();
1791 // reset to 1 TB size.
1792 header[24] = 0;
1793 header[26] = 1;
1794 header[31] = 0;
1795 // set cluster_bits
1796 header[23] = 16;
1797 with_basic_file(&header, |disk_file: File| {
1798 let mut qcow =
1799 QcowFile::from(disk_file, MAX_NESTING_DEPTH).expect("Failed to create file.");
1800 let value = 0x0000_0040_3f00_ffffu64;
1801 write_all_at(&mut qcow, &value.to_le_bytes(), 0x100_0000_0000 - 8)
1802 .expect("failed to write data");
1803 });
1804 }
1805
1806 #[test]
test_header_huge_num_refcounts()1807 fn test_header_huge_num_refcounts() {
1808 let mut header = valid_header();
1809 header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
1810 with_basic_file(&header, |disk_file: File| {
1811 QcowFile::from(disk_file, MAX_NESTING_DEPTH)
1812 .expect_err("Created disk with excessive refcount clusters");
1813 });
1814 }
1815
1816 #[test]
test_header_huge_refcount_offset()1817 fn test_header_huge_refcount_offset() {
1818 let mut header = valid_header();
1819 header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
1820 with_basic_file(&header, |disk_file: File| {
1821 QcowFile::from(disk_file, MAX_NESTING_DEPTH)
1822 .expect_err("Created disk with excessive refcount offset");
1823 });
1824 }
1825
1826 #[test]
write_read_start()1827 fn write_read_start() {
1828 with_basic_file(&valid_header(), |disk_file: File| {
1829 let mut q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1830 write_all_at(&mut q, b"test first bytes", 0).expect("Failed to write test string.");
1831 let mut buf = [0u8; 4];
1832 read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
1833 assert_eq!(&buf, b"test");
1834 });
1835 }
1836
1837 #[test]
write_read_start_backing()1838 fn write_read_start_backing() {
1839 let disk_file = basic_file(&valid_header());
1840 let mut backing = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1841 write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
1842 let mut buf = [0u8; 4];
1843 let wrapping_disk_file = basic_file(&valid_header());
1844 let mut wrapping = QcowFile::from(wrapping_disk_file, MAX_NESTING_DEPTH).unwrap();
1845 wrapping.set_backing_file(Some(Box::new(backing)));
1846 read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
1847 assert_eq!(&buf, b"test");
1848 }
1849
1850 #[test]
write_read_start_backing_overlap()1851 fn write_read_start_backing_overlap() {
1852 let disk_file = basic_file(&valid_header());
1853 let mut backing = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1854 write_all_at(&mut backing, b"test first bytes", 0).expect("Failed to write test string.");
1855 let wrapping_disk_file = basic_file(&valid_header());
1856 let mut wrapping = QcowFile::from(wrapping_disk_file, MAX_NESTING_DEPTH).unwrap();
1857 wrapping.set_backing_file(Some(Box::new(backing)));
1858 write_all_at(&mut wrapping, b"TEST", 0).expect("Failed to write second test string.");
1859 let mut buf = [0u8; 10];
1860 read_exact_at(&mut wrapping, &mut buf, 0).expect("Failed to read.");
1861 assert_eq!(&buf, b"TEST first");
1862 }
1863
1864 #[test]
offset_write_read()1865 fn offset_write_read() {
1866 with_basic_file(&valid_header(), |disk_file: File| {
1867 let mut q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1868 let b = [0x55u8; 0x1000];
1869 write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
1870 let mut buf = [0u8; 4];
1871 read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
1872 assert_eq!(buf[0], 0x55);
1873 });
1874 }
1875
1876 #[test]
write_zeroes_read()1877 fn write_zeroes_read() {
1878 with_basic_file(&valid_header(), |disk_file: File| {
1879 let mut q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1880 // Write some test data.
1881 let b = [0x55u8; 0x1000];
1882 write_all_at(&mut q, &b, 0xfff2000).expect("Failed to write test string.");
1883 // Overwrite the test data with zeroes.
1884 q.write_zeroes_all_at(0xfff2000, 0x200)
1885 .expect("Failed to write zeroes.");
1886 // Verify that the correct part of the data was zeroed out.
1887 let mut buf = [0u8; 0x1000];
1888 read_exact_at(&mut q, &mut buf, 0xfff2000).expect("Failed to read.");
1889 assert_eq!(buf[0], 0);
1890 assert_eq!(buf[0x1FF], 0);
1891 assert_eq!(buf[0x200], 0x55);
1892 assert_eq!(buf[0xFFF], 0x55);
1893 });
1894 }
1895
1896 #[test]
write_zeroes_full_cluster()1897 fn write_zeroes_full_cluster() {
1898 // Choose a size that is larger than a cluster.
1899 // valid_header uses cluster_bits = 12, which corresponds to a cluster size of 4096.
1900 const CHUNK_SIZE: usize = 4096 * 2 + 512;
1901 with_basic_file(&valid_header(), |disk_file: File| {
1902 let mut q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1903 // Write some test data.
1904 let b = [0x55u8; CHUNK_SIZE];
1905 write_all_at(&mut q, &b, 0).expect("Failed to write test string.");
1906 // Overwrite the full cluster with zeroes.
1907 q.write_zeroes_all_at(0, CHUNK_SIZE)
1908 .expect("Failed to write zeroes.");
1909 // Verify that the data was zeroed out.
1910 let mut buf = [0u8; CHUNK_SIZE];
1911 read_exact_at(&mut q, &mut buf, 0).expect("Failed to read.");
1912 assert_eq!(buf[0], 0);
1913 assert_eq!(buf[CHUNK_SIZE - 1], 0);
1914 });
1915 }
1916
1917 #[test]
write_zeroes_backing()1918 fn write_zeroes_backing() {
1919 let disk_file = basic_file(&valid_header());
1920 let mut backing = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1921 // Write some test data.
1922 let b = [0x55u8; 0x1000];
1923 write_all_at(&mut backing, &b, 0xfff2000).expect("Failed to write test string.");
1924 let wrapping_disk_file = basic_file(&valid_header());
1925 let mut wrapping = QcowFile::from(wrapping_disk_file, MAX_NESTING_DEPTH).unwrap();
1926 wrapping.set_backing_file(Some(Box::new(backing)));
1927 // Overwrite the test data with zeroes.
1928 // This should allocate new clusters in the wrapping file so that they can be zeroed.
1929 wrapping
1930 .write_zeroes_all_at(0xfff2000, 0x200)
1931 .expect("Failed to write zeroes.");
1932 // Verify that the correct part of the data was zeroed out.
1933 let mut buf = [0u8; 0x1000];
1934 read_exact_at(&mut wrapping, &mut buf, 0xfff2000).expect("Failed to read.");
1935 assert_eq!(buf[0], 0);
1936 assert_eq!(buf[0x1FF], 0);
1937 assert_eq!(buf[0x200], 0x55);
1938 assert_eq!(buf[0xFFF], 0x55);
1939 }
1940 #[test]
test_header()1941 fn test_header() {
1942 with_basic_file(&valid_header(), |disk_file: File| {
1943 let q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1944 assert_eq!(q.virtual_size(), 0x20_0000_0000);
1945 });
1946 }
1947
1948 #[test]
read_small_buffer()1949 fn read_small_buffer() {
1950 with_basic_file(&valid_header(), |disk_file: File| {
1951 let mut q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1952 let mut b = [5u8; 16];
1953 read_exact_at(&mut q, &mut b, 1000).expect("Failed to read.");
1954 assert_eq!(0, b[0]);
1955 assert_eq!(0, b[15]);
1956 });
1957 }
1958
1959 #[test]
replay_ext4()1960 fn replay_ext4() {
1961 with_basic_file(&valid_header(), |disk_file: File| {
1962 let mut q = QcowFile::from(disk_file, MAX_NESTING_DEPTH).unwrap();
1963 const BUF_SIZE: usize = 0x1000;
1964 let mut b = [0u8; BUF_SIZE];
1965
1966 struct Transfer {
1967 pub write: bool,
1968 pub addr: u64,
1969 }
1970
1971 // Write transactions from mkfs.ext4.
1972 let xfers: Vec<Transfer> = vec![
1973 Transfer {
1974 write: false,
1975 addr: 0xfff0000,
1976 },
1977 Transfer {
1978 write: false,
1979 addr: 0xfffe000,
1980 },
1981 Transfer {
1982 write: false,
1983 addr: 0x0,
1984 },
1985 Transfer {
1986 write: false,
1987 addr: 0x1000,
1988 },
1989 Transfer {
1990 write: false,
1991 addr: 0xffff000,
1992 },
1993 Transfer {
1994 write: false,
1995 addr: 0xffdf000,
1996 },
1997 Transfer {
1998 write: false,
1999 addr: 0xfff8000,
2000 },
2001 Transfer {
2002 write: false,
2003 addr: 0xffe0000,
2004 },
2005 Transfer {
2006 write: false,
2007 addr: 0xffce000,
2008 },
2009 Transfer {
2010 write: false,
2011 addr: 0xffb6000,
2012 },
2013 Transfer {
2014 write: false,
2015 addr: 0xffab000,
2016 },
2017 Transfer {
2018 write: false,
2019 addr: 0xffa4000,
2020 },
2021 Transfer {
2022 write: false,
2023 addr: 0xff8e000,
2024 },
2025 Transfer {
2026 write: false,
2027 addr: 0xff86000,
2028 },
2029 Transfer {
2030 write: false,
2031 addr: 0xff84000,
2032 },
2033 Transfer {
2034 write: false,
2035 addr: 0xff89000,
2036 },
2037 Transfer {
2038 write: false,
2039 addr: 0xfe7e000,
2040 },
2041 Transfer {
2042 write: false,
2043 addr: 0x100000,
2044 },
2045 Transfer {
2046 write: false,
2047 addr: 0x3000,
2048 },
2049 Transfer {
2050 write: false,
2051 addr: 0x7000,
2052 },
2053 Transfer {
2054 write: false,
2055 addr: 0xf000,
2056 },
2057 Transfer {
2058 write: false,
2059 addr: 0x2000,
2060 },
2061 Transfer {
2062 write: false,
2063 addr: 0x4000,
2064 },
2065 Transfer {
2066 write: false,
2067 addr: 0x5000,
2068 },
2069 Transfer {
2070 write: false,
2071 addr: 0x6000,
2072 },
2073 Transfer {
2074 write: false,
2075 addr: 0x8000,
2076 },
2077 Transfer {
2078 write: false,
2079 addr: 0x9000,
2080 },
2081 Transfer {
2082 write: false,
2083 addr: 0xa000,
2084 },
2085 Transfer {
2086 write: false,
2087 addr: 0xb000,
2088 },
2089 Transfer {
2090 write: false,
2091 addr: 0xc000,
2092 },
2093 Transfer {
2094 write: false,
2095 addr: 0xd000,
2096 },
2097 Transfer {
2098 write: false,
2099 addr: 0xe000,
2100 },
2101 Transfer {
2102 write: false,
2103 addr: 0x10000,
2104 },
2105 Transfer {
2106 write: false,
2107 addr: 0x11000,
2108 },
2109 Transfer {
2110 write: false,
2111 addr: 0x12000,
2112 },
2113 Transfer {
2114 write: false,
2115 addr: 0x13000,
2116 },
2117 Transfer {
2118 write: false,
2119 addr: 0x14000,
2120 },
2121 Transfer {
2122 write: false,
2123 addr: 0x15000,
2124 },
2125 Transfer {
2126 write: false,
2127 addr: 0x16000,
2128 },
2129 Transfer {
2130 write: false,
2131 addr: 0x17000,
2132 },
2133 Transfer {
2134 write: false,
2135 addr: 0x18000,
2136 },
2137 Transfer {
2138 write: false,
2139 addr: 0x19000,
2140 },
2141 Transfer {
2142 write: false,
2143 addr: 0x1a000,
2144 },
2145 Transfer {
2146 write: false,
2147 addr: 0x1b000,
2148 },
2149 Transfer {
2150 write: false,
2151 addr: 0x1c000,
2152 },
2153 Transfer {
2154 write: false,
2155 addr: 0x1d000,
2156 },
2157 Transfer {
2158 write: false,
2159 addr: 0x1e000,
2160 },
2161 Transfer {
2162 write: false,
2163 addr: 0x1f000,
2164 },
2165 Transfer {
2166 write: false,
2167 addr: 0x21000,
2168 },
2169 Transfer {
2170 write: false,
2171 addr: 0x22000,
2172 },
2173 Transfer {
2174 write: false,
2175 addr: 0x24000,
2176 },
2177 Transfer {
2178 write: false,
2179 addr: 0x40000,
2180 },
2181 Transfer {
2182 write: false,
2183 addr: 0x0,
2184 },
2185 Transfer {
2186 write: false,
2187 addr: 0x3000,
2188 },
2189 Transfer {
2190 write: false,
2191 addr: 0x7000,
2192 },
2193 Transfer {
2194 write: false,
2195 addr: 0x0,
2196 },
2197 Transfer {
2198 write: false,
2199 addr: 0x1000,
2200 },
2201 Transfer {
2202 write: false,
2203 addr: 0x2000,
2204 },
2205 Transfer {
2206 write: false,
2207 addr: 0x3000,
2208 },
2209 Transfer {
2210 write: false,
2211 addr: 0x0,
2212 },
2213 Transfer {
2214 write: false,
2215 addr: 0x449000,
2216 },
2217 Transfer {
2218 write: false,
2219 addr: 0x48000,
2220 },
2221 Transfer {
2222 write: false,
2223 addr: 0x48000,
2224 },
2225 Transfer {
2226 write: false,
2227 addr: 0x448000,
2228 },
2229 Transfer {
2230 write: false,
2231 addr: 0x44a000,
2232 },
2233 Transfer {
2234 write: false,
2235 addr: 0x48000,
2236 },
2237 Transfer {
2238 write: false,
2239 addr: 0x48000,
2240 },
2241 Transfer {
2242 write: true,
2243 addr: 0x0,
2244 },
2245 Transfer {
2246 write: true,
2247 addr: 0x448000,
2248 },
2249 Transfer {
2250 write: true,
2251 addr: 0x449000,
2252 },
2253 Transfer {
2254 write: true,
2255 addr: 0x44a000,
2256 },
2257 Transfer {
2258 write: true,
2259 addr: 0xfff0000,
2260 },
2261 Transfer {
2262 write: true,
2263 addr: 0xfff1000,
2264 },
2265 Transfer {
2266 write: true,
2267 addr: 0xfff2000,
2268 },
2269 Transfer {
2270 write: true,
2271 addr: 0xfff3000,
2272 },
2273 Transfer {
2274 write: true,
2275 addr: 0xfff4000,
2276 },
2277 Transfer {
2278 write: true,
2279 addr: 0xfff5000,
2280 },
2281 Transfer {
2282 write: true,
2283 addr: 0xfff6000,
2284 },
2285 Transfer {
2286 write: true,
2287 addr: 0xfff7000,
2288 },
2289 Transfer {
2290 write: true,
2291 addr: 0xfff8000,
2292 },
2293 Transfer {
2294 write: true,
2295 addr: 0xfff9000,
2296 },
2297 Transfer {
2298 write: true,
2299 addr: 0xfffa000,
2300 },
2301 Transfer {
2302 write: true,
2303 addr: 0xfffb000,
2304 },
2305 Transfer {
2306 write: true,
2307 addr: 0xfffc000,
2308 },
2309 Transfer {
2310 write: true,
2311 addr: 0xfffd000,
2312 },
2313 Transfer {
2314 write: true,
2315 addr: 0xfffe000,
2316 },
2317 Transfer {
2318 write: true,
2319 addr: 0xffff000,
2320 },
2321 ];
2322
2323 for xfer in &xfers {
2324 if xfer.write {
2325 write_all_at(&mut q, &b, xfer.addr).expect("Failed to write.");
2326 } else {
2327 read_exact_at(&mut q, &mut b, xfer.addr).expect("Failed to read.");
2328 }
2329 }
2330 });
2331 }
2332
2333 #[test]
combo_write_read()2334 fn combo_write_read() {
2335 with_default_file(1024 * 1024 * 1024 * 256, |mut qcow_file| {
2336 const NUM_BLOCKS: usize = 555;
2337 const BLOCK_SIZE: usize = 0x1_0000;
2338 const OFFSET: u64 = 0x1_0000_0020;
2339 let data = [0x55u8; BLOCK_SIZE];
2340 let mut readback = [0u8; BLOCK_SIZE];
2341 for i in 0..NUM_BLOCKS {
2342 let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2343 write_all_at(&mut qcow_file, &data, seek_offset)
2344 .expect("Failed to write test data.");
2345 // Read back the data to check it was written correctly.
2346 read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2347 for (orig, read) in data.iter().zip(readback.iter()) {
2348 assert_eq!(orig, read);
2349 }
2350 }
2351 // Check that address 0 is still zeros.
2352 read_exact_at(&mut qcow_file, &mut readback, 0).expect("Failed to read.");
2353 for read in readback.iter() {
2354 assert_eq!(*read, 0);
2355 }
2356 // Check the data again after the writes have happened.
2357 for i in 0..NUM_BLOCKS {
2358 let seek_offset = OFFSET + (i as u64) * (BLOCK_SIZE as u64);
2359 read_exact_at(&mut qcow_file, &mut readback, seek_offset).expect("Failed to read.");
2360 for (orig, read) in data.iter().zip(readback.iter()) {
2361 assert_eq!(orig, read);
2362 }
2363 }
2364
2365 assert_eq!(qcow_file.first_zero_refcount().unwrap(), None);
2366 });
2367 }
2368
2369 #[test]
rebuild_refcounts()2370 fn rebuild_refcounts() {
2371 with_basic_file(&valid_header(), |mut disk_file: File| {
2372 let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
2373 let cluster_size = 65536;
2374 let mut raw_file =
2375 QcowRawFile::from(disk_file, cluster_size).expect("Failed to create QcowRawFile.");
2376 QcowFile::rebuild_refcounts(&mut raw_file, header)
2377 .expect("Failed to rebuild recounts.");
2378 });
2379 }
2380
2381 #[test]
nested_qcow()2382 fn nested_qcow() {
2383 let tmp_dir = TempDir::new().unwrap();
2384
2385 // A file `backing` is backing a qcow file `qcow.l1`, which in turn is backing another
2386 // qcow file.
2387 let backing_file_path = tmp_dir.path().join("backing");
2388 let _backing_file = OpenOptions::new()
2389 .read(true)
2390 .write(true)
2391 .create(true)
2392 .open(&backing_file_path)
2393 .unwrap();
2394
2395 let level1_qcow_file_path = tmp_dir.path().join("qcow.l1");
2396 let level1_qcow_file = OpenOptions::new()
2397 .read(true)
2398 .write(true)
2399 .create(true)
2400 .open(&level1_qcow_file_path)
2401 .unwrap();
2402 let _level1_qcow_file = QcowFile::new_from_backing(
2403 level1_qcow_file,
2404 backing_file_path.to_str().unwrap(),
2405 1000, /* allow deep nesting */
2406 )
2407 .unwrap();
2408
2409 let level2_qcow_file = tempfile().unwrap();
2410 let _level2_qcow_file = QcowFile::new_from_backing(
2411 level2_qcow_file,
2412 level1_qcow_file_path.to_str().unwrap(),
2413 1000, /* allow deep nesting */
2414 )
2415 .expect("failed to create level2 qcow file");
2416 }
2417
2418 #[test]
io_seek()2419 fn io_seek() {
2420 with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2421 // Cursor should start at 0.
2422 assert_eq!(qcow_file.seek(SeekFrom::Current(0)).unwrap(), 0);
2423
2424 // Seek 1 MB from start.
2425 assert_eq!(
2426 qcow_file.seek(SeekFrom::Start(1024 * 1024)).unwrap(),
2427 1024 * 1024
2428 );
2429
2430 // Rewind 1 MB + 1 byte (past beginning) - seeking to a negative offset is an error and
2431 // should not move the cursor.
2432 qcow_file
2433 .seek(SeekFrom::Current(-(1024 * 1024 + 1)))
2434 .expect_err("negative offset seek should fail");
2435 assert_eq!(qcow_file.seek(SeekFrom::Current(0)).unwrap(), 1024 * 1024);
2436
2437 // Seek to last byte.
2438 assert_eq!(
2439 qcow_file.seek(SeekFrom::End(-1)).unwrap(),
2440 1024 * 1024 * 10 - 1
2441 );
2442
2443 // Seek to EOF.
2444 assert_eq!(qcow_file.seek(SeekFrom::End(0)).unwrap(), 1024 * 1024 * 10);
2445
2446 // Seek past EOF is not allowed.
2447 qcow_file
2448 .seek(SeekFrom::End(1))
2449 .expect_err("seek past EOF should fail");
2450 });
2451 }
2452
2453 #[test]
io_write_read()2454 fn io_write_read() {
2455 with_default_file(1024 * 1024 * 10, |mut qcow_file| {
2456 const BLOCK_SIZE: usize = 0x1_0000;
2457 let data_55 = [0x55u8; BLOCK_SIZE];
2458 let data_aa = [0xaau8; BLOCK_SIZE];
2459 let mut readback = [0u8; BLOCK_SIZE];
2460
2461 qcow_file.write_all(&data_55).unwrap();
2462 assert_eq!(
2463 qcow_file.seek(SeekFrom::Current(0)).unwrap(),
2464 BLOCK_SIZE as u64
2465 );
2466
2467 qcow_file.write_all(&data_aa).unwrap();
2468 assert_eq!(
2469 qcow_file.seek(SeekFrom::Current(0)).unwrap(),
2470 BLOCK_SIZE as u64 * 2
2471 );
2472
2473 // Read BLOCK_SIZE of just 0xaa.
2474 assert_eq!(
2475 qcow_file
2476 .seek(SeekFrom::Current(-(BLOCK_SIZE as i64)))
2477 .unwrap(),
2478 BLOCK_SIZE as u64
2479 );
2480 qcow_file.read_exact(&mut readback).unwrap();
2481 assert_eq!(
2482 qcow_file.seek(SeekFrom::Current(0)).unwrap(),
2483 BLOCK_SIZE as u64 * 2
2484 );
2485 for (orig, read) in data_aa.iter().zip(readback.iter()) {
2486 assert_eq!(orig, read);
2487 }
2488
2489 // Read BLOCK_SIZE of just 0x55.
2490 qcow_file.rewind().unwrap();
2491 qcow_file.read_exact(&mut readback).unwrap();
2492 for (orig, read) in data_55.iter().zip(readback.iter()) {
2493 assert_eq!(orig, read);
2494 }
2495
2496 // Read BLOCK_SIZE crossing between the block of 0x55 and 0xaa.
2497 qcow_file
2498 .seek(SeekFrom::Start(BLOCK_SIZE as u64 / 2))
2499 .unwrap();
2500 qcow_file.read_exact(&mut readback).unwrap();
2501 for (orig, read) in data_55[BLOCK_SIZE / 2..]
2502 .iter()
2503 .chain(data_aa[..BLOCK_SIZE / 2].iter())
2504 .zip(readback.iter())
2505 {
2506 assert_eq!(orig, read);
2507 }
2508 });
2509 }
2510 }
2511