Newer
Older
vvd / src / vdisk.h
@dd86k dd86k on 25 Sep 2020 8 KB misc.
#pragma once

#include "os.h"
#include "utils.h"
#include "vdisk/raw.h"
#include "vdisk/vdi.h"
#include "vdisk/vmdk.h"
#include "vdisk/vhd.h"
#include "vdisk/vhdx.h"
#include "vdisk/qed.h"
#include "vdisk/qcow.h"
#include "vdisk/phdd.h"

#define LINE_BEFORE (__LINE__ - 1)

//
// Constants
//

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
enum {	// DISKFORMAT magical hints (LSB), used for VDISK.format
	VDISK_FORMAT_NONE	= 0,	// No formats has been specificied yet
	VDISK_FORMAT_RAW	= 0xAAAAAAAA,	// Raw files and devices
	VDISK_FORMAT_VDI	= 0x203C3C3C,	// "<<< " VirtualBox
	VDISK_FORMAT_VMDK	= 0x564D444B,	// "VMDK" VMware
	VDISK_FORMAT_VMDK_COW	= 0x44574F43,	// "COWD" VMware EXSi COW disk
	VDISK_FORMAT_VHD	= 0x656E6F63,	// "cone" VirtualPC/Hyper-V
	VDISK_FORMAT_VHDX	= 0x78646876,	// "vhdx" Hyper-V
	VDISK_FORMAT_QED	= 0x00444551,	// "QED\0" QEMU Enhanced Disk
	VDISK_FORMAT_QCOW	= 0xFB494651,	// "QFI\xFB" QEMU Copy-On-Write, v1/v2
	VDISK_FORMAT_PHDD	= 0x68746957,	// "With" Parallels HDD
	VDISK_FORMAT_BOCHS	= 0x68636F68,	// "Boch" Bochs Virtual HD Image
//	VDISK_FORMAT_DMG	= 0x,	// "" Apple DMG
};
#else

#endif

enum {	// VDISK flags, the open/create flags may overlap
	VDISK_RAW	= 0x1,	// Open or create vdisk as raw

	//
	// vdisk_open flags
	//

	VDISK_OPEN_VDI_ONLY	= 0x1000,	//TODO: Only open successfully if VDISK is VDI
	VDISK_OPEN_VMDK_ONLY	= 0x2000,	//TODO: Only open successfully if VDISK is VMDK
	VDISK_OPEN_VHD_ONLY	= 0x3000,	//TODO: Only open successfully if VDISK is VHD
	VDISK_OPEN_VHDX_ONLY	= 0x4000,	//TODO: Only open successfully if VDISK is VHDX
	VDISK_OPEN_QED_ONLY	= 0x5000,	//TODO: Only open successfully if VDISK is QED
	VDISK_OPEN_QCOW_ONLY	= 0x6000,	//TODO: Only open successfully if VDISK is QCOW
	VDISK_OPEN_PHDD_ONLY	= 0x7000,	//TODO: Only open successfully if VDISK is Parallels HDD
	VDISK_OPEN_BOCHS_ONLY	= 0x8000,	//TODO: Only open successfully if VDISK is Parallels HDD

	//
	// vdisk_create flags
	//

	VDISK_CREATE_TEMP	= 0x0100,	//TODO: Create a temporary (random) vdisk file
	VDISK_CREATE_DYN	= 0x1000,	//TODO: Create a dynamic type VDISK
	VDISK_CREATE_FIXED	= 0x2000,	//TODO: Create a fixed type VDISK
	VDISK_CREATE_PARENT	= 0x3000,	//TODO: Create a parent of the VDISK
	VDISK_CREATE_SNAPSHOT	= 0x4000,	//TODO: Create a snapshot of the VDISK
};

enum {	// VDISK error codes
	VVD_EOK	= 0,	// VDISK OK
	VVD_EOS	= -2,	// OS/CRT related error
	VVD_ENULL	= -3,	// Input pointer is NULL
	VVD_ENOMEM	= -4,	// Could not allocate memory
	VVD_EVDFORMAT	= -10,	// Invalid VDISK format
	VVD_EVDMAGIC	= -11,	// Invalid VDISK magic signature
	VVD_EVDVERSION	= -12,	// Unsupported VDISK version (major)
	VVD_EVDTYPE	= -13,	// Unsupported VDISK type
	VVD_EVDFULL	= -14,	// VDISK is full and no more data can be allocated
	VVD_EVDUNALLOC	= -15,	// Block is unallocated
	VVD_EVDBOUND	= -16,	// Index was out of block index bounds
	VVD_EVDTODO	= -254,	// Currently unimplemented
	VVD_EVDMISC	= -255,	// Unknown
};

enum {
	// Operation has completed successfully.
	// Parameter: NULL
	VVD_NOTIF_DONE,
	// VDISK was created with type
	// Parameter: const char*
	VVD_NOTIF_VDISK_CREATED_TYPE_NAME,
	// Total amount of blocks before processing
	// Parameter: uint32_t
	VVD_NOTIF_VDISK_TOTAL_BLOCKS,
	// Total amount of blocks before processing (64-bit indexes)
	// Parameter: uint64_t
	VVD_NOTIF_VDISK_TOTAL_BLOCKS64,
	// 
	// Parameter: uint32_t
	VVD_NOTIF_VDISK_CURRENT_BLOCK,
	// 
	// Parameter: uint64_t
	VVD_NOTIF_VDISK_CURRENT_BLOCK64,
};

//
// Structure definitions
//

// Defines a virtual disk.
// All fields are more or less internal.
typedef struct VDISK {
	// Defines the virtual disk format (e.g. VDI, VMDK, etc.).
	// See VDISKFORMAT enumeration.
	uint32_t format;
	// Flags. See VDISK_FLAG enumeration.
	uint32_t flags;
	// Calculated absolute offset to data.
	uint64_t offset;
	// (Internal) Location of new allocation block
	uint64_t nextblock;
	// Virtual disk capacity in bytes. For RAW files, it's the file size. For
	// RAW devices, it's the disk size.
	uint64_t capacity;
	// (Posix) File descriptor (Windows) File HANDLE
	__OSFILE fd;
	int errcode;	// Error number
	int errline;	// Error line
	const char *errfunc;	// Function name
	// Function pointer: Read a sector with a LBA index
	int (*read_lba)(struct VDISK*, void*, uint64_t);
	// Function pointer: Read a dynamic block with a block index
	int (*read_block)(struct VDISK*, void*, uint64_t);
	// Function pointer: Read a sector with a LBA index
	int (*write_lba)(struct VDISK*, void*, uint64_t);
	// Function pointer: Read a sector with a LBA index
	int (*write_block)(struct VDISK*, void*, uint64_t);
	union {
		// Allocation table using 64-bit indexes
		uint64_t *u64block;
		// Allocation table using 32-bit indexes
		uint32_t *u32block;
	};
	union {
		// Total amount of allocated blocks
		uint64_t u64blockcount;
		// Total amount of allocated blocks
		uint32_t u32blockcount;
	};
	union {
		uint64_t blockmask64;
		uint32_t blockmask;
	};
	uint32_t blockshift;
	//TODO: Consider allocating on open/create operations
	union {
		struct {
			VDI_HDR vdihdr; // pre-header
			VDIHEADER0 vdiv0;
			VDIHEADER1 vdiv1;
			// Dynamic disk block mask for remaining offset to LBA.
			uint32_t vdi_blockmask;
			// Dynamic disk block index shift number. Populated by fpow2.
			uint32_t vdi_blockshift;
		};
		struct {
			VMDK_HDR vmdk;
			uint64_t vmdk_blockmask;
			uint32_t vmdk_blockshift;
		};
		struct {
			VHD_HDR vhd;
			VHD_DYN_HDR vhddyn;
			uint32_t vhd_blockmask;
			uint32_t vhd_blockshift;
		};
		struct {
			VHDX_HDR vhdx;
			VHDX_HEADER1 vhdxhdr;
			VHDX_REGION_HDR vhdxreg;
		};
		struct {
			QED_HDR qed;
			uint64_t *qed_L1; // L1 table
			QED_L2CACHE qed_L2;
			uint64_t qed_offset_mask;
			uint64_t qed_L2_mask;
			uint32_t qed_L2_shift;
			uint64_t qed_L1_mask;
			uint32_t qed_L1_shift;
		};
		QCOW_HDR qcow;
		PHDD_HDR phdd;
	};
} VDISK;

//
// SECTION Internal functions
//

/**
 * (Internal) Set errcode and errline.
 * 
 * \returns errcode
 */
int vdisk_i_err(VDISK *vd, int e, int l);

//
// SECTION Functions
//

/**
 * Open a VDISK.
 * 
 * When opening a file, this function verifies the file path, VDISK format,
 * header structure, version, and other fields.
 * 
 * When creating a file, the specified file at the file path is overwritten.
 * An empty, unallocated VDISK is created. If VDISK_CREATE_TEMP is defined,
 * path parameter can be NULL, since the function will create a random
 * filename (OS).
 * 
 * \param vd VDISK structure
 * \param path OS string path
 * \param flags Opening flags
 * 
 * \returns Exit status
 */
int vdisk_open(VDISK *vd, const oschar *path, uint32_t flags);

/**
 * Create a VDISK.
 * 
 * \param vd VDISK structure
 * \param path OS string path
 * \param format Virtual disk format
 * \param capacity Virtual disk capacity
 * \param flags Creation flags
 * 
 * \returns Exit status
 */
int vdisk_create(VDISK *vd, const oschar *path, int format, uint64_t capacity, uint16_t flags);

/**
 * Returns a string representation of the loaded virtual disk. If a format was
 * not found, a null pointer is returned.
 */
const char *vdisk_str(VDISK *vd);

/**
 * Update header information and allocation tables into file or device.
 */
int vdisk_update(VDISK *vd);

/**
 * Seek and read a sector-size (512 bytes) of data from a sector index (LBA).
 * 
 * This function checks if sector exists on dynamic type disks, and index
 * tables such as the BAT on VHDs.
 * 
 * Returns error code. Non-zero being an error.
 */
int vdisk_read_sector(VDISK *vd, void *buffer, uint64_t lba);

/**
 * Seek to a block index and read it. The size of the block depends on the size
 * speicified in the VDISK structure. Only certain VDISK types are supported,
 * notably dynamic types. If unsupported, returns EVDFORMAT or EVDTYPE.
 */
int vdisk_read_block(VDISK *vd, void *buffer, uint64_t index);

/**
 * 
 */
int vdisk_write_lba(VDISK *vd, void *buffer, uint64_t lba);

/**
 * 
 */
int vdisk_write_block(VDISK *vd, void *buffer, uint64_t index);

/**
 * 
 */
int vdisk_write_block_at(VDISK *vd, void *buffer, uint64_t bindex, uint64_t dindex);

//
// SECTION
//

/**
 * 
 */
int vdisk_op_compact(VDISK *vd, void(*cb)(uint32_t, void*));

/**
 * 
 */
//int vdisk_op_resize(VDISK *vd, void(*cb_progress)(uint64_t block));

//
// SECTION Error handling
//

/**
 * Returns an error message depending on the last value of vdisk_errno. If the
 * error is set to VVD_EOS, the error message will come from the OS (or CRT).
 */
const char* vdisk_error(VDISK *vd);

/**
 * Print to stdout, with the name of the function, a message with the last
 * value set to vdisk_errno.
 */
void vdisk_perror(VDISK *vd);