Newer
Older
vvd / src / vdisk / vdi.c
@dd86k dd86k on 9 Nov 2019 5 KB INIT
#include <stdio.h>
#include <string.h> // memcpy
#include "../utils.h"
#include "../vdisk.h"

void vdi_info(VDISK *vd) {
	char *type; // vdisk type
	switch (vd->vdi.type) {
	case VDI_DISK_DYN:	type = "dynamic"; break;
	case VDI_DISK_FIXED:	type = "fixed"; break;
	case VDI_DISK_UNDO:	type = "undo"; break;
	case VDI_DISK_DIFF:	type = "diff"; break;
	default:	type = "type?";
	}

	char disksize[BIN_FLENGTH];
	char bsize[BIN_FLENGTH];	// block size
	char create_uuid[GUID_TEXT_SIZE], modify_uuid[GUID_TEXT_SIZE],
		link_uuid[GUID_TEXT_SIZE], parent_uuid[GUID_TEXT_SIZE];

	fbins(vd->vdi.disksize, disksize);
	fbins(vd->vdi.blocksize, bsize);
	uuid_tostr(create_uuid, &vd->vdi.uuidCreate);
	uuid_tostr(modify_uuid, &vd->vdi.uuidModify);
	uuid_tostr(link_uuid, &vd->vdi.uuidLinkage);
	uuid_tostr(parent_uuid, &vd->vdi.uuidParentModify);

	printf(
	"VDI, VirtualBox %s vdisk v%u.%u, %s\n"
	"Header size: %u, Flags: %XH, Dummy: %u\n"
	"Blocks: %u (allocated: %u, extra: %u), %s size\n"
	"Offset to data: %Xh, to alloc blocks: %Xh\n"
	"Cylinders: %u (legacy: %u)\n"
	"Heads: %u (legacy: %u)\n"
	"Sectors: %u (legacy: %u)\n"
	"Sector size: %u (legacy: %u)\n"
	"Create UUID : %s\n"
	"Modifiy UUID: %s\n"
	"Linkage UUID: %s\n"
	"Parent UUID : %s\n",
	type, vd->vdihdr.majorv, vd->vdihdr.minorv, disksize,
	vd->vdi.hdrsize, vd->vdi.fFlags, vd->vdi.u32Dummy,
	vd->vdi.totalblocks, vd->vdi.blocksalloc, vd->vdi.blocksextra, bsize,
	vd->vdi.offData, vd->vdi.offBlocks,
	vd->vdi.cCylinders, vd->vdi.LegacyGeometry.cCylinders,
	vd->vdi.cHeads, vd->vdi.LegacyGeometry.cHeads,
	vd->vdi.cSectors, vd->vdi.LegacyGeometry.cSectors,
	vd->vdi.cbSector, vd->vdi.LegacyGeometry.cbSector,
	create_uuid, modify_uuid, link_uuid, parent_uuid
	);
}

int vdi_compact(VDISK *vd) {
	if (vd->format != VDISK_FORMAT_VDI) {
		fputs("vdi_compact: vdisk not VDI\n", stderr);
		return EVDFORMAT;
	}
	if (vd->vdi.type != VDI_DISK_DYN) {
		fputs("vdi_compact: vdisk not dynamic\n", stderr);
		return EVDTYPE;
	}

	uint8_t *buffer;	// Block buffer

	//
	// Block buffer for transfer
	//

	if ((buffer = malloc(vd->vdi.blocksize)) == NULL)
		return EVDALLOC;

	//
	// Temporary VDISK
	//
	// Also assigns the same attributes from the source vdisk
	//

	VDISK vdtmp;
	if ((vdtmp.u32blocks = malloc(vd->u32nblocks << 2)) == NULL)
		return EVDALLOC;
	vdtmp.offset = vd->offset;
	vdtmp.format = vd->format;
	vdtmp.u32nblocks = vd->u32nblocks;
	for (size_t i = 0; i < vdtmp.u32nblocks; ++i)
		vdtmp.u32blocks[i] = VDI_BLOCK_UNALLOCATED;
	memcpy(&vdtmp.vdihdr, &vd->vdihdr, sizeof(VDI_HDR));
	memcpy(&vdtmp.vdi, &vd->vdi, sizeof(VDIHEADER1));
	if (vdisk_open(NULL, &vdtmp, VDISK_CREATE_TEMP)) {
		vdisk_perror(__func__);
		return vdisk_errno;
	}
	printf("vdi_compact: %s disk created\n", vdisk_str(&vdtmp));

	//
	// Block tranfer
	//
	// Status progress
	//	Original:
	//		12,803,112,960 Bytes
	//	vboxmanage --compact:
	//		11,011,096,576 Bytes
	// 1.	Initial (vdisk_write_block_at):
	//	11,917,066,240 Bytes
	// 2.	Direct write (os_write)
	//	11,914,969,088 Bytes
	// 3.	
	//	 Bytes
	// 4.	
	//	 Bytes
	// 5.	
	//	 Bytes
	// 6.	
	//	 Bytes
	// 7.	
	//	 Bytes
	// 8.	
	//	 Bytes
	// 9.	
	//	 Bytes
	//

/*

Original:
| 0 | 1 | 4 | - | 3 | <- Block index
  |   |   |   /---+
  |   |   +---|---\
  v   v       v   v
|   |   | - |   |   | <- Block data (1 MiB)

Compacted:
| 0 | 1 | 2 | - | 3 |
  |   |   |   +---+
  v   v   v   v
|   |   |   |   | - |

*/

	// "Optimized" buffer size
	size_t oblocksize = vd->vdi.blocksize / sizeof(size_t);
	// "Optimized" buffer pointer
	size_t *obuffer = (size_t*)buffer;
	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
	char strbsize[BIN_FLENGTH];
	fbins(vd->vdi.blocksize, strbsize);
	printf("vdi_compact: Writing (%s blocks, %u checks/%u bytes)...\n",
		strbsize, (uint32_t)oblocksize, (uint32_t)sizeof(size_t));
	uint64_t d = 0;	// disk block index
	os_seek(vd->fd, vd->offset, SEEK_SET);
	for (size_t i = 0; i < vd->u32nblocks; ++i) {
		if (vd->u32blocks[i] == VDI_BLOCK_UNALLOCATED ||
			vd->u32blocks[i] == VDI_BLOCK_FREE) {
			vdtmp.u32blocks[i] = VDI_BLOCK_UNALLOCATED;
			++stat_unalloc;
			continue;
		}
		int re = vdisk_read_block(vd, buffer, i);
		if (re == EVDREAD || re == EVDSEEK) {
			fprintf(stderr, "vdi_compact: Couldn't %s disk\n",
				re == EVDSEEK ? "seek" : "read");
			return re;
		}
		// Check if block has data, if so, write the block into tmp VDISK
		++stat_alloc;
		for (size_t b = 0; b < oblocksize; ++b) {
			if (obuffer[b])
				goto L_HASDATA;
		}
		++stat_zero;
		continue;
L_HASDATA:
		//if (vdisk_write_block_at(&vdtmp, buffer, i, d)) {
		//	fputs("vdi_compact: Couldn't write to disk\n", stderr);
		//	return EVDWRITE;
		//}
		vdtmp.u32blocks[i] = i;
		os_write(vdtmp.fd, buffer, vd->vdi.blocksize);
		++stat_occupied;
		++d;
	}
	vdtmp.vdi.blocksalloc = stat_occupied;
	vdisk_update_headers(&vdtmp);
	printf(
		"vdi_compact: %u/%u blocks written, %u unallocated, %u zero, %u total\n",
		stat_occupied, stat_alloc, stat_unalloc, stat_zero, vd->u32nblocks
	);
	return 0;
}