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


/*
	VDiskVmdk.cpp

	VMDK class
	Copyright (c) 2003 Ken Kato
*/

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

#include "vmdisk.h"

#include "VDiskVmdk.h"
#include "VDiskExtRaw.h"
#include "VDiskExtVmdk.h"
#include "VDiskUtil.h"
#include "VDiskFile.h"

//
//	Constructor -- set default member values
//
VDiskVmdk::VDiskVmdk()
{
	m_nVMwareVer	= 4;
	m_nHardwareVer	= VMDK_HARDWARE_VMWARE4;
}

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

//
//	Initialize instance from a VMware 4.x descriptor file
//
VDKSTAT VDiskVmdk::Initialize(PCHAR pPath)
{
	VDiskFile file;
	CHAR	buf[MAX_PATH + 40];
	CHAR	path[MAX_PATH];
	PVOID	cbparams[2];
	PCHAR	current;
	ULONG	signature;
	ULONG	len;
	VDKSTAT	ret;

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

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

	//
	//	open file
	//
	if ((ret = file.Open(pPath)) != VDK_OK) {
		return ret;
	}

	//
	//	initialize members
	//
	cbparams[0] = pPath;

	//
	//	read first 4 bytes
	//
	signature = 0;

	ret = file.ReadByte((PUCHAR)&signature, sizeof(signature), &len);

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

	if (len != sizeof(signature)) {
		return VDK_EOF;
	}

	if (signature == VMDK_SIGNATURE) {
		//
		//	it's a monolithic sparse file
		//	-- descriptor offset is stored in header
		//
		VMDK_HEADER vmdk;

		ret = file.ReadByte((PUCHAR)&vmdk + sizeof(signature), 
			sizeof(vmdk) - sizeof(signature), &len);

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

		if (len != sizeof(vmdk) - sizeof(signature)) {
			return VDK_DATA;
		}

		if (vmdk.DescOffsetLow == 0		||
			vmdk.DescOffsetHigh != 0	||
			vmdk.DescSizeLow == 0		||
			vmdk.DescSizeHigh != 0) {

			VDiskCallBack(VDISK_CB_VMDK_NODESC, cbparams);

			return VDK_DATA;
		}

		ret = VdkSeekFile(file.Handle(),
			vmdk.DescOffsetLow << VDK_BYTE_SHIFT_TO_SECTOR);

		if (ret != VDK_OK) {
			return ret;
		}
	}
	else {
		ret = VdkSeekFile(file.Handle(), 0);

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

	while ((ret = file.ReadText(buf, sizeof(buf), NULL)) == VDK_OK) {

		//
		//	replace tabs with blanks
		//
		current = buf;

		while (*current) {
			if (*current == '\t' || *current == '\n') {
				*current = ' ';
			}
			current++;
		}

		//
		//	remove trailing blanks
		//
		while (current > buf && *(--current) == ' ') {
			*current = '\0';
		}

		//
		//	skip leading blanks
		//
		current = buf;

		while (*current == ' ') {
			current++;
		}

		//
		//	blank line?
		//
		if (!*current) {
			continue;
		}

		cbparams[1] = current;

		//
		//	parse current line
		//
		if (!VdkCmpNoCaseN(current, "RW ", 3) ||
			!VdkCmpNoCaseN(current, "RDONLY", 6)) {
			VDiskExt	*ext;
			PCHAR		top, tail, p;
			ULONG		capacity;
			CHAR		delim;
			BOOL		sparse;
			ULONG		offset;
			HANDLE		hFile;

			//
			//	search capacity field
			//
			p = current + 3;

			while (*p == ' ') {
				p++;
			}

			capacity = atol(p);

			if (!capacity) {

				capacity = VDiskCallBack(
					VDISK_CB_DESC_CAPACITY, cbparams);

				if (!capacity) {
					return VDK_DATA;
				}

				SetFlag(VDISK_FLAG_DIRTY);
			}

			//
			//	search extent type field
			//
			while (isdigit(*p)) {
				p++;
			}

			while (*p == ' ') {
				p++;
			}

			if (!VdkCmpNoCaseN(p, "flat ", 5)) {
				sparse = FALSE;
			}
			else if (!VdkCmpNoCaseN(p, "sparse ", 7)) {
				sparse = TRUE;
			}
			else {

				ULONG type = VDiskCallBack(
					VDISK_CB_DESC_FILETYPE, cbparams);

				if (type == VDK_FILETYPE_FLAT) {
					sparse = FALSE;
				}
				else if (type == VDK_FILETYPE_VMDK) {
					sparse = TRUE;
				}
				else {
					return VDK_DATA;
				}

				SetFlag(VDISK_FLAG_DIRTY);
			}

			//
			//	search extent path field
			//
			top = p;

			while (*top && *top != ' ') {
				top++;
			}

			while (*top == ' ') {
				top++;
			}

			if (*top == '\"') {
				delim = '\"';
				top++;
			}
			else {
				delim = ' ';
			}

			tail = top;

			while (*tail && *tail != delim) {
				tail++;
			}

			if (!*tail || tail - top >= MAX_PATH) {

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

				SetFlag(VDISK_FLAG_DIRTY);
				continue;
			}

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

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

			VdkCopyMem(path, top, tail - top);
			path[tail - top] = '\0';

			if (sparse) {
				offset = 0;
			}
			else {
				//
				//	search "backing offset" field
				//
				p = tail + 1;

				while (*p == ' ') {
					p++;
				}

				if (isdigit(*p)) {
					offset = atol(p);
				}
				else {
					offset = 0;
				}
			}

			//	open the extent file
			*tail = '\0';

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

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

			//
			//	create an extent object
			//
			if (sparse) {
				ext = new VDiskExtVmdk;
			}
			else {
				ext = new VDiskExtRaw;
			}

			if (ext == NULL) {
				ret = VdkLastError();
				VdkCloseFile(hFile);
				return ret;
			}

			ret = AddExtent(ext);

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

			ret = ext->SetPath(path);

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

			ret = ext->Load(hFile);

			VdkCloseFile(hFile);

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

			ext->SetCapacity(capacity);

			if (!sparse) {
				((VDiskExtRaw *)ext)->SetBackOffset(offset);
			}
		}
		else if (!VdkCmpNoCaseN(current, "ddb.geometry.sectors", 20)) {
			PCHAR p = current + 20;

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

			m_nSectors = atol(p);

			if (!m_nSectors) {

				m_nSectors = VDiskCallBack(
					VDISK_CB_DESC_GEOMETRY, cbparams);

				if (!m_nSectors) {
					return VDK_DATA;
				}

				SetFlag(VDISK_FLAG_DIRTY);
			}
		}
		else if (!VdkCmpNoCaseN(current, "ddb.geometry.heads", 18)) {
			PCHAR p = current + 18;

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

			m_nTracks = atol(p);

			if (!m_nTracks) {

				m_nTracks = VDiskCallBack(
					VDISK_CB_DESC_GEOMETRY, cbparams);

				if (!m_nTracks) {
					return VDK_DATA;
				}

				SetFlag(VDISK_FLAG_DIRTY);
			}
		}
		else if (!VdkCmpNoCaseN(current, "ddb.geometry.cylinders", 22)) {
			PCHAR p = current + 22;

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

			m_nCylinders = atol(p);

			if (!m_nCylinders) {

				m_nCylinders = VDiskCallBack(
					VDISK_CB_DESC_GEOMETRY, cbparams);

				if (!m_nCylinders) {
					return VDK_DATA;
				}

				SetFlag(VDISK_FLAG_DIRTY);
			}
		}
		else if (!VdkCmpNoCaseN(current, "CID", 3)) {
			PCHAR p = current + 3;

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

			if (!sscanf(p, "%lx", &m_nTimeStamp)) {

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

				m_nTimeStamp = (ULONG)-1;
				SetFlag(VDISK_FLAG_DIRTY);
			}
		}
		else if (!VdkCmpNoCaseN(current, "parentCID", 9)) {
			PCHAR p = current + 9;

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

			}

			if (!sscanf(p, "%lx", &m_nParentTS)) {

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

				m_nParentTS = (ULONG)-1;
				SetFlag(VDISK_FLAG_DIRTY);
			}

			if (m_nParentTS != (ULONG)-1) {
				SetFlag(VDISK_FLAG_CHILD);
			}
		}
		else if (!VdkCmpNoCaseN(current, "ddb.virtualHWVersion", 20)) {
			PCHAR p = current + 20;

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

			m_nHardwareVer = atol(p);
		}
		else if (!VdkCmpNoCaseN(current, "ddb.adapterType", 15)) {
			PCHAR p = current + 15;

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

			if (!VdkCmpNoCaseN(p, "ide", 3)) {
				m_nController = VDISK_CONTROLLER_IDE;
			}
			else if (!VdkCmpNoCaseN(p, "buslogic", 8)) {
				m_nController = VDISK_CONTROLLER_SCSI;
			}
			else {

				m_nController = VDiskCallBack(
					VDISK_CB_CONTROLLER, cbparams);

				if (m_nController != VDISK_CONTROLLER_SCSI &&
					m_nController != VDISK_CONTROLLER_IDE) {
					return VDK_CANCEL;
				}

				SetFlag(VDISK_FLAG_DIRTY);
			}
		}
		else if (!VdkCmpNoCaseN(current, "createType", 10)) {
			PCHAR p = current + 10;
			ULONG type;

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

			if (!VdkCmpNoCaseN(p, "twoGbMaxExtentSparse", 20)) {
				type = VDISK_VMDK_SPLIT_SPARSE;
			}
			else if (!VdkCmpNoCaseN(p, "monolithicSparse", 16)) {
				type = VDISK_VMDK_MONO_SPARSE;
			}
			else if (!VdkCmpNoCaseN(p, "twoGbMaxExtentFlat", 18)) {
				type = VDISK_VMDK_SPLIT_FLAT;
			}
			else if (!VdkCmpNoCaseN(p, "monolithicFlat", 14)) {
				type = VDISK_VMDK_MONO_FLAT;
			}
			else {

				type = VDiskCallBack(
					VDISK_CB_DESC_DISKTYPE, cbparams);

				SetFlag(VDISK_FLAG_DIRTY);
			}

			switch (type) {
			case VDISK_VMDK_SPLIT_FLAT:
				ClrFlag(VDISK_FLAG_SINGLE);
				ClrFlag(VDISK_FLAG_CHILD);
				ClrFlag(VDISK_FLAG_SPARSE);
				break;

			case VDISK_VMDK_MONO_FLAT:
				SetFlag(VDISK_FLAG_SINGLE);
				ClrFlag(VDISK_FLAG_CHILD);
				ClrFlag(VDISK_FLAG_SPARSE);
				break;

			case VDISK_VMDK_SPLIT_SPARSE:
				ClrFlag(VDISK_FLAG_SINGLE);
				SetFlag(VDISK_FLAG_SPARSE);
				break;

			case VDISK_VMDK_MONO_SPARSE:
				SetFlag(VDISK_FLAG_SINGLE);
				SetFlag(VDISK_FLAG_SPARSE);
				break;

			default:
				return VDK_DATA;
			}
		}
		else if (!VdkCmpNoCaseN(current, "version", 7)) {
/*
			PCHAR p = current + 7;

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

			m_nVmdkVersion = atol(p);
*/
		}
		else if (!VdkCmpNoCaseN(current, "parentFileNameHint", 18)) {
			PCHAR top = current + 18;
			PCHAR tail;

			while (*top == ' ' || *top == '=' || *top == '\"') {
				top++;
			}

			tail = top;

			while (*tail && *tail != '\"') {
				tail++;
			}

			*tail = '\0';

			ret = StoreParentPath(top);

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

			SetFlag(VDISK_FLAG_CHILD);
		}
		else if (!VdkCmpNoCaseN(current, "ddb.toolsVersion", 16)) {
			PCHAR p = current + 16;

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

			m_nToolsFlag = atol(p);
		}
		else if (*current && *current != '#') {

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

			SetFlag(VDISK_FLAG_DIRTY);
		}
	}

	if (ret == VDK_EOF) {
		return Check();
	}
	else {
		return ret;
	}
}

//
//	create VMDK descriptor file and extent files
//
VDKSTAT VDiskVmdk::Create(ULONG flags)
{
	ULONG idx;
	VDKSTAT ret;

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

	if (m_nFlags & VDISK_FLAG_SINGLE) {
		flags |= VDISK_CREATE_SINGLE;
	}

	//
	//	create each extent file
	//
	for (idx = 0; idx < m_nExtents; idx++) {
		ret = m_ppExtents[idx]->Create(flags);

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

	//
	//	create the description file (or write description into monolithic file)
	//
	ret = WriteDescriptor(TRUE, (flags & VDISK_CREATE_FORCE));

	return ret;
}

//
//	write VMDK descriptor file
//
VDKSTAT VDiskVmdk::WriteDescriptor(BOOL create, BOOL force)
{
	CHAR	buf[MAX_PATH + 30];
	ULONG	idx;
	ULONG	path_len;
	HANDLE	hFile;
	VDKSTAT	ret = VDK_OK;

	//
	//	create/open the description file
	//
	sprintf(buf, "%s" PATH_SEPARATOR_STR "%s.%s",
		m_pPath, m_pBody, m_pExtension);

	if (create) {
		ret = VdkCreateFile(&hFile, buf, force);
	}
	else {
		ret = VdkOpenFile(&hFile, buf, strlen(buf), FALSE);
	}

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

	if ((m_nFlags & VDISK_FLAG_SINGLE) &&
		(m_nFlags & VDISK_FLAG_SPARSE)) {

		//
		//	adjust descriptor offset for monolithic sparse file
		//
		ret = VdkSeekFile(hFile,
			((VDiskExtVmdk *)m_ppExtents[0])->GetHeader()->DescOffsetLow);

		if (ret != VDK_OK) {
			goto cleanup;
		}
	}

	//
	//	create header entries
	//
	sprintf(buf,
		"# Disk DescriptorFile\n"
		"version=1\n"
		"CID=%08lx\n"
		"parentCID=%08lx\n"
		"createType=\"%s%s\"\n",

		m_nTimeStamp,
		m_nParentTS,
		(m_nFlags & VDISK_FLAG_SINGLE) ? "monolithic" : "twoGbMaxExtent",
		(m_nFlags & VDISK_FLAG_SPARSE) ? "Sparse" : "Flat");

	ret = VdkWriteFileAt(hFile, -1, buf, strlen(buf), NULL);

	if (ret != VDK_OK) {
		goto cleanup;
	}

	if (m_nFlags & VDISK_FLAG_CHILD) {
		sprintf(buf, "parentFileNameHint=\"%s\"\n", m_pParentPath);

		ret = VdkWriteFileAt(hFile, -1, buf, strlen(buf), NULL);

		if (ret != VDK_OK) {
			goto cleanup;
		}
	}

	//
	//	create each extent entry
	//
	strcpy(buf, "\n# Extent description\n");

	ret = VdkWriteFileAt(hFile, -1, buf, strlen(buf), NULL);

	if (ret != VDK_OK) {
		goto cleanup;
	}

	path_len = strlen(m_pPath);

	for (idx = 0; idx < m_nExtents; idx++) {
		VDiskExt *ext = *(m_ppExtents + idx);
		PCHAR path;

		path = ext->GetFullPath();

		if (!(m_nFlags & VDISK_FLAG_ABSPATH) &&
			!VdkCmpNoCaseN(m_pPath, path, path_len)) {

			path = ext->GetFileName();
		}

		if (ext->GetFileType() == VDK_FILETYPE_VMDK) {
			sprintf(buf, "RW %lu SPARSE \"%s\"\n",
				ext->GetCapacity(),
				path);
		}
		else {
			sprintf(buf, "RW %lu FLAT \"%s\" %lu\n",
				ext->GetCapacity(),
				path,
				((VDiskExtRaw *)ext)->GetBackOffset());
		}

		ret = VdkWriteFileAt(hFile, -1, buf, strlen(buf), NULL);

		if (ret != VDK_OK) {
			goto cleanup;
		}
	}

	//
	//	create trailing entries
	//
	strcpy(buf,
		"\n# The Disk Data Base \n"
		"#DDB\n\n");

	ret = VdkWriteFileAt(hFile, -1, buf, strlen(buf), NULL);

	if (ret != VDK_OK) {
		goto cleanup;
	}

	if (!(m_nFlags & VDISK_FLAG_CHILD)) {
		sprintf(buf,
			"ddb.virtualHWVersion = \"%lu\"\n"
			"ddb.geometry.cylinders = \"%lu\"\n"
			"ddb.geometry.heads = \"%lu\"\n"
			"ddb.geometry.sectors = \"%lu\"\n"
			"ddb.adapterType = \"%s\"\n",
			m_nHardwareVer,
			m_nCylinders,
			m_nTracks,
			m_nSectors,
			(m_nController == VDISK_CONTROLLER_IDE) ? "ide" : "buslogic");

		ret = VdkWriteFileAt(hFile, -1, buf, strlen(buf), NULL);

		if (ret != VDK_OK) {
			goto cleanup;
		}
	}


cleanup:
	if (hFile != INVALID_HANDLE_VALUE) {
		VdkCloseFile(hFile);
	}

	return ret;
}

//
//	Create a new extent object for this disk instance
//
VDiskExt *VDiskVmdk::NewExtent()
{
	if (m_nFlags & VDISK_FLAG_SPARSE) {
		return new VDiskExtVmdk;
	}
	else {
		return new VDiskExtRaw;
	}
}

//
//	Get path for each extent files
//
void VDiskVmdk::GetExtentPath(
	PCHAR pPath, ULONG nSeq)
{
	if (m_nFlags & VDISK_FLAG_SPARSE) {
		if (m_nFlags & VDISK_FLAG_SINGLE) {
			sprintf(pPath, "%s" PATH_SEPARATOR_STR "%s.%s",
				m_pPath, m_pBody, m_pExtension);
		}
		else {
			sprintf(pPath, "%s" PATH_SEPARATOR_STR "%s-s%03lu.%s",
				m_pPath, m_pBody, nSeq + 1, m_pExtension);
		}
	}
	else {
		if (m_nFlags & VDISK_FLAG_SINGLE) {
			sprintf(pPath, "%s" PATH_SEPARATOR_STR "%s-flat.%s",
				m_pPath, m_pBody, m_pExtension);
		}
		else {
			sprintf(pPath, "%s" PATH_SEPARATOR_STR "%s-f%03lu.%s",
				m_pPath, m_pBody, nSeq + 1, m_pExtension);
		}
	}
}

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

		m_nTracks	= VMDK_TRACKS_IDE;
		m_nSectors	= VMDK_SECTORS_IDE;
	}
	else {
		if (m_nCapacity <=
			VMDK_SECTORS_SCSI_1023M * VMDK_TRACKS_SCSI_1023M * 1023) {

			m_nSectors	= VMDK_SECTORS_SCSI_1023M;
			m_nTracks	= VMDK_TRACKS_SCSI_1023M;
		}
		else if (m_nCapacity <=
			VMDK_SECTORS_SCSI_2046M * VMDK_TRACKS_SCSI_2046M * 1023) {

			m_nSectors	= VMDK_SECTORS_SCSI_2046M;
			m_nTracks	= VMDK_TRACKS_SCSI_2046M;
		}
		else {
			m_nSectors	= VMDK_SECTORS_SCSI_LARGE;
			m_nTracks	= VMDK_TRACKS_SCSI_LARGE;
		}
	}

	m_nCylinders = m_nCapacity / (m_nSectors * m_nTracks);
}

//
//	Set default Timestamp values;
//
void VDiskVmdk::SetDefaultTS()
{
	m_nParentTS	= (ULONG)-1;
	m_nTimeStamp = (ULONG)-2;
}

//
//	Get default extent size
//
ULONG VDiskVmdk::DefaultExtSize()
{
	ULONG ext_size;

	if (m_nFlags & VDISK_FLAG_SINGLE) {
		ext_size = m_nCapacity;
	}
	else {
		if (m_nFlags & VDISK_FLAG_SPARSE) {
			ext_size = VMDK_MAX_EXTENT_SPARSE;
		}
		else {
			ext_size = VMDK_MAX_EXTENT_SOLID;
		}

		if (ext_size > m_nCapacity) {
			ext_size = m_nCapacity;
		}
	}

	return ext_size;
}

//
//	Check VMDK sparse disk
//
VDKSTAT VDiskVmdk::Check()
{
	PVOID	cbparams[3];
	CHAR	path[MAX_PATH];
	ULONG	idx;
	VDKSTAT ret;

	FullPath(path);
	cbparams[0] = path;

	//
	//	At least one extent must be present
	//
	if (!m_nExtents) {
		VDiskCallBack(VDISK_CB_EMPTY_IMAGE, cbparams);
		return VDK_DATA;
	}

	//
	//	Sum up extent capacity
	//
	m_nCapacity = 0;

	for (idx = 0; idx < m_nExtents; idx++) {
		if ((ret = m_ppExtents[idx]->Check()) != VDK_OK) {
			return ret;
		}

		if (m_ppExtents[idx]->IsModified()) {
			SetFlag(VDISK_FLAG_DIRTY);
		}

		m_nCapacity += m_ppExtents[idx]->GetCapacity();
	}

	//
	//	Check total capacity
	//
	/*
	if (total_size != m_nCapacity) {

		cbparams[1] = (PVOID)m_nCapacity;
		cbparams[2] = (PVOID)total_size;

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

		m_nCapacity	= total_size;
		SetGeometry();

		SetFlag(VDISK_FLAG_DIRTY);

		for (idx = 0; idx < m_nExtents; idx++) {
			if (total_size < m_ppExtents[idx]->GetCapacity()) {
				m_ppExtents[idx]->SetCapacity(total_size);
				total_size = 0;
			}
			else {
				total_size -= m_ppExtents[idx]->GetCapacity();
			}
		}
	}
	*/

	//
	//	Any bad parameter?
	//
	if (m_nFlags & VDISK_FLAG_DIRTY) {

		if (VDiskCallBack(VDISK_CB_CONFIRM_FIX, cbparams)) {
			ret = WriteDescriptor(FALSE, FALSE);

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

			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;
}