From 2ea8f3d0aa7230da41b3e6beba27ff9140f661d3 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Thu, 8 Nov 2018 15:02:53 -0800 Subject: qcow: scan for free clusters at startup During runtime, we track unreferenced clusters (via unref_clusters and avail_clusters) and reuse them before extending the disk image. However, across boots, we did not previously recover the list of unreferenced clusters, so the disk file could grow beyond the range that the reference table count represent. This patch adds a boot-time scan for all unreferenced clusters so that they get reused. BUG=chromium:899273 TEST=Boot with qcow2 image, fill the disk with dd, delete the dd'd file, refill with dd, and so on, repeatedly. Ensure that the disk image does not grow beyond the expected max size and that no clusters beyond the size of the refcount table are used. Change-Id: Idd21b08bb4c55b8244e7ecaccafc4ccc46b7b17a Signed-off-by: Daniel Verkamp Reviewed-on: https://chromium-review.googlesource.com/1327822 Reviewed-by: Dylan Reid --- qcow/src/qcow.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'qcow') diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs index 640f469..6373d01 100644 --- a/qcow/src/qcow.rs +++ b/qcow/src/qcow.rs @@ -388,7 +388,7 @@ impl QcowFile { let l2_entries = cluster_size / size_of::() as u64; - let qcow = QcowFile { + let mut qcow = QcowFile { raw_file, header, l1_table, @@ -410,6 +410,8 @@ impl QcowFile { .checked_add(u64::from(qcow.header.refcount_table_clusters) * cluster_size) .ok_or(Error::InvalidRefcountTableOffset)?; + qcow.find_avail_clusters()?; + Ok(qcow) } @@ -517,6 +519,29 @@ impl QcowFile { Ok(None) } + fn find_avail_clusters(&mut self) -> Result<()> { + let cluster_size = self.raw_file.cluster_size(); + + let file_size = self + .raw_file + .file_mut() + .metadata() + .map_err(Error::GettingFileSize)? + .len(); + + for i in (0..file_size).step_by(cluster_size as usize) { + let refcount = self + .refcounts + .get_cluster_refcount(&mut self.raw_file, i) + .map_err(Error::GettingRefcount)?; + if refcount == 0 { + self.avail_clusters.push(i); + } + } + + Ok(()) + } + /// Rebuild the reference count tables. fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> Result<()> { fn add_ref(refcounts: &mut [u16], cluster_size: u64, cluster_address: u64) -> Result<()> { -- cgit 1.4.1