/* 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];
} __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;
} __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;
int cmp_last_size;
/*
* 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 int offset;
/** Number of bytes to copy */
unsigned int 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 * const 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