Newer
Older
vvd / src / vdisk / qed.h
/**
 * QED: QEMU Enhanced Disk
 * 
 * Little-endian format. Features clusters, including the header (cluster0).
 *
 * Layout:
 * +--------+----------+----------+----------+-----+
 * | Header | L1 table | Cluster0 | Cluster1 | ... |
 * +--------+----------+----------+----------+-----+
 *
 * For cluster allocation, there is a 2-level table:
 * 
 *            +----------+
 *            | L1 table |            <- Fixed
 *            +----------+
 *                 |
 *       +---------+---------+
 *       |         |         |
 * +----------+ +-----+ +----------+
 * | L2 table | | ... | | L2 table |  <- Allocated on-demand
 * +----------+ +-----+ +----------+
 *                 |
 *          +------+------+
 *          |      |      |
 *     +------+ +-----+ +------+
 *     | Data | | ... | | Data |      <- Cluster data
 *     +------+ +-----+ +------+
 *
 * Both the L1 table and L2 tables are of the same size (table_size * cluster_size).
 * The L1 table holds absolute file offsets to L2 table clusters, which the
 * L2 table holds absolute file offsets to data clusters.
 * 
 * https://wiki.qemu.org/Features/QED/Specification
 * https://github.com/qemu/qemu/blob/master/docs/interop/qed_spec.txt
 */

#include <stdint.h>

static const uint32_t QED_CLUSTER_DEFAULT	= 64 * 1024; // 64K
static const uint32_t QED_TABLE_DEFAULT	= 4; // 4 clusters
static const uint32_t QED_CLUSTER_MIN	= 4096; // 2^12, or 4 * 1024
static const uint32_t QED_CLUSTER_MAX	= 67108864; // 2^26, or 64 * 1024 * 1024
static const uint32_t QED_TABLE_MIN	= 1;
static const uint32_t QED_TABLE_MAX	= 16;

// Disk image uses a backup file for unallocated clusters
static const uint64_t QED_F_BACKING_FILE	= 1; // bit 0
// Disk image needs to be checked before use
static const uint64_t QED_F_NEED_CHECK	= 2; // bit 1
// Treat as raw
static const uint64_t QED_F_BACKING_FILE_NO_PROBE	= 4; // bit 2
// Known features
static const uint64_t QED_FEATS = QED_F_BACKING_FILE | QED_F_NEED_CHECK | QED_F_BACKING_FILE_NO_PROBE;

// QED header structure
typedef struct {
	// Magic signature
	uint32_t magic;
	// In bytes, must be a power of 2 within [2^12, 2^26].
	uint32_t cluster_size;
	// For L1/L2 tables, in clusters, must be a power of 2 within [1, 16].
	uint32_t table_size;
	// In clusters
	uint32_t header_size;
	// Format feature flags
	uint64_t features;
	// Compat feature flags
	uint64_t compat_features;
	// Self-reset feature flags
	uint64_t autoclear_features;
	// L1 table offset in bytes
	uint64_t l1_offset;
	// Disk logical capacity in bytes
	uint64_t capacity;
	// (QED_F_BACKING_FILE) Offset, in bytes from start of header, to the
	// name of the backing filename.
	uint32_t backup_name_offset;
	// (QED_F_BACKING_FILE) Size, in bytes, of the backing filename.
	uint32_t backup_name_size;
} QED_HDR;

// Deprecated
typedef struct {
	uint64_t *offsets;	// L2 table offsets to data clusters
	uint64_t offset;	// Last loaded offset
	uint32_t tablesize;	// table_size
	uint32_t entries;	/// number of entries
} QED_L2CACHE;

typedef struct {
	uint32_t tablesize;	// Calculated table size in bytes (table_size * cluster_size)
	uint32_t entries;	// Number of entries
	uint64_t mask;	// Offset mask after L1/L2 calculations
	struct {
		uint64_t *offsets;
		uint64_t mask;
		uint32_t shift;
	} L1;	// L1 table
	struct {
		uint64_t *offsets;
		uint64_t mask;
		uint32_t shift;
		uint64_t current;	// Last L2 offset loaded
	} L2;	// L2 table
} QED_INTERNALS;

typedef struct {
	QED_HDR hdr;
	QED_INTERNALS in;
} QED_META;

static const uint32_t QED_META_ALLOC = sizeof(QED_META);

struct VDISK;

int vdisk_qed_open(struct VDISK *vd, uint32_t flags, uint32_t internal);

int vdisk_qed_L2_load(struct VDISK *vd, uint64_t index);

int vdisk_qed_read_sector(struct VDISK *vd, void *buffer, uint64_t index);