www.pudn.com > vdksrc.zip > vdkopen.c
/*
vdkopen.c
Virtual Disk open functions
Copyright (C) 2003 Ken Kato
*/
#include "vdkbase.h"
#include "vdkutil.h"
#include "vdkfile.h"
#include "vdkaccess.h"
#include "cowdisk.h"
#include "vmdisk.h"
//
// local functions
//
static VDKSTAT VdkSetCowdParam(
PVDK_FILE_INFO DiskInfo,
ULONG ReadOnly);
static VDKSTAT VdkSetVmdkParam(
PVDK_FILE_INFO DiskInfo,
ULONG ReadOnly);
//
// declare pageable functions
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VdkOpenCheckParam)
#pragma alloc_text(PAGE, VdkOpenDisk)
#pragma alloc_text(PAGE, VdkSetCowdParam)
#pragma alloc_text(PAGE, VdkSetVmdkParam)
#endif // ALLOC_PRAGMA
//
// Check open file info integrity
//
VDKSTAT VdkOpenCheckParam(
PVDK_OPEN_FILE_INFO OpenInfo,
ULONG InputLen)
{
ULONG fixed_size;
ULONG total_sectors;
ULONG total_name_len;
ULONG generation;
ULONG idx;
//
// check minimum length
//
if (InputLen < sizeof(VDK_OPEN_FILE_INFO)) {
return VDK_BUFFER;
}
//
// check DiskType
//
if (OpenInfo->DiskType != VDK_DISKTYPE_WRITABLE &&
OpenInfo->DiskType != VDK_DISKTYPE_READONLY &&
OpenInfo->DiskType != VDK_DISKTYPE_WRITEBLOCK) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: Invalid DiskType %u\n",
OpenInfo->DiskType));
return VDK_PARAM;
}
//
// There must be at least one file to open
//
if (OpenInfo->FilesTotal == 0 || OpenInfo->Capacity == 0) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: disk size == 0\n"));
return VDK_PARAM;
}
//
// If geometry values are specified, they must be
// consistent with the number of total sectors
//
if (OpenInfo->Cylinders *
OpenInfo->Tracks *
OpenInfo->Sectors > OpenInfo->Capacity) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: geometry / capacity conflict\n"));
return VDK_PARAM;
}
//
// Calculate the minimum necessary size for
// the VDK_OEPN_DISK and all VDK_OPEN_FILE_ITEM structures.
//
fixed_size =
FIELD_OFFSET(VDK_OPEN_FILE_INFO, Files) +
(sizeof(VDK_OPEN_FILE_ITEM) * OpenInfo->FilesTotal);
//
// Recheck input parameter length
// -- large enough to contain all fixed info?
//
if (InputLen < fixed_size) {
return VDK_BUFFER;
}
//
// Check each file info
//
total_sectors = 0;
total_name_len = 0;
generation = 0;
idx = 0;
do {
PVDK_OPEN_FILE_ITEM open_file;
//
// file item to work with
//
open_file = &(OpenInfo->Files[idx]);
//
// check file type
//
if (open_file->FileType != VDK_FILETYPE_NONE &&
open_file->FileType != VDK_FILETYPE_FLAT &&
open_file->FileType != VDK_FILETYPE_COWD &&
open_file->FileType != VDK_FILETYPE_VMDK) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: File #%u Invalid FileType %u\n",
idx, open_file->FileType));
return VDK_PARAM;
}
//
// Top level files cannot be missing if disk is writable
//
if (!generation &&
OpenInfo->DiskType == VDK_DISKTYPE_WRITABLE &&
open_file->FileType == VDK_FILETYPE_NONE) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: Missing top level file #%u\n",
idx));
return VDK_PARAM;
}
//
// check file length
//
if (open_file->Capacity == 0 ||
open_file->Capacity > OpenInfo->Capacity) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: File #%u invalid size %u\n",
idx, open_file->Capacity));
return VDK_PARAM;
}
//
// if the file is not missing, file name must be provided
//
if (open_file->FileType != VDK_FILETYPE_NONE &&
open_file->NameLength == 0) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: File #%u filename required\n",
idx));
return VDK_PARAM;
}
//
// BackOffset can only be used for solid files
//
if (open_file->FileType != VDK_FILETYPE_FLAT &&
open_file->BackOffset != 0) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: File #%u BackOffset #%lu\n",
idx, open_file->BackOffset));
return VDK_PARAM;
}
//
// Sum up the file length and name length
//
total_sectors += open_file->Capacity;
total_name_len += open_file->NameLength;
//
// completed one generation?
//
if (total_sectors == OpenInfo->Capacity) {
//
// prepare for the next generation
//
generation++;
total_sectors = 0;
}
else if (total_sectors > OpenInfo->Capacity) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: Size mismatch at File #%u\n",
idx));
return VDK_PARAM;
}
}
while (++idx < OpenInfo->FilesTotal);
//
// The last generation is complete?
//
if (total_sectors) {
VDKTRACE(VDKOPEN,
("[VDK] IOCTL_VDK_OPEN_FILE: Size mismatch at File #%u\n",
idx - 1));
return VDK_PARAM;
}
//
// now total name length is known, check buffer length again
//
if (InputLen < fixed_size + total_name_len) {
return VDK_BUFFER;
}
return VDK_OK;
}
//
// Open image file(s)
//
VDKSTAT VdkOpenDisk(
PVDK_OPEN_FILE_INFO OpenFile,
PVDK_DISK_INFO DiskInfo)
{
PCHAR name_top;
ULONG name_total;
ULONG start_sector;
ULONG generation;
ULONG read_only;
ULONG idx;
VDKSTAT status = VDK_OK;
//
// store disk information
//
VdkZeroMem(DiskInfo, sizeof(VDK_DISK_INFO));
DiskInfo->DiskType = OpenFile->DiskType;
DiskInfo->Capacity = OpenFile->Capacity;
if (OpenFile->Cylinders && OpenFile->Tracks && OpenFile->Sectors) {
DiskInfo->Cylinders = OpenFile->Cylinders;
DiskInfo->Tracks = OpenFile->Tracks;
DiskInfo->Sectors = OpenFile->Sectors;
}
else {
//
// make up suitable values
//
DiskInfo->Sectors = VDK_SECTORS_PER_TRACK;
DiskInfo->Tracks = VDK_TRACKS_PER_CYLINDER;
DiskInfo->Cylinders =
OpenFile->Capacity / VDK_SECTORS_PER_TRACK / VDK_TRACKS_PER_CYLINDER;
}
DiskInfo->FilesTotal = OpenFile->FilesTotal;
//
// allocate buffer to store file information
//
DiskInfo->Files = (PVDK_FILE_INFO)VdkAllocMem(
sizeof(VDK_FILE_INFO) * OpenFile->FilesTotal);
if (!DiskInfo->Files) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for disk info\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
VdkZeroMem(DiskInfo->Files,
sizeof(VDK_FILE_INFO) * OpenFile->FilesTotal);
//
// process each files
//
name_top = (PCHAR)OpenFile +
FIELD_OFFSET(VDK_OPEN_FILE_INFO, Files) +
(sizeof(VDK_OPEN_FILE_ITEM) * OpenFile->FilesTotal);
name_total = 0;
start_sector = 0;
generation = 0;
read_only = (OpenFile->DiskType != VDK_DISKTYPE_WRITABLE);
for (idx = 0; idx < DiskInfo->FilesTotal; idx++) {
PVDK_FILE_INFO file_info = &DiskInfo->Files[idx];
//
// set up basic parameters
//
file_info->FileType = OpenFile->Files[idx].FileType;
file_info->Capacity = OpenFile->Files[idx].Capacity;
file_info->NameLength = OpenFile->Files[idx].NameLength;
file_info->StartSector = start_sector;
//
// skip "Missing" files
//
if (file_info->FileType == VDK_FILETYPE_NONE) {
goto next_file;
}
//
// open file
//
status = VdkOpenFile(
&(file_info->FileHandle),
name_top + name_total,
file_info->NameLength,
read_only);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
//
// check file attributes
//
status = VdkCheckAttribute(
file_info->FileHandle);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
//
// get file size
//
{
INT64 size;
status = VdkGetFileSize(file_info->FileHandle, &size);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
file_info->EndOfFile = (ULONG)(size >> VDK_BYTE_SHIFT_TO_SECTOR);
}
//
// prepare type specific parameters
//
if (file_info->FileType == VDK_FILETYPE_FLAT) {
file_info->prm.SolidBackOffset =
OpenFile->Files[idx].BackOffset;
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] BackOffset = %lu\n", file_info->prm.SolidBackOffset));
}
else if (file_info->FileType == VDK_FILETYPE_COWD) {
status = VdkSetCowdParam(file_info, read_only);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
}
else if (file_info->FileType == VDK_FILETYPE_VMDK) {
status = VdkSetVmdkParam(file_info, read_only);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
}
next_file:
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] FileType = %lu\n", file_info->FileType));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] StartSector = %lu\n", file_info->StartSector));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] Capacity = %lu\n", file_info->Capacity));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] EndOfFile = %lu\n", file_info->EndOfFile));
//
// calculate next file's start sector
//
start_sector += file_info->Capacity;
//
// sum up name length
//
name_total += file_info->NameLength;
//
// completed one generation?
//
if (start_sector == OpenFile->Capacity) {
start_sector = 0;
generation++;
read_only = TRUE;
}
} // next file
//
// Store filenames
//
DiskInfo->NameBuffer = VdkAllocMem(name_total);
if (!DiskInfo->NameBuffer) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for image path\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
DiskInfo->BufferLen = name_total;
VdkCopyMem(DiskInfo->NameBuffer, name_top, name_total);
cleanup_exit:
if (!VDKSUCCESS(status)) {
VdkCloseDisk(DiskInfo);
}
return status;
}
//
// Prepare extra parameters for COWD
//
VDKSTAT VdkSetCowdParam(
PVDK_FILE_INFO FileInfo,
ULONG ReadOnly)
{
PVDK_COWD_PARAM cowd_prm = NULL;
PCOWD_SECTOR_0 cowd_hdr = NULL;
VDKSTAT status = VDK_OK;
//
// Allocate COWD parameter area
//
cowd_prm = (PVDK_COWD_PARAM)VdkAllocMem(
sizeof(VDK_COWD_PARAM));
if (!cowd_prm) {
VDKTRACE(VDKOPEN,
("[VDK] Failed to allocate cowd parameter\n"));
return VDK_NOMEMORY;
}
VdkZeroMem(cowd_prm, sizeof(VDK_COWD_PARAM));
FileInfo->prm.cowd = cowd_prm;
//
// Allocate COWD header area
//
cowd_hdr = (PCOWD_SECTOR_0)VdkAllocMem(
VDK_BYTES_PER_SECTOR);
if (!cowd_hdr) {
VDKTRACE(VDKOPEN,
("[VDK] Failed to allocate cowd header buffer\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Read COWD header
//
status = VdkReadFileAt(
FileInfo->FileHandle,
0,
cowd_hdr,
VDK_BYTES_PER_SECTOR,
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
//
// set up COWD parameters
//
cowd_prm->PrimaryMapSize =
(((cowd_hdr->PrimaryMapSize * sizeof(ULONG)) +
(VDK_BYTES_PER_SECTOR - 1)) & ~VDK_SECTOR_ALIGNMENT_MASK)
/ sizeof(ULONG);
cowd_prm->SecondaryMapSize = COWD_SECONDARY_MAP_SIZE;
cowd_prm->PrimaryGranularity =
cowd_hdr->Granularity * cowd_prm->SecondaryMapSize;
cowd_prm->SecondaryGranularity =
cowd_hdr->Granularity;
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] PrimaryMap size = %lu, granularity = %lu\n",
cowd_prm->PrimaryMapSize, cowd_prm->PrimaryGranularity));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] Secondary size = %lu, granularity = %lu\n",
cowd_prm->SecondaryMapSize, cowd_prm->SecondaryGranularity));
//
// Check parameter logical consistency
//
if (cowd_prm->PrimaryMapSize * cowd_prm->PrimaryGranularity
< FileInfo->Capacity) {
//
// Sector map cannot cover whole sectors in this file
//
VDKTRACE(VDKOPEN,
("[VDK] map is too small\n"));
status = VDK_PARAM;
goto cleanup_exit;
}
if (cowd_hdr->PrimaryMapOffset < 4 ||
cowd_hdr->PrimaryMapOffset >= FileInfo->EndOfFile) {
//
// PrimaryMapOffset is too large for this file.
// Probably the file is corrupt.
//
VDKTRACE(VDKOPEN,
("[VDK] Invalid PrimaryMapOffset %u\n",
cowd_hdr->PrimaryMapOffset));
status = VDK_PARAM;
goto cleanup_exit;
}
//
// Prepare PrimaryMap buffer
//
cowd_prm->PrimaryMap = (PULONG)VdkAllocMem(
cowd_prm->PrimaryMapSize * sizeof(ULONG));
if (!cowd_prm->PrimaryMap) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for primary map\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Read primary map
//
status = VdkReadFileAt(
FileInfo->FileHandle,
(INT64)cowd_hdr->PrimaryMapOffset <<
VDK_BYTE_SHIFT_TO_SECTOR,
cowd_prm->PrimaryMap,
cowd_prm->PrimaryMapSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
//
// Prepare SecondaryMap buffer
//
cowd_prm->SecondaryMap = (PULONG)VdkAllocMem(
cowd_prm->SecondaryMapSize * sizeof(ULONG));
if (!cowd_prm->SecondaryMap) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for secondary map\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Read secondary map
//
if (cowd_prm->PrimaryMap[0] <= cowd_hdr->PrimaryMapOffset ||
cowd_prm->PrimaryMap[0] >= FileInfo->EndOfFile) {
//
// Secondary Map is not allocated or too large for this file.
//
VDKTRACE(VDKOPEN | VDKWARN,
("[VDK] Invalid SecondaryMapOffset %u\n",
cowd_prm->PrimaryMap[0]));
VdkZeroMem(
cowd_prm->SecondaryMap,
cowd_prm->SecondaryMapSize * sizeof(ULONG));
}
else {
status = VdkReadFileAt(
FileInfo->FileHandle,
(INT64)cowd_prm->PrimaryMap[0] <<
VDK_BYTE_SHIFT_TO_SECTOR,
cowd_prm->SecondaryMap,
cowd_prm->SecondaryMapSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
}
cleanup_exit:
if (cowd_hdr) {
if (VDKSUCCESS(status) && !ReadOnly) {
FileInfo->prm.cowd->Sector0 = cowd_hdr;
}
else {
VdkFreeMem(cowd_hdr);
}
}
return status;
}
//
// Prepare extra parameters for VMDK files
//
VDKSTAT VdkSetVmdkParam(
PVDK_FILE_INFO FileInfo,
ULONG ReadOnly)
{
PVDK_VMDK_PARAM vmdk_prm = NULL;
PVMDK_HEADER vmdk_hdr = NULL;
PVOID read_buf = NULL;
ULONG read_len;
VDKSTAT status = VDK_OK;
//
// Allocate VMDK parameter area
//
vmdk_prm = (PVDK_VMDK_PARAM)VdkAllocMem(sizeof(VDK_VMDK_PARAM));
if (!vmdk_prm) {
VDKTRACE(VDKOPEN,
("[VDK] Failed to allocate VMDK parameter\n"));
return VDK_NOMEMORY;
}
VdkZeroMem(vmdk_prm, sizeof(VDK_VMDK_PARAM));
FileInfo->prm.vmdk = vmdk_prm;
//
// Allocate VMDK header area
//
vmdk_hdr = (PVMDK_HEADER)VdkAllocMem(VDK_BYTES_PER_SECTOR);
if (!vmdk_hdr) {
status = VDK_NOMEMORY;
VDKTRACE(VDKOPEN,
("[VDK] Failed to allocate VMDK header buffer\n"));
goto cleanup_exit;
}
//
// Read VMDK header
//
status = VdkReadFileAt(
FileInfo->FileHandle,
0,
vmdk_hdr,
VDK_BYTES_PER_SECTOR,
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
//
// Check parameter logical consistency
//
if (!vmdk_hdr->CapacityLow ||
!vmdk_hdr->numGTEsPerGT ||
!vmdk_hdr->GranularityLow ||
!vmdk_hdr->rgdOffsetLow ||
!vmdk_hdr->gdOffsetLow ||
vmdk_hdr->CapacityHigh ||
vmdk_hdr->GranularityHigh ||
vmdk_hdr->rgdOffsetHigh ||
vmdk_hdr->gdOffsetHigh ||
vmdk_hdr->rgdOffsetLow >= FileInfo->EndOfFile ||
vmdk_hdr->gdOffsetLow >= FileInfo->EndOfFile) {
//
// Directory Offset is too large for this file.
// Probably the file is corrupt.
//
VDKTRACE(VDKOPEN,
("[VDK] Invalid VMDK header param\n"));
status = VDK_PARAM;
goto cleanup_exit;
}
if (vmdk_hdr->CapacityLow < FileInfo->Capacity) {
//
// VMDK file cannot cover specified capacity
//
VDKTRACE(VDKOPEN,
("[VDK] VMDK capacity mismatch\n"));
status = VDK_PARAM;
goto cleanup_exit;
}
//
// set up VMDK parameters
//
vmdk_prm->GrainTableSize = vmdk_hdr->numGTEsPerGT;
vmdk_prm->SectorsPerGrain = vmdk_hdr->GranularityLow;
vmdk_prm->SectorsPerTable =
vmdk_prm->GrainTableSize * vmdk_prm->SectorsPerGrain;
vmdk_prm->DirectorySize =
(vmdk_hdr->CapacityLow + vmdk_prm->SectorsPerTable - 1)
/ vmdk_prm->SectorsPerTable;
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] Directory size = %lu\n",
vmdk_prm->DirectorySize));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] GrainTableSize = %lu\n",
vmdk_prm->GrainTableSize));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] SectorsPerTable = %lu\n",
vmdk_prm->SectorsPerTable));
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] SectorsPerGrain = %lu\n",
vmdk_prm->SectorsPerGrain));
//
// Prepare Directory read buffer
//
read_len = (vmdk_prm->DirectorySize * sizeof(ULONG) + VDK_BYTES_PER_SECTOR - 1)
& ~VDK_SECTOR_ALIGNMENT_MASK;
read_buf = VdkAllocMem(read_len);
if (!read_buf) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for read buffer\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Prepare Primary Directory buffer
//
vmdk_prm->PrimaryDirectory = (PULONG)VdkAllocMem(
vmdk_prm->DirectorySize * sizeof(ULONG));
if (!vmdk_prm->PrimaryDirectory) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for PrimaryDirectory\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Read Primary Directory
//
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] Reading Primary Directory at offset 0x%08x\n",
vmdk_hdr->rgdOffsetLow));
status = VdkReadFileAt(
FileInfo->FileHandle,
(INT64)vmdk_hdr->rgdOffsetLow << VDK_BYTE_SHIFT_TO_SECTOR,
read_buf,
read_len,
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
VdkCopyMem(vmdk_prm->PrimaryDirectory, read_buf,
vmdk_prm->DirectorySize * sizeof(ULONG));
if (!ReadOnly) {
//
// Prepare Backup Directory buffer
//
vmdk_prm->BackupDirectory = (PULONG)VdkAllocMem(
vmdk_prm->DirectorySize * sizeof(ULONG));
if (!vmdk_prm->BackupDirectory) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for BackupDirectory\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Read Backup Directory
//
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] Reading Backup Directory at offset 0x%08x\n",
vmdk_hdr->gdOffsetLow));
status = VdkReadFileAt(
FileInfo->FileHandle,
(INT64)vmdk_hdr->gdOffsetLow << VDK_BYTE_SHIFT_TO_SECTOR,
read_buf,
read_len,
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
VdkCopyMem(vmdk_prm->BackupDirectory, read_buf,
vmdk_prm->DirectorySize * sizeof(ULONG));
}
//
// Prepare Grain Table buffer
//
vmdk_prm->GrainTable = (PULONG)VdkAllocMem(
vmdk_prm->GrainTableSize * sizeof(ULONG));
if (!vmdk_prm->GrainTable) {
VDKTRACE(VDKOPEN,
("[VDK] Can't allocate memory for Grain Table\n"));
status = VDK_NOMEMORY;
goto cleanup_exit;
}
//
// Read Grain Table
//
if (vmdk_prm->PrimaryDirectory[0] <= vmdk_hdr->rgdOffsetLow ||
vmdk_prm->PrimaryDirectory[0] >= FileInfo->EndOfFile) {
//
// Secondary Map is not allocated or too large for this file.
//
VDKTRACE(VDKOPEN,
("[VDK] Invalid Grain Table[0] Offset %u\n",
vmdk_prm->PrimaryDirectory[0]));
VdkZeroMem(
vmdk_prm->GrainTable,
vmdk_prm->GrainTableSize * sizeof(ULONG));
}
else {
VDKTRACE(VDKOPEN | VDKINFO,
("[VDK] Reading Grain Table[0] at offset 0x%08x\n",
vmdk_prm->PrimaryDirectory[0]));
status = VdkReadFileAt(
FileInfo->FileHandle,
(INT64)vmdk_prm->PrimaryDirectory[0] <<
VDK_BYTE_SHIFT_TO_SECTOR,
vmdk_prm->GrainTable,
vmdk_prm->GrainTableSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
}
cleanup_exit:
if (vmdk_hdr) {
VdkFreeMem(vmdk_hdr);
}
if (read_buf) {
VdkFreeMem(read_buf);
}
return status;
}