mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Extract utility functions into util.rs
Move L1 and L2 table entry helpers, division utilities and related constants from mod.rs into a dedicated util.rs submodule. Both mod.rs and metadata.rs import from util. No functional changes. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
210514cbf3
commit
63db385c3c
+8
-67
@@ -9,6 +9,7 @@ mod header;
|
|||||||
mod qcow_raw_file;
|
mod qcow_raw_file;
|
||||||
mod raw_file;
|
mod raw_file;
|
||||||
mod refcount;
|
mod refcount;
|
||||||
|
mod util;
|
||||||
mod vec_cache;
|
mod vec_cache;
|
||||||
|
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
@@ -37,6 +38,12 @@ use libc::{EINVAL, EIO, ENOSPC};
|
|||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use remain::sorted;
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
pub(crate) use util::MAX_NESTING_DEPTH;
|
||||||
|
use util::{
|
||||||
|
L1_TABLE_OFFSET_MASK, L2_TABLE_OFFSET_MASK, div_round_up_u32, div_round_up_u64, l1_entry_make,
|
||||||
|
l2_entry_compressed_cluster_layout, l2_entry_is_compressed, l2_entry_is_empty,
|
||||||
|
l2_entry_is_zero, l2_entry_make_std, l2_entry_make_zero, l2_entry_std_cluster_addr,
|
||||||
|
};
|
||||||
use vmm_sys_util::file_traits::{FileSetLen, FileSync};
|
use vmm_sys_util::file_traits::{FileSetLen, FileSync};
|
||||||
use vmm_sys_util::seek_hole::SeekHole;
|
use vmm_sys_util::seek_hole::SeekHole;
|
||||||
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||||
@@ -47,9 +54,6 @@ pub use crate::qcow::raw_file::RawFile;
|
|||||||
use crate::qcow::refcount::RefCount;
|
use crate::qcow::refcount::RefCount;
|
||||||
use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
|
use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
|
||||||
|
|
||||||
/// Nesting depth limit for disk formats that can open other disk files.
|
|
||||||
pub(super) const MAX_NESTING_DEPTH: u32 = 10;
|
|
||||||
|
|
||||||
#[sorted]
|
#[sorted]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -157,60 +161,6 @@ pub enum Error {
|
|||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
// bits 0-8 and 56-63 are reserved.
|
|
||||||
const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
|
||||||
const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
|
||||||
// Flags
|
|
||||||
const ZERO_FLAG: u64 = 1 << 0;
|
|
||||||
const COMPRESSED_FLAG: u64 = 1 << 62;
|
|
||||||
const COMPRESSED_SECTOR_SIZE: u64 = 512;
|
|
||||||
const CLUSTER_USED_FLAG: u64 = 1 << 63;
|
|
||||||
|
|
||||||
fn l2_entry_is_empty(l2_entry: u64) -> bool {
|
|
||||||
l2_entry == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check bit 0 - only valid for standard clusters.
|
|
||||||
fn l2_entry_is_zero(l2_entry: u64) -> bool {
|
|
||||||
l2_entry & ZERO_FLAG != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn l2_entry_is_compressed(l2_entry: u64) -> bool {
|
|
||||||
l2_entry & COMPRESSED_FLAG != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get file offset and size of compressed cluster data
|
|
||||||
fn l2_entry_compressed_cluster_layout(l2_entry: u64, cluster_bits: u32) -> (u64, usize) {
|
|
||||||
let compressed_size_shift = 62 - (cluster_bits - 8);
|
|
||||||
let compressed_size_mask = (1 << (cluster_bits - 8)) - 1;
|
|
||||||
let compressed_cluster_addr = l2_entry & ((1 << compressed_size_shift) - 1);
|
|
||||||
let nsectors = (l2_entry >> compressed_size_shift & compressed_size_mask) + 1;
|
|
||||||
let compressed_cluster_size = ((nsectors * COMPRESSED_SECTOR_SIZE)
|
|
||||||
- (compressed_cluster_addr & (COMPRESSED_SECTOR_SIZE - 1)))
|
|
||||||
as usize;
|
|
||||||
(compressed_cluster_addr, compressed_cluster_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get file offset of standard (non-compressed) cluster
|
|
||||||
fn l2_entry_std_cluster_addr(l2_entry: u64) -> u64 {
|
|
||||||
l2_entry & L2_TABLE_OFFSET_MASK
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make L2 entry for standard (non-compressed) cluster
|
|
||||||
fn l2_entry_make_std(cluster_addr: u64) -> u64 {
|
|
||||||
(cluster_addr & L2_TABLE_OFFSET_MASK) | CLUSTER_USED_FLAG
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make L2 entry for preallocated zero cluster
|
|
||||||
fn l2_entry_make_zero(cluster_addr: u64) -> u64 {
|
|
||||||
(cluster_addr & L2_TABLE_OFFSET_MASK) | CLUSTER_USED_FLAG | ZERO_FLAG
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make L1 entry with optional flags
|
|
||||||
fn l1_entry_make(cluster_addr: u64, refcount_is_one: bool) -> u64 {
|
|
||||||
(cluster_addr & L1_TABLE_OFFSET_MASK) | (refcount_is_one as u64 * CLUSTER_USED_FLAG)
|
|
||||||
}
|
|
||||||
|
|
||||||
trait BackingFileOps: Send + Seek + Read {
|
trait BackingFileOps: Send + Seek + Read {
|
||||||
fn read_at(&mut self, address: u64, buf: &mut [u8]) -> std::io::Result<()> {
|
fn read_at(&mut self, address: u64, buf: &mut [u8]) -> std::io::Result<()> {
|
||||||
self.seek(SeekFrom::Start(address))?;
|
self.seek(SeekFrom::Start(address))?;
|
||||||
@@ -2008,16 +1958,6 @@ impl BlockBackend for QcowFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ceiling of the division of `dividend`/`divisor`.
|
|
||||||
fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
|
|
||||||
dividend / divisor + u64::from(!dividend.is_multiple_of(divisor))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ceiling of the division of `dividend`/`divisor`.
|
|
||||||
fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
|
|
||||||
dividend / divisor + u32::from(!dividend.is_multiple_of(divisor))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_copy<R, W>(reader: &mut R, writer: &mut W, offset: u64, size: u64) -> Result<()>
|
fn convert_copy<R, W>(reader: &mut R, writer: &mut W, offset: u64, size: u64) -> Result<()>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
@@ -2153,6 +2093,7 @@ mod unit_tests {
|
|||||||
use vmm_sys_util::tempfile::TempFile;
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
use vmm_sys_util::write_zeroes::WriteZeroes;
|
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||||
|
|
||||||
|
use super::util::{COMPRESSED_FLAG, ZERO_FLAG};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn valid_header_v3() -> Vec<u8> {
|
fn valid_header_v3() -> Vec<u8> {
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
//
|
||||||
|
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
|
//! Pure helper functions and constants for QCOW2 L1/L2 table entry
|
||||||
|
//! manipulation and integer arithmetic. Shared across the `qcow` submodules.
|
||||||
|
|
||||||
|
/// Nesting depth limit for disk formats that can open other disk files.
|
||||||
|
pub(crate) const MAX_NESTING_DEPTH: u32 = 10;
|
||||||
|
|
||||||
|
// bits 0-8 and 56-63 are reserved.
|
||||||
|
pub(super) const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||||
|
pub(super) const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||||
|
// Flags
|
||||||
|
pub(super) const ZERO_FLAG: u64 = 1 << 0;
|
||||||
|
pub(super) const COMPRESSED_FLAG: u64 = 1 << 62;
|
||||||
|
pub(super) const COMPRESSED_SECTOR_SIZE: u64 = 512;
|
||||||
|
pub(super) const CLUSTER_USED_FLAG: u64 = 1 << 63;
|
||||||
|
|
||||||
|
/// Check if L2 entry is empty (unallocated).
|
||||||
|
pub(super) fn l2_entry_is_empty(l2_entry: u64) -> bool {
|
||||||
|
l2_entry == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check bit 0 - only valid for standard clusters.
|
||||||
|
pub(super) fn l2_entry_is_zero(l2_entry: u64) -> bool {
|
||||||
|
l2_entry & ZERO_FLAG != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if L2 entry refers to a compressed cluster.
|
||||||
|
pub(super) fn l2_entry_is_compressed(l2_entry: u64) -> bool {
|
||||||
|
l2_entry & COMPRESSED_FLAG != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get file offset and size of compressed cluster data.
|
||||||
|
pub(super) fn l2_entry_compressed_cluster_layout(l2_entry: u64, cluster_bits: u32) -> (u64, usize) {
|
||||||
|
let compressed_size_shift = 62 - (cluster_bits - 8);
|
||||||
|
let compressed_size_mask = (1 << (cluster_bits - 8)) - 1;
|
||||||
|
let compressed_cluster_addr = l2_entry & ((1 << compressed_size_shift) - 1);
|
||||||
|
let nsectors = (l2_entry >> compressed_size_shift & compressed_size_mask) + 1;
|
||||||
|
let compressed_cluster_size = ((nsectors * COMPRESSED_SECTOR_SIZE)
|
||||||
|
- (compressed_cluster_addr & (COMPRESSED_SECTOR_SIZE - 1)))
|
||||||
|
as usize;
|
||||||
|
(compressed_cluster_addr, compressed_cluster_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get file offset of standard (non-compressed) cluster.
|
||||||
|
pub(super) fn l2_entry_std_cluster_addr(l2_entry: u64) -> u64 {
|
||||||
|
l2_entry & L2_TABLE_OFFSET_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make L2 entry for standard (non-compressed) cluster.
|
||||||
|
pub(super) fn l2_entry_make_std(cluster_addr: u64) -> u64 {
|
||||||
|
(cluster_addr & L2_TABLE_OFFSET_MASK) | CLUSTER_USED_FLAG
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make L2 entry for preallocated zero cluster.
|
||||||
|
pub(super) fn l2_entry_make_zero(cluster_addr: u64) -> u64 {
|
||||||
|
(cluster_addr & L2_TABLE_OFFSET_MASK) | CLUSTER_USED_FLAG | ZERO_FLAG
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make L1 entry with optional flags.
|
||||||
|
pub(super) fn l1_entry_make(cluster_addr: u64, refcount_is_one: bool) -> u64 {
|
||||||
|
(cluster_addr & L1_TABLE_OFFSET_MASK) | (refcount_is_one as u64 * CLUSTER_USED_FLAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ceiling of the division of `dividend`/`divisor`.
|
||||||
|
pub(super) fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
|
||||||
|
dividend / divisor + u32::from(!dividend.is_multiple_of(divisor))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ceiling of the division of `dividend`/`divisor`.
|
||||||
|
pub(super) fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
|
||||||
|
dividend / divisor + u64::from(!dividend.is_multiple_of(divisor))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user