www.pudn.com > vdksrc.zip > VDiskUtil.cpp


/*
	VDiskUtil.cpp

	Virtual Disk utility functions
	Copyright (C) 2003 Ken Kato
*/

#include "vdkbase.h"
#include "vdkutil.h"
#include "vdkfile.h"

#include "cowdisk.h"
#include "vmdisk.h"

#include "VDisk.h"
#include "VDiskPlain.h"
#include "VDiskCowd.h"
#include "VDiskVmdk.h"
#include "VDiskSimple.h"
#include "VDiskRaw.h"
#include "VDiskExt.h"
#include "VDiskUtil.h"


//
//	CallBack function pointer and default CallBack
//
static ULONG DefCallBack(ULONG reason, PVOID *params)
{
	UNREFERENCED_PARAMETER(reason);
	UNREFERENCED_PARAMETER(params);
	return 0;
}

VDISK_CALLBACK VDiskCallBack = DefCallBack;

void VDiskSetCallBack(VDISK_CALLBACK cb)
{
	VDiskCallBack = cb ? cb : DefCallBack;
}

//
//	Initialize from an existing virtual disk file
//
VDKSTAT VDiskLoadFile(
	PVOID *ppDisk,
	PCHAR pPath,
	PCHAR pBase)
{
	VDisk	*pDisk;
	UCHAR	buf[512];
	CHAR	path[MAX_PATH];
	HANDLE	hFile;
	INT64	size;
	ULONG	len;
	PCHAR	p;
	VDKSTAT	ret;

	if (!ppDisk || !pPath || !*pPath) {
		return VDK_PARAM;
	}

	*ppDisk = pDisk = NULL;

	//
	// Open the file
	//
	strcpy(path, pPath);

	ret = VDiskSearchFile(&hFile, path, pBase);

	if (ret != VDK_OK) {
		return ret;
	}

	//
	//	read first 512 bytes of the file
	//
	VdkZeroMem(buf, sizeof(buf));

	ret = VdkReadFileAt(hFile, 0, buf, sizeof(buf), &len);

	if (ret != VDK_OK) {
		VdkCloseFile(hFile);
		return ret;
	}

	//
	//	get file size
	//
	ret = VdkGetFileSize(hFile, &size);

	if (ret != VDK_OK) {
		VdkCloseFile(hFile);
		return ret;
	}

	VdkCloseFile(hFile);

	//
	//	Identify the file type
	//
	if (*(PULONG)buf == COWD_SIGNATURE) {

		//	VMware 2 or 3 virtual disk

		if ((pDisk = new VDiskCowd) == NULL) {
			return VdkLastError();
		}

		goto initialize;
	}

	if (*(PULONG)buf == VMDK_SIGNATURE) {

		//	VMware 4 virtual disk extent file

		if ((pDisk = new VDiskVmdk) == NULL) {
			return VdkLastError();
		}

		goto initialize;
	}

	//
	//	does the file contain control character?
	//
	while (len--) {
		if (buf[len] <= 0x08 ||
			buf[len] == 0x0b ||
			buf[len] == 0x0c ||
			(buf[len] >= 0x0e && buf[len] <= 0x1f) ||
			buf[len] == 0x7f) {

			//	contains a control character
			//	-- cannot be a descriptor file

			goto simple_disk;
		}
	}

	//	does not contain a control character
	//	-- one of descriptor file types

	p = (PCHAR)buf;

	buf[sizeof(buf) - 1] = '\0';

	while (*p) {

		while (*p == ' ' || *p == '\t') {
			p++;
		}

		if (!VdkCmpNoCaseN(p, "#vm|TOOLSVERSION",	16) ||
			!VdkCmpNoCaseN(p, "#vm|VERSION",		11) ||
			!VdkCmpNoCaseN(p, "DRIVETYPE",			 9) ||
			!VdkCmpNoCaseN(p, "CYLINDERS",			 9) ||
			!VdkCmpNoCaseN(p, "HEADS",				 5) ||
			!VdkCmpNoCaseN(p, "SECTORS",			 7) ||
			!VdkCmpNoCaseN(p, "ACCESS",				 6)) {

			//
			//	The file has VMware 2 plain disk entry
			//
			if ((pDisk = new VDiskPlain) == NULL) {
				return VdkLastError();
			}

			goto initialize;
		}
		else if (
			!VdkCmpNoCaseN(p, "# Disk DescriptorFile",	21) ||
			!VdkCmpNoCaseN(p, "version",				 7) ||
			!VdkCmpNoCaseN(p, "CID",					 3) ||
			!VdkCmpNoCaseN(p, "parentCID",				 9) ||
			!VdkCmpNoCaseN(p, "createType",				10) ||
			!VdkCmpNoCaseN(p, "parentFileNameHint",		18) ||
			!VdkCmpNoCaseN(p, "# Extent description",	20) ||
			!VdkCmpNoCaseN(p, "RW",						 2) ||
			!VdkCmpNoCaseN(p, "# The Disk Data Base",	20) ||
			!VdkCmpNoCaseN(p, "#DDB",					 4) ||
			!VdkCmpNoCaseN(p, "ddb.geometry.sectors",	20) ||
			!VdkCmpNoCaseN(p, "ddb.geometry.heads",		18) ||
			!VdkCmpNoCaseN(p, "ddb.geometry.cylinders",	22) ||
			!VdkCmpNoCaseN(p, "ddb.adapterType",		15) ||
			!VdkCmpNoCaseN(p, "ddb.virtualHWVersion",	20) ||
			!VdkCmpNoCaseN(p, "ddb.toolsVersion",		16)) {

			//
			//	The file has VMware 4 descriptor file entry
			//
			if ((pDisk = new VDiskVmdk) == NULL) {
				return VdkLastError();
			}

			goto initialize;
		}

		//
		//	skip to next line
		//
		while (*(p++) != '\n')
			;
	}

	//
	//	not a VMware descriptor file
	//	-- raw descriptor
	//
	if ((pDisk = new VDiskRaw) == NULL) {
		return VdkLastError();
	}

	goto initialize;


simple_disk:

	//
	//	None of above file type
	//
	{
		PVOID param = path;

		if (!VDiskCallBack(VDISK_CB_FILE_TYPE, ¶m)) {
			return VDK_CANCEL;
		}
	}

	//
	//	generic flat sector image file
	//
	if ((pDisk = new VDiskSimple) == NULL) {
		return VdkLastError();
	}


initialize:
	ret = pDisk->Initialize(path);

#ifdef VDK_DEBUG
	pDisk->Dump();
#endif

	*ppDisk = pDisk;

	return ret;
}

//
//	Create a virtual disk tree
//
VDKSTAT VDiskCreateTree(PVOID pDisk)
{
	return ((VDisk *)pDisk)->CreateTree();
}

//
//	Create a REDO log for a virtual disk
//
VDKSTAT VDiskCreateRedo(PVOID *ppDisk)
{
	VDKSTAT ret;
	VDisk *disk, *redo;
	CHAR path[MAX_PATH];

	disk = (VDisk *)*ppDisk;

	sprintf(path, "%s" PATH_SEPARATOR_STR "%s.%s.REDO",
		disk->GetPath(), disk->GetBody(), disk->GetExt());

	if (disk->GetVMwareVer() == 4) {
		redo = new VDiskVmdk;
	}
	else {
		redo = new VDiskCowd;
	}

	if (!redo) {
		return VdkLastError();
	}

	ret = redo->InitChild(
		VDISK_FLAG_SPARSE | VDISK_FLAG_CHILD,	// | VDISK_FLAG_ABSPATH,
		path, disk->GetVMwareVer(), disk);

	if (ret != VDK_OK) {
		delete redo;
		return ret;
	}

	ret = redo->Create(0);

	if (ret != VDK_OK) {
		delete redo;
		return ret;
	}

	*ppDisk = redo;

	return VDK_OK;
}

//
//	Delete VDisk Tree
//
VOID VDiskDelete(PVOID pDisk)
{
	if (pDisk) {
		((VDisk *)pDisk)->DeleteTree();
	}
}


//
//	Search file
//
VDKSTAT VDiskSearchFile(HANDLE *pFile, PCHAR pPath, PCHAR pBase)
{
	CHAR	path[MAX_PATH];
	VDKSTAT	ret;

	//
	//	remove leading ".\"
	//
	while (*pPath == '.' && (
		*(pPath + 1) == ALT_SEPARATOR_CHAR ||
		*(pPath + 1) == PATH_SEPARATOR_CHAR)) {
		memmove(pPath, pPath + 2, strlen(pPath + 2) + 1);
	}

	//
	//	try to open with given path
	//
	ret = VdkOpenFile(pFile, pPath, strlen(pPath), TRUE);

	if (ret == VDK_OK) {
		return ret;
	}

	if ((ret == VDK_NOFILE || ret == VDK_NOPATH) && pBase) {

		//
		//	Try given base path
		//
		PCHAR	body;

		if ((isalpha(*pPath) && *(pPath + 1) == ':') ||
			*pPath == PATH_SEPARATOR_CHAR ||
			*pPath == ALT_SEPARATOR_CHAR) {

			//
			//	absolute path -- extract filename only
			//
			body = pPath + strlen(pPath);

			while (body > pPath &&
				*(body - 1) != PATH_SEPARATOR_CHAR &&
				*(body - 1) != ALT_SEPARATOR_CHAR) {

				body--;
			}
		}
		else {
			//
			//	relative path -- use as it is
			//
			body = pPath;
		}

		sprintf(path, "%s" PATH_SEPARATOR_STR "%s", pBase, body);

		//	shouldn't modify 'ret' variable here

		if (VdkOpenFile(pFile, path, strlen(path), TRUE) == VDK_OK) {
			strcpy(pPath, path);
			return VDK_OK;
		}
	}

	//
	//	ask user
	//
	strcpy(path, pPath);

	do {
		PVOID cbparams[2];
		cbparams[0] = path;
		cbparams[1] = (PVOID)ret;

		if (!VDiskCallBack(VDISK_CB_FILE_OPEN, cbparams)) {
			return ret;
		}

		ret = VdkOpenFile(pFile, path, strlen(path), TRUE);
	}
	while (ret != VDK_OK);

	strcpy(pPath, path);

	return ret;
}

//
//	Map VDisk attributes to VDK_OPEN_FILE_INFO
//
VDKSTAT VDiskMapToOpenInfo(
	PVOID	pDisk,
	PVOID	*ppInfo,
	PULONG	pInfoLen)
{
	PVDK_OPEN_FILE_INFO open_info;
	PVDK_OPEN_FILE_ITEM open_item;
	VDisk *disk;
	VDiskExt **ext;

	ULONG total_files;
	ULONG name_len;
	PCHAR name_pos;
	ULONG idx;

	//
	//	Parameter check
	//
	if (!pDisk || !ppInfo || !pInfoLen) {
		return VDK_PARAM;
	}

	//
	//	Find out necessary memory size
	//
	total_files = 0;
	name_len = 0;

	disk = (VDisk *)pDisk;

	if (disk->GetCylinders() &&
		disk->GetTracks() &&
		disk->GetSectors()) {

		if (disk->GetCylinders() *
			disk->GetTracks() *
			disk->GetSectors() > disk->GetCapacity()) {

			return VDK_PARAM;
		}
	}

	do {
		if (!disk->GetExtentCnt()) {
			return VDK_PARAM;
		}

		total_files += disk->GetExtentCnt();

		ext = disk->GetExtents();

		for (idx = 0; idx < disk->GetExtentCnt(); idx++) {

			if (!ext[idx]->GetCapacity() ||
				ext[idx]->GetCapacity() > disk->GetCapacity()) {
				return VDK_DATA;
			}

			if (ext[idx]->GetFileAttr() & VDK_INVALID_ATTRIBUTES) {
				return VDK_DATA;
			}

			name_len += strlen(ext[idx]->GetFullPath()) + 1;
		}

		disk = disk->GetParent();
	}
	while (disk);

	//
	//	allocate file information area
	//
	*pInfoLen = FIELD_OFFSET(VDK_OPEN_FILE_INFO, Files) +
		sizeof(VDK_OPEN_FILE_ITEM) * total_files + name_len;

	open_info = (PVDK_OPEN_FILE_INFO)VdkAllocMem(*pInfoLen);

	if (!open_info) {
		return VDK_NOMEMORY;
	}

	VdkZeroMem(open_info, *pInfoLen);

	//
	//	Copy file structure into VDK_OPEN_FILE_INFO
	//
	disk = (VDisk *)pDisk;

	open_info->Capacity		= disk->GetCapacity();
	open_info->Cylinders	= disk->GetCylinders();
	open_info->Tracks		= disk->GetTracks();
	open_info->Sectors		= disk->GetSectors();
	open_info->FilesTotal	= total_files;

	//
	//	copy each file information
	//
	open_item = open_info->Files;
	name_pos = (PCHAR)&(open_item[total_files]);

	do {
		ext = disk->GetExtents();

		for (idx = 0; idx < disk->GetExtentCnt(); idx++) {

			open_item->FileType = ext[idx]->GetFileType();
			open_item->Capacity = ext[idx]->GetCapacity();

			strcpy(name_pos, ext[idx]->GetFullPath());
			open_item->NameLength = strlen(name_pos) + 1;

			name_pos += open_item->NameLength;
			open_item++;
		}

		disk = disk->GetParent();
	}
	while (disk);

	*ppInfo = open_info;

	return VDK_OK;
}


PCHAR	VDiskGetDiskName(PVOID pDisk)
{
	return ((VDisk *)pDisk)->GetBody();
}