diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index f19a52ce9..4600ec118 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -773,7 +773,7 @@ impl QcowFile { let raw_file = &mut self.raw_file; self.l2_cache .insert(l1_index, table, |index, evicted| { - raw_file.write_pointer_table(l1_table[index], evicted.get_values(), 0) + raw_file.write_pointer_table_direct(l1_table[index], evicted.iter()) }) .map_err(Error::EvictingCache)?; } @@ -998,7 +998,7 @@ impl QcowFile { // Rewrite the top-level refcount table. raw_file - .write_pointer_table(header.refcount_table_offset, ref_table, 0) + .write_pointer_table_direct(header.refcount_table_offset, ref_table.iter()) .map_err(Error::WritingHeader)?; // Rewrite the header again, now with lazy refcounts disabled. @@ -1513,7 +1513,7 @@ impl QcowFile { let l1_table = &self.l1_table; let raw_file = &mut self.raw_file; self.l2_cache.insert(l1_index, l2_table, |index, evicted| { - raw_file.write_pointer_table(l1_table[index], evicted.get_values(), 0) + raw_file.write_pointer_table_direct(l1_table[index], evicted.iter()) })?; } Ok(new_cluster) @@ -1596,7 +1596,7 @@ impl QcowFile { let addr = self.l1_table[*l1_index]; if addr != 0 { self.raw_file - .write_pointer_table(addr, l2_table.get_values(), 0)?; + .write_pointer_table_direct(addr, l2_table.iter())?; } else { return Err(std::io::Error::from_raw_os_error(EINVAL)); } @@ -1610,25 +1610,22 @@ impl QcowFile { // Push L1 table and refcount table last as all the clusters they point to are now // guaranteed to be valid. let mut sync_required = if self.l1_table.dirty() { - // Build L1 table with OFLAG_COPIED bits set correctly based on L2 cluster refcounts - let l1_active: Vec = self - .l1_table - .get_values() - .iter() - .map(|&l2_addr| { + // Write L1 table with OFLAG_COPIED bits + let refcounts = &mut self.refcounts; + self.raw_file.write_pointer_table( + self.header.l1_table_offset, + self.l1_table.iter(), + |raw_file, l2_addr| { if l2_addr == 0 { Ok(0) } else { - let refcount = self - .refcounts - .get_cluster_refcount(&mut self.raw_file, l2_addr) + let refcount = refcounts + .get_cluster_refcount(raw_file, l2_addr) .map_err(|e| std::io::Error::other(Error::GettingRefcount(e)))?; Ok(l1_entry_make(l2_addr, refcount == 1)) } - }) - .collect::>>()?; - self.raw_file - .write_pointer_table(self.header.l1_table_offset, &l1_active, 0)?; + }, + )?; self.l1_table.mark_clean(); true } else { diff --git a/block/src/qcow/qcow_raw_file.rs b/block/src/qcow/qcow_raw_file.rs index 18a1c771e..1b869b459 100644 --- a/block/src/qcow/qcow_raw_file.rs +++ b/block/src/qcow/qcow_raw_file.rs @@ -61,24 +61,47 @@ impl QcowRawFile { self.read_pointer_table(offset, count, mask) } - /// Writes `table` of u64 pointers to `offset` in the file. - /// `non_zero_flags` will be ORed with all non-zero values in `table`. - /// writing. - pub fn write_pointer_table( + /// Internal helper for creating a buffered writer for pointer tables. + #[inline] + fn setup_pointer_table_writer( &mut self, offset: u64, - table: &[u64], - non_zero_flags: u64, - ) -> io::Result<()> { + entries: &impl Iterator, + ) -> io::Result> { self.file.seek(SeekFrom::Start(offset))?; - let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file); - for addr in table { - let val = if *addr == 0 { - 0 - } else { - *addr | non_zero_flags - }; - buffer.write_u64::(val)?; + let my_file = self.file.try_clone()?; + let capacity = entries.size_hint().0 * size_of::(); + Ok(BufWriter::with_capacity(capacity, my_file)) + } + + /// Writes a pointer table to `offset` in the file. + /// Entries are computed on-the-fly by the callback. + pub fn write_pointer_table<'a, T: Copy + 'a>( + &mut self, + offset: u64, + entries: impl Iterator, + mut f: impl FnMut(&mut QcowRawFile, T) -> io::Result, + ) -> io::Result<()> { + let mut buffer = self.setup_pointer_table_writer(offset, &entries)?; + + for addr in entries { + let entry = f(self, *addr)?; + buffer.write_u64::(entry)?; + } + buffer.flush()?; + Ok(()) + } + + /// Writes a pointer table directly without transforming values. + pub fn write_pointer_table_direct<'a>( + &mut self, + offset: u64, + entries: impl Iterator, + ) -> io::Result<()> { + let mut buffer = self.setup_pointer_table_writer(offset, &entries)?; + + for &entry in entries { + buffer.write_u64::(entry)?; } buffer.flush()?; Ok(()) diff --git a/block/src/qcow/refcount.rs b/block/src/qcow/refcount.rs index 0ec1ab5f4..7ef33c1b3 100644 --- a/block/src/qcow/refcount.rs +++ b/block/src/qcow/refcount.rs @@ -156,11 +156,8 @@ impl RefCount { /// Returns true if the table changed since the previous `flush_table()` call. pub fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result { if self.ref_table.dirty() { - raw_file.write_pointer_table( - self.refcount_table_offset, - self.ref_table.get_values(), - 0, - )?; + raw_file + .write_pointer_table_direct(self.refcount_table_offset, self.ref_table.iter())?; self.ref_table.mark_clean(); Ok(true) } else {