diff options
Diffstat (limited to 'src/kernel/xloop_file_fmt_qcow_main.h')
-rw-r--r-- | src/kernel/xloop_file_fmt_qcow_main.h | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/src/kernel/xloop_file_fmt_qcow_main.h b/src/kernel/xloop_file_fmt_qcow_main.h new file mode 100644 index 0000000..023c679 --- /dev/null +++ b/src/kernel/xloop_file_fmt_qcow_main.h @@ -0,0 +1,646 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * xloop_file_fmt_qcow.h + * + * QCOW file format driver for the xloop device module. + * + * Ported QCOW2 implementation of the QEMU project (GPL-2.0): + * Declarations for the QCOW2 file format. + * + * The copyright (C) 2004-2006 of the original code is owned by Fabrice Bellard. + * + * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> + */ + +#ifndef _LINUX_XLOOP_FILE_FMT_QCOW_H +#define _LINUX_XLOOP_FILE_FMT_QCOW_H + +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/types.h> +#include <linux/zlib.h> +#ifdef CONFIG_ZSTD_DECOMPRESS +#include <linux/zstd.h> +#endif + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#endif + +#include "xloop_file_fmt.h" + +#ifdef CONFIG_DEBUG_DRIVER +#define ASSERT(x) \ +do { \ + if (!(x)) { \ + printk(KERN_EMERG "assertion failed %s: %d: %s\n", \ + __FILE__, __LINE__, #x); \ + BUG(); \ + } \ +} while (0) +#else +#define ASSERT(x) do { } while (0) +#endif + +#define KiB (1024) +#define MiB (1024 * 1024) + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) + +#define QCOW_CRYPT_NONE 0 +#define QCOW_CRYPT_AES 1 +#define QCOW_CRYPT_LUKS 2 + +#define QCOW_MAX_CRYPT_CLUSTERS 32 +#define QCOW_MAX_SNAPSHOTS 65536 + +/* Field widths in QCOW mean normal cluster offsets cannot reach + * 64PB; depending on cluster size, compressed clusters can have a + * smaller limit (64PB for up to 16k clusters, then ramps down to + * 512TB for 2M clusters). */ +#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) + +/* 8 MB refcount table is enough for 2 PB images at 64k cluster size + * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ +#define QCOW_MAX_REFTABLE_SIZE (8 * MiB) + +/* 32 MB L1 table is enough for 2 PB images at 64k cluster size + * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ +#define QCOW_MAX_L1_SIZE (32 * MiB) + +/* Allow for an average of 1k per snapshot table entry, should be plenty of + * space for snapshot names and IDs */ +#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS) + +/* Bitmap header extension constraints */ +#define QCOW_MAX_BITMAPS 65535 +#define QCOW_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW_MAX_BITMAPS) + +/* indicate that the refcount of the referenced cluster is exactly one. */ +#define QCOW_OFLAG_COPIED (1ULL << 63) +/* indicate that the cluster is compressed (they never have the copied flag) */ +#define QCOW_OFLAG_COMPRESSED (1ULL << 62) +/* The cluster reads as all zeros */ +#define QCOW_OFLAG_ZERO (1ULL << 0) + +#define QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER 32 + +/* The subcluster X [0..31] is allocated */ +#define QCOW_OFLAG_SUB_ALLOC(X) (1ULL << (X)) +/* The subcluster X [0..31] reads as zeroes */ +#define QCOW_OFLAG_SUB_ZERO(X) (QCOW_OFLAG_SUB_ALLOC(X) << 32) +/* Subclusters [X, Y) (0 <= X <= Y <= 32) are allocated */ +#define QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) \ + (QCOW_OFLAG_SUB_ALLOC(Y) - QCOW_OFLAG_SUB_ALLOC(X)) +/* Subclusters [X, Y) (0 <= X <= Y <= 32) read as zeroes */ +#define QCOW_OFLAG_SUB_ZERO_RANGE(X, Y) \ + (QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) << 32) +/* L2 entry bitmap with all allocation bits set */ +#define QCOW_L2_BITMAP_ALL_ALLOC (QCOW_OFLAG_SUB_ALLOC_RANGE(0, 32)) +/* L2 entry bitmap with all "read as zeroes" bits set */ +#define QCOW_L2_BITMAP_ALL_ZEROES (QCOW_OFLAG_SUB_ZERO_RANGE(0, 32)) + +/* Size of normal and extended L2 entries */ +#define QCOW_L2E_SIZE_NORMAL (sizeof(u64)) +#define QCOW_L2E_SIZE_EXTENDED (sizeof(u64) * 2) + +/* Size of L1 table entries */ +#define QCOW_L1E_SIZE (sizeof(u64)) + +/* Size of reftable entries */ +#define QCOW_REFTABLE_ENTRY_SIZE (sizeof(u64)) + +#define QCOW_MIN_CLUSTER_BITS 9 +#define QCOW_MAX_CLUSTER_BITS 21 + +/* Defined in the qcow2 spec (compressed cluster descriptor) */ +#define QCOW_COMPRESSED_SECTOR_SIZE 512U +#define QCOW_COMPRESSED_SECTOR_MASK (~(QCOW_COMPRESSED_SECTOR_SIZE - 1)) + +/* Must be at least 2 to cover COW */ +#define QCOW_MIN_L2_CACHE_SIZE 2 /* cache entries */ + +/* Must be at least 4 to cover all cases of refcount table growth */ +#define QCOW_MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ + +#define QCOW_DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB) +#define QCOW_DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */ + +#define QCOW_DEFAULT_CLUSTER_SIZE 65536 + +/* Buffer size for debugfs file buffer to display QCOW header information */ +#define QCOW_HEADER_BUF_LEN 1024 + +/* Buffer size for debugfs file buffer to receive and display offset and + * cluster offset information */ +#define QCOW_OFFSET_BUF_LEN 32 +#define QCOW_CLUSTER_BUF_LEN 256 + +struct xloop_file_fmt_qcow_header { + u32 magic; + u32 version; + u64 backing_file_offset; + u32 backing_file_size; + u32 cluster_bits; + u64 size; /* in bytes */ + u32 crypt_method; + u32 l1_size; + u64 l1_table_offset; + u64 refcount_table_offset; + u32 refcount_table_clusters; + u32 nb_snapshots; + u64 snapshots_offset; + + /* The following fields are only valid for version >= 3 */ + u64 incompatible_features; + u64 compatible_features; + u64 autoclear_features; + + u32 refcount_order; + u32 header_length; + + /* Additional fields */ + u8 compression_type; + + /* header must be a multiple of 8 */ + u8 padding[7]; +} __attribute__((packed)); + +struct xloop_file_fmt_qcow_snapshot_header { + /* header is 8 byte aligned */ + u64 l1_table_offset; + + u32 l1_size; + u16 id_str_size; + u16 name_size; + + u32 date_sec; + u32 date_nsec; + + u64 vm_clock_nsec; + + u32 vm_state_size; + + /* Size of all extra data, including QCowSnapshotExtraData if available */ + u32 extra_data_size; + /* Data beyond QCowSnapshotExtraData, if any */ + void *unknown_extra_data; +} __attribute__((packed)); + +enum { + QCOW_FEAT_TYPE_INCOMPATIBLE = 0, + QCOW_FEAT_TYPE_COMPATIBLE = 1, + QCOW_FEAT_TYPE_AUTOCLEAR = 2, +}; + +/* incompatible feature bits */ +enum { + QCOW_INCOMPAT_DIRTY_BITNR = 0, + QCOW_INCOMPAT_CORRUPT_BITNR = 1, + QCOW_INCOMPAT_DATA_FILE_BITNR = 2, + QCOW_INCOMPAT_COMPRESSION_BITNR = 3, + QCOW_INCOMPAT_EXTL2_BITNR = 4, + QCOW_INCOMPAT_DIRTY = 1 << QCOW_INCOMPAT_DIRTY_BITNR, + QCOW_INCOMPAT_CORRUPT = 1 << QCOW_INCOMPAT_CORRUPT_BITNR, + QCOW_INCOMPAT_DATA_FILE = 1 << QCOW_INCOMPAT_DATA_FILE_BITNR, + QCOW_INCOMPAT_COMPRESSION = 1 << QCOW_INCOMPAT_COMPRESSION_BITNR, + QCOW_INCOMPAT_EXTL2 = 1 << QCOW_INCOMPAT_EXTL2_BITNR, + + QCOW_INCOMPAT_MASK = QCOW_INCOMPAT_DIRTY + | QCOW_INCOMPAT_CORRUPT + | QCOW_INCOMPAT_DATA_FILE + | QCOW_INCOMPAT_COMPRESSION + | QCOW_INCOMPAT_EXTL2, +}; + +/* compatible feature bits */ +enum { + QCOW_COMPAT_LAZY_REFCOUNTS_BITNR = 0, + QCOW_COMPAT_LAZY_REFCOUNTS = 1 << QCOW_COMPAT_LAZY_REFCOUNTS_BITNR, + + QCOW_COMPAT_FEAT_MASK = QCOW_COMPAT_LAZY_REFCOUNTS, +}; + +/* autoclear feature bits */ +enum { + QCOW_AUTOCLEAR_BITMAPS_BITNR = 0, + QCOW_AUTOCLEAR_DATA_FILE_RAW_BITNR = 1, + QCOW_AUTOCLEAR_BITMAPS = 1 << QCOW_AUTOCLEAR_BITMAPS_BITNR, + QCOW_AUTOCLEAR_DATA_FILE_RAW = 1 << QCOW_AUTOCLEAR_DATA_FILE_RAW_BITNR, + + QCOW_AUTOCLEAR_MASK = QCOW_AUTOCLEAR_BITMAPS | + QCOW_AUTOCLEAR_DATA_FILE_RAW, +}; + +enum xloop_file_fmt_qcow_compression_type { + QCOW_COMPRESSION_TYPE_ZLIB, + QCOW_COMPRESSION_TYPE_ZSTD, +}; + +struct xloop_file_fmt_qcow_data { + u64 size; + int cluster_bits; + int cluster_size; + int l2_slice_size; + int subcluster_bits; + int subcluster_size; + int subclusters_per_cluster; + int l2_bits; + int l2_size; + int l1_size; + int l1_vm_state_index; + int refcount_block_bits; + int refcount_block_size; + int csize_shift; + int csize_mask; + u64 cluster_offset_mask; + u64 l1_table_offset; + u64 *l1_table; + + struct xloop_file_fmt_qcow_cache *l2_table_cache; + struct xloop_file_fmt_qcow_cache *refcount_block_cache; + + u64 *refcount_table; + u64 refcount_table_offset; + u32 refcount_table_size; + u32 max_refcount_table_index; /* Last used entry in refcount_table */ + u64 free_cluster_index; + u64 free_byte_offset; + + u32 crypt_method_header; + u64 snapshots_offset; + int snapshots_size; + unsigned int nb_snapshots; + + u32 nb_bitmaps; + u64 bitmap_directory_size; + u64 bitmap_directory_offset; + + int qcow_version; + bool use_lazy_refcounts; + int refcount_order; + int refcount_bits; + u64 refcount_max; + + u64 incompatible_features; + u64 compatible_features; + u64 autoclear_features; + + /* ZLIB specific data */ + z_streamp zlib_dstrm; + + /* ZSTD specific data */ +#ifdef CONFIG_ZSTD_DECOMPRESS + void *zstd_dworkspace; + ZSTD_DStream *zstd_dstrm; +#endif + + /* used to cache last compressed QCOW cluster */ + u8 *cmp_out_buf; + u64 cmp_last_coffset; + + /* + * Compression type used for the image. Default: 0 - ZLIB + * The image compression type is set on image creation. + * For now, the only way to change the compression type + * is to convert the image with the desired compression type set. + */ + enum xloop_file_fmt_qcow_compression_type compression_type; + + /* debugfs entries */ +#ifdef CONFIG_DEBUG_FS + struct dentry *dbgfs_dir; + struct dentry *dbgfs_file_qcow_header; + char dbgfs_file_qcow_header_buf[QCOW_HEADER_BUF_LEN]; + struct dentry *dbgfs_file_qcow_offset; + char dbgfs_file_qcow_offset_buf[QCOW_OFFSET_BUF_LEN]; + char dbgfs_file_qcow_cluster_buf[QCOW_CLUSTER_BUF_LEN]; + u64 dbgfs_qcow_offset; + struct mutex dbgfs_qcow_offset_mutex; +#endif +}; + +struct xloop_file_fmt_qcow_cow_region { + /** + * Offset of the COW region in bytes from the start of the first + * cluster touched by the request. + */ + unsigned offset; + + /** Number of bytes to copy */ + unsigned nb_bytes; +}; + +/* + * In images with standard L2 entries all clusters are treated as if + * they had one subcluster so xloop_file_fmt_qcow_cluster_type and + * xloop_file_fmt_qcow_subcluster_type can be mapped to each other and + * have the exact same meaning (QCOW_SUBCLUSTER_UNALLOCATED_ALLOC cannot + * happen in these images). + * + * In images with extended L2 entries xloop_file_fmt_qcow_cluster_type + * refers to the complete cluster and xloop_file_fmt_qcow_subcluster_type + * to each of the individual subclusters, so there are several possible + * combinations: + * + * |--------------+---------------------------| + * | Cluster type | Possible subcluster types | + * |--------------+---------------------------| + * | UNALLOCATED | UNALLOCATED_PLAIN | + * | | ZERO_PLAIN | + * |--------------+---------------------------| + * | NORMAL | UNALLOCATED_ALLOC | + * | | ZERO_ALLOC | + * | | NORMAL | + * |--------------+---------------------------| + * | COMPRESSED | COMPRESSED | + * |--------------+---------------------------| + * + * QCOW_SUBCLUSTER_INVALID means that the L2 entry is incorrect and + * the image should be marked corrupt. + */ +enum xloop_file_fmt_qcow_cluster_type { + QCOW_CLUSTER_UNALLOCATED, + QCOW_CLUSTER_ZERO_PLAIN, + QCOW_CLUSTER_ZERO_ALLOC, + QCOW_CLUSTER_NORMAL, + QCOW_CLUSTER_COMPRESSED, +}; + +enum xloop_file_fmt_qcow_subcluster_type { + QCOW_SUBCLUSTER_UNALLOCATED_PLAIN, + QCOW_SUBCLUSTER_UNALLOCATED_ALLOC, + QCOW_SUBCLUSTER_ZERO_PLAIN, + QCOW_SUBCLUSTER_ZERO_ALLOC, + QCOW_SUBCLUSTER_NORMAL, + QCOW_SUBCLUSTER_COMPRESSED, + QCOW_SUBCLUSTER_INVALID, +}; + +enum xloop_file_fmt_qcow_metadata_overlap { + QCOW_OL_MAIN_HEADER_BITNR = 0, + QCOW_OL_ACTIVE_L1_BITNR = 1, + QCOW_OL_ACTIVE_L2_BITNR = 2, + QCOW_OL_REFCOUNT_TABLE_BITNR = 3, + QCOW_OL_REFCOUNT_BLOCK_BITNR = 4, + QCOW_OL_SNAPSHOT_TABLE_BITNR = 5, + QCOW_OL_INACTIVE_L1_BITNR = 6, + QCOW_OL_INACTIVE_L2_BITNR = 7, + QCOW_OL_BITMAP_DIRECTORY_BITNR = 8, + + QCOW_OL_MAX_BITNR = 9, + + QCOW_OL_NONE = 0, + QCOW_OL_MAIN_HEADER = (1 << QCOW_OL_MAIN_HEADER_BITNR), + QCOW_OL_ACTIVE_L1 = (1 << QCOW_OL_ACTIVE_L1_BITNR), + QCOW_OL_ACTIVE_L2 = (1 << QCOW_OL_ACTIVE_L2_BITNR), + QCOW_OL_REFCOUNT_TABLE = (1 << QCOW_OL_REFCOUNT_TABLE_BITNR), + QCOW_OL_REFCOUNT_BLOCK = (1 << QCOW_OL_REFCOUNT_BLOCK_BITNR), + QCOW_OL_SNAPSHOT_TABLE = (1 << QCOW_OL_SNAPSHOT_TABLE_BITNR), + QCOW_OL_INACTIVE_L1 = (1 << QCOW_OL_INACTIVE_L1_BITNR), + /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv + * reads. */ + QCOW_OL_INACTIVE_L2 = (1 << QCOW_OL_INACTIVE_L2_BITNR), + QCOW_OL_BITMAP_DIRECTORY = (1 << QCOW_OL_BITMAP_DIRECTORY_BITNR), +}; + +/* Perform all overlap checks which can be done in constant time */ +#define QCOW_OL_CONSTANT \ + (QCOW_OL_MAIN_HEADER | QCOW_OL_ACTIVE_L1 | QCOW_OL_REFCOUNT_TABLE | \ + QCOW_OL_SNAPSHOT_TABLE | QCOW_OL_BITMAP_DIRECTORY) + +/* Perform all overlap checks which don't require disk access */ +#define QCOW_OL_CACHED \ + (QCOW_OL_CONSTANT | QCOW_OL_ACTIVE_L2 | QCOW_OL_REFCOUNT_BLOCK | \ + QCOW_OL_INACTIVE_L1) + +/* Perform all overlap checks */ +#define QCOW_OL_ALL \ + (QCOW_OL_CACHED | QCOW_OL_INACTIVE_L2) + +#define QCOW_L1E_OFFSET_MASK 0x00fffffffffffe00ULL +#define QCOW_L2E_OFFSET_MASK 0x00fffffffffffe00ULL +#define QCOW_L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL + +static inline bool xloop_file_fmt_qcow_has_subclusters( + struct xloop_file_fmt_qcow_data *qcow_data) +{ + return qcow_data->incompatible_features & QCOW_INCOMPAT_EXTL2; +} + +static inline size_t xloop_file_fmt_qcow_l2_entry_size( + struct xloop_file_fmt_qcow_data *qcow_data) +{ + return xloop_file_fmt_qcow_has_subclusters(qcow_data) ? + QCOW_L2E_SIZE_EXTENDED : QCOW_L2E_SIZE_NORMAL; +} + +static inline u64 xloop_file_fmt_qcow_get_l2_entry( + struct xloop_file_fmt_qcow_data *qcow_data, u64 *l2_slice, int idx) +{ + idx *= xloop_file_fmt_qcow_l2_entry_size(qcow_data) / sizeof(u64); + return be64_to_cpu(l2_slice[idx]); +} + +static inline u64 xloop_file_fmt_qcow_get_l2_bitmap( + struct xloop_file_fmt_qcow_data *qcow_data, u64 *l2_slice, int idx) +{ + if (xloop_file_fmt_qcow_has_subclusters(qcow_data)) { + idx *= xloop_file_fmt_qcow_l2_entry_size(qcow_data) / sizeof(u64); + return be64_to_cpu(l2_slice[idx + 1]); + } else { + return 0; /* For convenience only; this value has no meaning. */ + } +} + +static inline bool xloop_file_fmt_qcow_has_data_file( + struct xloop_file_fmt_qcow_data *qcow_data) +{ + /* At the moment, there is no support for copy on write! */ + return false; +} + +static inline bool xloop_file_fmt_qcow_data_file_is_raw( + struct xloop_file_fmt_qcow_data *qcow_data) +{ + return !!(qcow_data->autoclear_features & + QCOW_AUTOCLEAR_DATA_FILE_RAW); +} + +static inline s64 xloop_file_fmt_qcow_start_of_cluster( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) +{ + return offset & ~(qcow_data->cluster_size - 1); +} + +static inline s64 xloop_file_fmt_qcow_offset_into_cluster( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) +{ + return offset & (qcow_data->cluster_size - 1); +} + +static inline s64 xloop_file_fmt_qcow_offset_into_subcluster( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) +{ + return offset & (qcow_data->subcluster_size - 1); +} + +static inline s64 xloop_file_fmt_qcow_size_to_clusters( + struct xloop_file_fmt_qcow_data *qcow_data, u64 size) +{ + return (size + (qcow_data->cluster_size - 1)) >> + qcow_data->cluster_bits; +} + +static inline s64 xloop_file_fmt_qcow_size_to_l1( + struct xloop_file_fmt_qcow_data *qcow_data, s64 size) +{ + int shift = qcow_data->cluster_bits + qcow_data->l2_bits; + return (size + (1ULL << shift) - 1) >> shift; +} + +static inline int xloop_file_fmt_qcow_offset_to_l1_index( + struct xloop_file_fmt_qcow_data *qcow_data, u64 offset) +{ + return offset >> (qcow_data->l2_bits + qcow_data->cluster_bits); +} + +static inline int xloop_file_fmt_qcow_offset_to_l2_index( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) +{ + return (offset >> qcow_data->cluster_bits) & (qcow_data->l2_size - 1); +} + +static inline int xloop_file_fmt_qcow_offset_to_l2_slice_index( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) +{ + return (offset >> qcow_data->cluster_bits) & + (qcow_data->l2_slice_size - 1); +} + +static inline int xloop_file_fmt_qcow_offset_to_sc_index( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) +{ + return (offset >> qcow_data->subcluster_bits) & + (qcow_data->subclusters_per_cluster - 1); +} + +static inline s64 xloop_file_fmt_qcow_vm_state_offset( + struct xloop_file_fmt_qcow_data *qcow_data) +{ + return (s64)qcow_data->l1_vm_state_index << + (qcow_data->cluster_bits + qcow_data->l2_bits); +} + +static inline enum xloop_file_fmt_qcow_cluster_type +xloop_file_fmt_qcow_get_cluster_type(struct xloop_file_fmt *xlo_fmt, + u64 l2_entry) +{ + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + + if (l2_entry & QCOW_OFLAG_COMPRESSED) { + return QCOW_CLUSTER_COMPRESSED; + } else if (l2_entry & QCOW_OFLAG_ZERO) { + if (l2_entry & QCOW_L2E_OFFSET_MASK) { + return QCOW_CLUSTER_ZERO_ALLOC; + } + return QCOW_CLUSTER_ZERO_PLAIN; + } else if (!(l2_entry & QCOW_L2E_OFFSET_MASK)) { + /* Offset 0 generally means unallocated, but it is ambiguous + * with external data files because 0 is a valid offset there. + * However, all clusters in external data files always have + * refcount 1, so we can rely on QCOW_OFLAG_COPIED to + * disambiguate. */ + if (xloop_file_fmt_qcow_has_data_file(qcow_data) && + (l2_entry & QCOW_OFLAG_COPIED)) { + return QCOW_CLUSTER_NORMAL; + } else { + return QCOW_CLUSTER_UNALLOCATED; + } + } else { + return QCOW_CLUSTER_NORMAL; + } +} + +/* + * In an image without subsclusters @l2_bitmap is ignored and + * @sc_index must be 0. + * Return QCOW_SUBCLUSTER_INVALID if an invalid l2 entry is detected + * (this checks the whole entry and bitmap, not only the bits related + * to subcluster @sc_index). + */ +static inline enum xloop_file_fmt_qcow_subcluster_type +xloop_file_fmt_qcow_get_subcluster_type(struct xloop_file_fmt *xlo_fmt, + u64 l2_entry, u64 l2_bitmap, unsigned int sc_index) +{ + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + enum xloop_file_fmt_qcow_cluster_type type = + xloop_file_fmt_qcow_get_cluster_type(xlo_fmt, l2_entry); + ASSERT(sc_index < qcow_data->subclusters_per_cluster); + + if (xloop_file_fmt_qcow_has_subclusters(qcow_data)) { + switch (type) { + case QCOW_CLUSTER_COMPRESSED: + return QCOW_SUBCLUSTER_COMPRESSED; + case QCOW_CLUSTER_NORMAL: + if ((l2_bitmap >> 32) & l2_bitmap) { + return QCOW_SUBCLUSTER_INVALID; + } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { + return QCOW_SUBCLUSTER_ZERO_ALLOC; + } else if (l2_bitmap & QCOW_OFLAG_SUB_ALLOC(sc_index)) { + return QCOW_SUBCLUSTER_NORMAL; + } else { + return QCOW_SUBCLUSTER_UNALLOCATED_ALLOC; + } + case QCOW_CLUSTER_UNALLOCATED: + if (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC) { + return QCOW_SUBCLUSTER_INVALID; + } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { + return QCOW_SUBCLUSTER_ZERO_PLAIN; + } else { + return QCOW_SUBCLUSTER_UNALLOCATED_PLAIN; + } + default: + /* not reachable */ + ASSERT(false); + return QCOW_SUBCLUSTER_INVALID; + } + } else { + switch (type) { + case QCOW_CLUSTER_COMPRESSED: + return QCOW_SUBCLUSTER_COMPRESSED; + case QCOW_CLUSTER_ZERO_PLAIN: + return QCOW_SUBCLUSTER_ZERO_PLAIN; + case QCOW_CLUSTER_ZERO_ALLOC: + return QCOW_SUBCLUSTER_ZERO_ALLOC; + case QCOW_CLUSTER_NORMAL: + return QCOW_SUBCLUSTER_NORMAL; + case QCOW_CLUSTER_UNALLOCATED: + return QCOW_SUBCLUSTER_UNALLOCATED_PLAIN; + default: + /* not reachable */ + ASSERT(false); + return QCOW_SUBCLUSTER_INVALID; + } + } +} + +#ifdef CONFIG_DEBUG_FS +static inline const char *xloop_file_fmt_qcow_get_subcluster_name( + const enum xloop_file_fmt_qcow_subcluster_type type) +{ + static const char *subcluster_names[] = { + "QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN", + "QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC", + "QCOW2_SUBCLUSTER_ZERO_PLAIN", + "QCOW2_SUBCLUSTER_ZERO_ALLOC", + "QCOW2_SUBCLUSTER_NORMAL", + "QCOW2_SUBCLUSTER_COMPRESSED", + "QCOW2_SUBCLUSTER_INVALID" + }; + + return subcluster_names[type]; +} +#endif + +#endif |