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 <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1327822
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Daniel Verkamp 2018-11-08 15:02:53 -08:00 committed by chrome-bot
parent f503276291
commit 2ea8f3d0aa

View file

@ -388,7 +388,7 @@ impl QcowFile {
let l2_entries = cluster_size / size_of::<u64>() 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<()> {