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


/*
	VDiskCowd.cpp

	COWDisk class
	Copyright (c) 2003 Ken Kato
*/

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

#include "cowdisk.h"

#include "VDiskCowd.h"
#include "VDiskExtCowd.h"
#include "VDiskUtil.h"


//
//	Constructor -- set default members
//
VDiskCowd::VDiskCowd()
{
	m_nVMwareVer	= 3;
	m_nHardwareVer	= COWD_HARDWARE_VMWARE3;
}

//
//	Destructor
//
VDiskCowd::~VDiskCowd()
{
}

//
//	Initialize instance from a VMware 2.x/3.x virtual file
//
VDKSTAT VDiskCowd::Initialize(PCHAR pPath)
{
	CHAR	path[MAX_PATH];
	ULONG	total_extents;
	ULONG	idx;
	VDKSTAT	ret;
	HANDLE	hFile;
	ULONG	result;
	COWD_HEADER	cowd;

	//	check parameter

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

	//	store path

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

	//	open file

	ret = VdkOpenFile(&hFile, pPath, strlen(pPath), TRUE);

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

	//	read COWD header

	ret = VdkReadFileAt(hFile, 0, &cowd, sizeof(cowd), &result);

	VdkCloseFile(hFile);

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

	if (result != sizeof(cowd)) {
		return VDK_EOF;
	}

	//	process header information

	if (cowd.sec0.Version == COWD_FILEVER_VMWARE2) {
		m_nVMwareVer = 2;
	}
	else {
		m_nVMwareVer = 3;
	}

	//
	//	decide how many extents should be present
	//
	if (COWD_IS_MULTI(cowd.sec0.Flags)) {
		total_extents = cowd.sec3.FilesPerDisk;

		if (cowd.sec3.FileOrdinal) {
			//
			// adjust base file name --
			// if the source path has a sequence number, remove it
			//
			idx = strlen(m_pBody);

			if (idx > 3 && *(m_pBody + idx - 3) == '-' &&
				(ULONG)atol(m_pBody + idx - 2) == cowd.sec3.FileOrdinal + 1) {

				*(m_pBody + idx - 3) = '\0';
			}
		}
	}
	else {
		total_extents = 1;
	}

	//
	// initialize each extent object
	//
	for (idx = 0; idx < total_extents; idx++) {
		VDiskExtCowd *ext;

		ext = new VDiskExtCowd;

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

		ret = AddExtent(ext);

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

		GetExtentPath(path, idx);

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

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

		ret = ext->SetPath(path);

		if (ret == VDK_OK) {
			ret = ext->Load(hFile);
		}

		VdkCloseFile(hFile);

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

	return Check();
}

//
//	check COWDisk consistency
//
VDKSTAT VDiskCowd::Check()
{
	ULONG total_sectors;
	PCOWD_SECTOR_0 sec0_0;
	PCOWD_SECTOR_2 sec2_0;
	PCOWD_SECTOR_3 sec3_0;
	PVOID cbparams[4];
	ULONG idx;
	VDKSTAT ret;

	for (idx = 0; idx < m_nExtents; idx++) {
		VDiskExtCowd *ext = (VDiskExtCowd *)m_ppExtents[idx];

		ret = ext->Check();

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

		if (ext->IsModified()) {
			SetFlag(VDISK_FLAG_DIRTY);
		}

		if (ext->GetSec3()->FileOrdinal != idx) {

			cbparams[0] = ext->GetFileName();
			cbparams[1] = (PVOID)ext->GetSec3()->FileOrdinal;
			cbparams[2] = (PVOID)idx;

			if (!VDiskCallBack(VDISK_CB_COWD_ORDINAL, cbparams)) {
				return VDK_CANCEL;
			}

			ext->GetSec3()->FileOrdinal = idx;
			ext->SetModify();
			SetFlag(VDISK_FLAG_DIRTY);
		}
	}

	//
	//	check extent parameters consistency
	//
	sec0_0 = ((VDiskExtCowd *)m_ppExtents[0])->GetSec0();
	sec2_0 = ((VDiskExtCowd *)m_ppExtents[0])->GetSec2();
	sec3_0 = ((VDiskExtCowd *)m_ppExtents[0])->GetSec3();

	cbparams[0] = m_ppExtents[0]->GetFullPath();

	for (idx = 1; idx < m_nExtents; idx++) {
		BOOL conflict = FALSE, fatal = FALSE;
		PCOWD_SECTOR_0 sec0 = ((VDiskExtCowd *)m_ppExtents[idx])->GetSec0();
		PCOWD_SECTOR_2 sec2 = ((VDiskExtCowd *)m_ppExtents[idx])->GetSec2();
		PCOWD_SECTOR_3 sec3 = ((VDiskExtCowd *)m_ppExtents[idx])->GetSec3();

		cbparams[2] = m_ppExtents[idx]->GetFullPath();

		//
		//	FATAL conflicts
		//
		if (sec0_0->Version != sec0->Version) {
			cbparams[1] = (PVOID)sec0_0->Version;
			cbparams[3] = (PVOID)sec0->Version;

			VDiskCallBack(VDISK_CB_CONF_FILEVER, cbparams);
			fatal = TRUE;
		}

		if (sec0_0->Flags != sec0->Flags) {
			cbparams[1] = (PVOID)sec0_0->Flags;
			cbparams[3] = (PVOID)sec0->Flags;

			VDiskCallBack(VDISK_CB_CONF_FLAGS, cbparams);
			fatal = TRUE;
		}

		if (sec3_0->FilesPerDisk != sec3->FilesPerDisk) {
			cbparams[1] = (PVOID)sec3_0->FilesPerDisk;
			cbparams[3] = (PVOID)sec3->FilesPerDisk;

			VDiskCallBack(VDISK_CB_CONF_EXTENTS, cbparams);
			fatal = TRUE;
		}

		if (sec3_0->DiskCapacity != sec3->DiskCapacity) {
			cbparams[1] = (PVOID)sec3_0->DiskCapacity;
			cbparams[3] = (PVOID)sec3->DiskCapacity;

			VDiskCallBack(VDISK_CB_CONF_CAPACITY, cbparams);
			fatal = TRUE;
		}

		if (!COWD_IS_ROOT(sec0_0->Flags)) {
			if (sec0_0->Capacity != sec0->Capacity) {
				cbparams[1] = (PVOID)sec0_0->Capacity;
				cbparams[3] = (PVOID)sec0->Capacity;

				VDiskCallBack(VDISK_CB_CONF_CAPACITY, cbparams);
				fatal = TRUE;
			}

			if (VdkCmpNoCase(sec0_0->u.ParentPath, sec0->u.ParentPath)) {
				cbparams[1] = sec0_0->u.ParentPath;
				cbparams[3] = sec0->u.ParentPath;

				VDiskCallBack(VDISK_CB_CONF_PARENTPATH, cbparams);
				fatal = TRUE;
			}
		}

		//
		//	Errors which can be ignored
		//

		if (sec2_0->ParentTS != sec2->ParentTS) {
			cbparams[1] = (PVOID)sec2_0->ParentTS;
			cbparams[3] = (PVOID)sec2->ParentTS;

			VDiskCallBack(VDISK_CB_CONF_PARENTTS, cbparams);
			conflict = TRUE;
		}

		if (sec2_0->TimeStamp != sec2->TimeStamp) {
			cbparams[1] = (PVOID)sec2_0->TimeStamp;
			cbparams[3] = (PVOID)sec2->TimeStamp;

			VDiskCallBack(VDISK_CB_CONF_TIMESTAMP, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->Controller != sec3->Controller) {
			cbparams[1] = &sec3_0->Controller;
			cbparams[3] = &sec3->Controller;

			VDiskCallBack(VDISK_CB_CONF_CONTROLLER, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->Cylinders	!= sec3->Cylinders) {
			cbparams[1] = (PVOID)sec3_0->Cylinders;
			cbparams[3] = (PVOID)sec3->Cylinders;

			VDiskCallBack(VDISK_CB_CONF_CYLINDERS, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->Tracks != sec3->Tracks) {
			cbparams[1] = (PVOID)sec3_0->Tracks;
			cbparams[3] = (PVOID)sec3->Tracks;

			VDiskCallBack(VDISK_CB_CONF_TRACKS, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->Sectors != sec3->Sectors) {
			cbparams[1] = (PVOID)sec3_0->Sectors;
			cbparams[3] = (PVOID)sec3->Sectors;

			VDiskCallBack(VDISK_CB_CONF_SECTORS, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->SequenceNumber != sec3->SequenceNumber) {
			cbparams[1] = (PVOID)sec3_0->SequenceNumber;
			cbparams[3] = (PVOID)sec3->SequenceNumber;

			VDiskCallBack(VDISK_CB_CONF_SEQNUM, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->HardwareVer != sec3->HardwareVer) {
			cbparams[1] = (PVOID)sec3_0->HardwareVer;
			cbparams[3] = (PVOID)sec3->HardwareVer;

			VDiskCallBack(VDISK_CB_CONF_HARDWARE, cbparams);
			conflict = TRUE;
		}

		if (sec3_0->ToolsFlag != sec3->ToolsFlag) {
			cbparams[1] = (PVOID)sec3_0->ToolsFlag;
			cbparams[3] = (PVOID)sec3->ToolsFlag;

			VDiskCallBack(VDISK_CB_CONF_TOOLSFLAG, cbparams);
			conflict = TRUE;
		}

		if (fatal) {
			return VDK_DATA;
		}

		if (conflict) {
			if (!VDiskCallBack(VDISK_CB_CONFLICT_IGNORE, NULL)) {
				return VDK_CANCEL;
			}
			SetFlag(VDISK_FLAG_DIRTY);
		}
	}

	//
	//	store necessary disk parameters
	//
	if (COWD_IS_ROOT(sec0_0->Flags)) {
		ClrFlag(VDISK_FLAG_CHILD);

		if (COWD_IS_MULTI(sec0_0->Flags)) {
			m_nCapacity		= sec3_0->DiskCapacity;
			m_nCylinders	= sec3_0->Cylinders;
			m_nTracks		= sec3_0->Tracks;
			m_nSectors		= sec3_0->Sectors;
		}
		else {
			m_nCapacity		= sec0_0->Capacity;
			m_nCylinders	= sec0_0->u.Geometry.Cylinders;
			m_nTracks		= sec0_0->u.Geometry.Tracks;
			m_nSectors		= sec0_0->u.Geometry.Sectors;
		}
	}
	else {
		SetFlag(VDISK_FLAG_CHILD);

		m_nCapacity = sec0_0->Capacity;

		StoreParentPath(sec0_0->u.ParentPath);

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

			SetFlag(VDISK_FLAG_ABSPATH);
		}
		else {
			ClrFlag(VDISK_FLAG_ABSPATH);
		}
	}

	m_nParentTS		= sec2_0->ParentTS;
	m_nTimeStamp	= sec2_0->TimeStamp;
	m_nHardwareVer	= sec3_0->HardwareVer;
	m_nToolsFlag	= sec3_0->ToolsFlag;

	if (sec3_0->Controller == COWD_CONTROLLER_IDE) {
		m_nController	= VDISK_CONTROLLER_IDE;
	}
	else {
		m_nController	= VDISK_CONTROLLER_SCSI;
	}

	//
	// adjust file capacity of the last extent
	// -- cowd header does not explicitly specify it.
	//
	total_sectors = 0;

	for (idx = 0; idx < m_nExtents - 1; idx++) {
		total_sectors += m_ppExtents[idx]->GetCapacity();
	}

	if (total_sectors >= m_nCapacity) {
		CHAR path[MAX_PATH];

		cbparams[0] = path;
		cbparams[1] = (PVOID)m_nCapacity;
		cbparams[2] = (PVOID)total_sectors;

		FullPath(path);

		if (!VDiskCallBack(VDISK_CB_COWD_CAPACITY, cbparams)) {
			return VDK_DATA;
		}

		if (total_sectors >= m_nCapacity) {
			return VDK_DATA;
		}

		SetFlag(VDISK_FLAG_DIRTY);
	}

	m_ppExtents[idx]->SetCapacity(m_nCapacity - total_sectors);

	//
	//	Update ?
	//
	if (m_nFlags & VDISK_FLAG_DIRTY) {
		CHAR path[MAX_PATH];

		cbparams[0] = path;

		FullPath(path);

		if (VDiskCallBack(VDISK_CB_CONFIRM_FIX, cbparams)) {
			for (idx = 0; idx < m_nExtents; idx++) {
				ret = m_ppExtents[idx]->Update();

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


	return VDK_OK;
}

//
//	Create VMware 2/3 virtual disk
//
VDKSTAT VDiskCowd::Create(ULONG flags)
{
	ULONG idx;
	VDKSTAT ret = VDK_OK;

	if (!m_nExtents || !m_ppExtents || !m_ppExtents[0]) {
		return VDK_FUNCTION;
	}

	for (idx = 0; idx < m_nExtents; idx++) {
		VDiskExtCowd *ext = (VDiskExtCowd *)m_ppExtents[idx];

		ext->CreateHeader(this, idx);

		ret = ext->Create(flags);

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

	return ret;
}

//
//	Creates a new extent object for this instance
//
VDiskExt *VDiskCowd::NewExtent()
{
	return new VDiskExtCowd;
}

//
//	Get path for each extent files
//
void VDiskCowd::GetExtentPath(
	PCHAR pPath, ULONG nSeq)
{
	if (nSeq) {
		sprintf(pPath, "%s" PATH_SEPARATOR_STR "%s-%02ld.%s",
			m_pPath, m_pBody, nSeq + 1, m_pExtension);
	}
	else {
		sprintf(pPath, "%s" PATH_SEPARATOR_STR "%s.%s",
			m_pPath, m_pBody, m_pExtension);
	}
}

//
//	Set default geometry values
//
void VDiskCowd::SetGeometry()
{
	if (m_nController == VDISK_CONTROLLER_IDE) {

		if (m_nVMwareVer == 2) {
			m_nTracks	= COWD1_TRACKS_IDE;
			m_nSectors	= COWD_SECTORS_IDE;
		}
		else {
			m_nTracks	= COWD3_TRACKS_IDE;
			m_nSectors	= COWD_SECTORS_IDE;
		}
	}
	else {
		if (m_nCapacity <=
			COWD_SECTORS_SCSI_1023M * COWD_TRACKS_SCSI_1023M * 1023) {

			m_nSectors	= COWD_SECTORS_SCSI_1023M;
			m_nTracks	= COWD_TRACKS_SCSI_1023M;
		}
		else if (m_nCapacity <=
			COWD_SECTORS_SCSI_2046M * COWD_TRACKS_SCSI_2046M * 1023) {

			m_nSectors	= COWD_SECTORS_SCSI_2046M;
			m_nTracks	= COWD_TRACKS_SCSI_2046M;
		}
		else {
			m_nSectors	= COWD_SECTORS_SCSI_LARGE;
			m_nTracks	= COWD_TRACKS_SCSI_LARGE;
		}
	}

	m_nCylinders = m_nCapacity / (m_nSectors * m_nTracks);
}

//
//	Get default extent size
//
ULONG VDiskCowd::DefaultExtSize()
{
	ULONG ext_size = 0;

	if (m_nVMwareVer == 2) {
		if (m_nController == VDISK_CONTROLLER_SCSI) {
			ext_size = COWD1_MAX_EXTENT_SCSI;
		}
		else if (m_nController == VDISK_CONTROLLER_IDE) {
			ext_size = COWD1_MAX_EXTENT_IDE;
		}
	}
	else {
		ext_size = COWD3_MAX_EXTENT_SPARSE;
	}

	if (m_nCapacity > ext_size) {

		if (m_nVMwareVer == 2) {
			//
			//	VMware 2.x doesn't allow multi-extent virutal disk
			//
			ext_size = 0;
		}
		else {
			ext_size = m_nCapacity;
		}
	}

	return ext_size;
}