Newer
Older
vvd / src / vdisk / vdi.c
#include <string.h> // memcpy
#include "../vdisk.h"
#include "../utils.h"
#include "../platform.h"
#ifdef TRACE
#include <stdio.h>
#include <inttypes.h>
#endif

//
// vdisk_vdi_open
//

int vdisk_vdi_open(VDISK *vd, uint32_t flags, uint32_t internal) {
	vd->errfunc = __func__;

	if (os_fseek(vd->fd, 64, SEEK_SET))
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);
	if (os_fread(vd->fd, &vd->vdihdr, sizeof(VDI_HDR)))
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);
	if (vd->vdihdr.magic != VDI_HEADER_MAGIC)
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);

	switch (vd->vdihdr.majorv) { // Use latest major version natively
	case 1: // Includes all minor releases
		if (os_fread(vd->fd, &vd->vdiv1, sizeof(VDIHEADER1)))
			return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);
		break;
	case 0: { // Or else, translate header
		//TODO: Don't tranlate header in case of in-file ops
		VDIHEADER0 vd0;
		if (os_fread(vd->fd, &vd0, sizeof(VDIHEADER0)))
			return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);
		vd->vdiv1.disksize = vd0.disksize;
		vd->vdiv1.type = vd0.type;
		vd->vdiv1.offBlocks = sizeof(VDI_HDR) + sizeof(VDIHEADER0);
		vd->vdiv1.offData = sizeof(VDI_HDR) + sizeof(VDIHEADER0) +
			(vd0.blockstotal << 2); // sizeof(uint32_t) -> "* 4" -> "<< 2"
		memcpy(&vd->vdiv1.uuidCreate, &vd0.uuidCreate, 16);
		memcpy(&vd->vdiv1.uuidModify, &vd0.uuidModify, 16);
		memcpy(&vd->vdiv1.uuidLinkage, &vd0.uuidLinkage, 16);
		memcpy(&vd->vdiv1.LegacyGeometry, &vd0.LegacyGeometry, sizeof(VDIDISKGEOMETRY));
		break;
	}
	default:
		return vdisk_i_err(vd, VVD_EVDVERSION, LINE_BEFORE);
	}

	switch (vd->vdiv1.type) {
	case VDI_DISK_DYN:
	case VDI_DISK_FIXED: break;
	default:
		return vdisk_i_err(vd, VVD_EVDTYPE, LINE_BEFORE);
	}

	vd->read_lba = vdisk_vdi_read_sector;

	// allocation table
	//TODO: Consider if this is an error (or warning)
	if (vd->vdiv1.blocksize == 0)
		vd->vdiv1.blocksize = VDI_BLOCKSIZE;
	if (os_fseek(vd->fd, vd->vdiv1.offBlocks, SEEK_SET))
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);
	int bsize = vd->vdiv1.blockstotal << 2; // * sizeof(u32)
	if ((vd->u32block = malloc(bsize)) == NULL)
		return vdisk_i_err(vd, VVD_EALLOC, LINE_BEFORE);
	if (os_fread(vd->fd, vd->u32block, bsize))
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);

	vd->offset = vd->vdiv1.offData;
	vd->u32blockcount = vd->vdiv1.blockstotal;
	vd->capacity = vd->vdiv1.disksize;
	vd->vdi_blockmask = vd->vdiv1.blocksize - 1;
	vd->vdi_blockshift = fpow2(vd->vdiv1.blocksize);

	return 0;
}

//
// vdisk_vdi_read_sector
//

int vdisk_vdi_read_sector(VDISK *vd, void *buffer, uint64_t index) {
	vd->errfunc = __func__;

	uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
	size_t bi = offset >> vd->vdi_blockshift;

	if (bi >= vd->vdiv1.blockstotal) // out of bounds
		return vdisk_i_err(vd, VVD_EVDBOUND, LINE_BEFORE);

	uint32_t block = vd->u32block[bi];
	switch (block) {
	case VDI_BLOCK_ZERO: //TODO: Should this be zero'd too?
		return vdisk_i_err(vd, VVD_EVDUNALLOC, LINE_BEFORE);
	case VDI_BLOCK_FREE:
		memset(buffer, 0, 512);
		return 0;
	}

	offset = vd->offset +
		((uint64_t)block * vd->vdiv1.blocksize) +
		(offset & vd->vdi_blockmask);

#ifdef TRACE
	printf("%s: lba=%" PRId64 " -> offset=0x%" PRIX64 "\n", __func__, index, offset);
#endif

	if (os_fseek(vd->fd, offset, SEEK_SET))
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);
	if (os_fread(vd->fd, buffer, 512))
		return vdisk_i_err(vd, VVD_EOS, LINE_BEFORE);

	return 0;
}

//
// vdisk_vdi_compact
//

int vdisk_vdi_compact(VDISK *vd, void(*cb)(uint32_t type, void *data)) {
	if (vd->vdiv1.type != VDI_DISK_DYN)
		return vdisk_i_err(vd, VVD_EVDTYPE, LINE_BEFORE);

	if (vd->vdiv1.blocksalloc == 0)
		return 0;

	uint32_t stat_unalloc = 0;	// unallocated blocks
	uint32_t stat_occupied = 0;	// allocated blocks with data
	uint32_t stat_zero = 0;	// blocks with no data inside
	uint32_t stat_alloc = 0;	// allocated blocks

	uint32_t *block2 = malloc(vd->vdiv1.blocksalloc << 2); // * 4
	if (block2)
		return vdisk_i_err(vd, VVD_EALLOC, LINE_BEFORE);
	for (uint32_t i; i < vd->vdiv1.blocksalloc; ++i)
		block2[i] = VDI_BLOCK_FREE;

/*	char strbsize[BINSTR_LENGTH];
	bintostr(strbsize, vd->vdiv1.blocksize);
	printf("vvd_compact: writing (%s/block, %u checks/%u bytes)...\n",
		strbsize, (uint32_t)oblocksize, (uint32_t)sizeof(size_t));*/

	uint32_t d = 0;
	uint32_t i = 0;

	// Check and fix allocation errors before compacting
	for (; i < vd->u32blockcount; ++i) {
		uint32_t bi = vd->u32block[i]; // block index
		if (bi >= VDI_BLOCK_FREE) {
			++stat_unalloc;
			continue;
		}

		if (cb) cb(VVD_NOTIF_VDISK_CURRENT_BLOCK, &i);

		if (bi < vd->vdiv1.blocksalloc) {
			if (vd->u32block[bi] == VDI_BLOCK_FREE) {
				vd->u32block[bi] = i;
			} else {
				vd->u32block[bi] = VDI_BLOCK_FREE;
				//TODO: Update header once manipulating source
				//rc = vdiUpdateBlockInfo(pImage, i);
				//vdisk_write_block_at(vd, buffer, i, d++);
			}
		} else {
			vd->u32block[bi] = VDI_BLOCK_FREE;
			//TODO: Update header once manipulating source
			//vd->u32block[bi] = VDI_BLOCK_FREE;
			//vdisk_write_block_at(vd, buffer, i, d++);
		}
	}
	// Find redundant information and update the block pointers accordingly
	for (i = 0; i < vd->u32blockcount; ++i) {
		uint32_t bi = vd->u32block[i]; // block index
		if (bi >= VDI_BLOCK_FREE) {
			++stat_unalloc;
			continue;
		}
	}
	// Fill bubbles with other data if available
//		for (i = 0; o < vd->vdi.blocksalloc

	return 0;
}