www.pudn.com > vdksrc.zip > vdkread.c
/*
vdkread.c
Virtual Disk read function
Copyright (C) 2003 Ken Kato
*/
#include "vdkbase.h"
#include "vdkutil.h"
#include "vdkaccess.h"
#include "cowdisk.h"
#include "vmdisk.h"
//
// Read requested sectors from virtual disk
//
VDKSTAT VdkReadSector(
PVDK_DISK_INFO DiskInfo,
ULONG Offset,
ULONG Length,
PUCHAR Buffer)
{
PVDK_FILE_INFO top_level_file;
PVDK_FILE_INFO no_more_file;
ULONG top_level_offset;
VDKSTAT status;
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] VdkRead - Request Offset sec. %u, length %u\n",
Offset, Length));
if (DiskInfo == NULL || DiskInfo->Files == NULL) {
VDKTRACE(VDKREAD,
("[VDK] null DiskInfo\n"));
return VDK_INTERNAL;
}
if (Offset + Length > DiskInfo->Capacity) {
VDKTRACE(VDKREAD,
("[VDK] request out of range\n"));
return VDK_INTERNAL;
}
if (Buffer == NULL) {
VDKTRACE(VDKREAD,
("[VDK] null read buffer\n"));
return VDK_INTERNAL;
}
top_level_file = DiskInfo->Files;
no_more_file = &(DiskInfo->Files[DiskInfo->FilesTotal]);
top_level_offset = Offset;
//
// Start reading data from file
//
while (Length) {
PVDK_FILE_INFO file_info;
ULONG sector_map_idx;
ULONG read_offset;
ULONG read_length;
ULONG actual_length;
//
// look for a top level file containing the target sector
//
while (top_level_offset >= top_level_file->Capacity) {
//
// data is stored past this file
//
top_level_offset -= top_level_file->Capacity;
top_level_file++;
}
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Checking file #%u.\n",
top_level_file - DiskInfo->Files));
//
// current file info pointer to work with
//
file_info = top_level_file;
//
// offset in a allocation block
// The whole file is one block for plain files.
//
read_offset = top_level_offset;
//
// maximum data which could be read at once
//
read_length = file_info->Capacity;
//
// initial sector map index number
//
sector_map_idx = 0;
//
// Walk through the file generation chain to find a file
// which actually contains target sector range
//
for (;;) {
if (file_info->FileType == VDK_FILETYPE_FLAT) {
//
// This file contains the target sector
//
break;
}
else if (file_info->FileType == VDK_FILETYPE_COWD) {
//
// check for the smallest block size
//
if (read_length > file_info->prm.cowd->SecondaryGranularity) {
read_length = file_info->prm.cowd->SecondaryGranularity;
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Using Granularity %u\n", read_length));
}
//
// check secondary map allocation
//
sector_map_idx =
read_offset / file_info->prm.cowd->PrimaryGranularity;
if (sector_map_idx >= file_info->prm.cowd->PrimaryMapSize) {
//
// offset exceeds the primary map size
// Probably COWD header is corrupt and inconsistent
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Primary Map overflow\n"));
file_info = NULL;
break;
}
if (file_info->prm.cowd->PrimaryMap[sector_map_idx]) {
//
// Secondary map is allocated in the file
//
if (file_info->prm.cowd->SecondaryMapIdx != sector_map_idx) {
//
// Need to read secondary map from the file
//
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Loading Secondary Map #%u\n",
sector_map_idx));
if (file_info->prm.cowd->PrimaryMap[sector_map_idx] >=
file_info->EndOfFile) {
//
// Secondary Map Offset is too large
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Invalid Secondary Map Offset 0x%x\n",
file_info->prm.cowd->PrimaryMap[sector_map_idx]));
VdkZeroMem(
file_info->prm.cowd->SecondaryMap,
file_info->prm.cowd->SecondaryMapSize * sizeof(ULONG));
}
else {
status = VdkReadFileAt(
file_info->FileHandle,
(INT64)file_info->prm.cowd->PrimaryMap[sector_map_idx] <<
VDK_BYTE_SHIFT_TO_SECTOR,
file_info->prm.cowd->SecondaryMap,
file_info->prm.cowd->SecondaryMapSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
}
file_info->prm.cowd->SecondaryMapIdx = sector_map_idx;
}
//
// check the target sector allocation
//
sector_map_idx =
(read_offset % file_info->prm.cowd->PrimaryGranularity) /
file_info->prm.cowd->SecondaryGranularity;
if (sector_map_idx >= file_info->prm.cowd->SecondaryMapSize) {
//
// offset exceeds the secondary map size
// Probably COWD header is corrupt and inconsistent
// Or I have to find out how to get non-fixed secondary map size.
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Secondary Map overflow\n"));
file_info = NULL;
break;
}
if (file_info->prm.cowd->SecondaryMap[sector_map_idx] &&
file_info->prm.cowd->SecondaryMap[sector_map_idx] < file_info->EndOfFile) {
//
// Start sector is allocated in this file
//
break;
}
else if (file_info->prm.cowd->SecondaryMap[sector_map_idx] >= file_info->EndOfFile) {
//
// sector block offset exceeds current end of file
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Sector block offset too large\n"));
file_info = NULL;
break;
}
//
// sector block is not allocated
//
}
}
else if (file_info->FileType == VDK_FILETYPE_VMDK) {
//
// check for the smallest block size
//
if (read_length > file_info->prm.vmdk->SectorsPerGrain) {
read_length = file_info->prm.vmdk->SectorsPerGrain;
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Using Granularity %u\n",
read_length));
}
//
// Get grain table index
//
sector_map_idx =
read_offset / file_info->prm.vmdk->SectorsPerTable;
if (sector_map_idx >= file_info->prm.vmdk->DirectorySize ||
!file_info->prm.vmdk->PrimaryDirectory[sector_map_idx] ||
file_info->prm.vmdk->PrimaryDirectory[sector_map_idx]
>= file_info->EndOfFile) {
//
// Grain table is not present;
// something's wrong with VMDK header
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Cannot locate Grain Table %lu\n",
sector_map_idx));
file_info = NULL;
break;
}
if (file_info->prm.vmdk->GrainTableIdx != sector_map_idx) {
//
// Need to read the grain table from the file
//
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Loading Grain Table #%u\n",
sector_map_idx));
status = VdkReadFileAt(
file_info->FileHandle,
(INT64)file_info->prm.vmdk->PrimaryDirectory[sector_map_idx]
<< VDK_BYTE_SHIFT_TO_SECTOR,
file_info->prm.vmdk->GrainTable,
file_info->prm.vmdk->GrainTableSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
file_info->prm.vmdk->GrainTableIdx = sector_map_idx;
}
//
// Find grain offset
//
sector_map_idx =
(read_offset % file_info->prm.vmdk->SectorsPerTable)
/ file_info->prm.vmdk->SectorsPerGrain;
if (sector_map_idx >= file_info->prm.vmdk->GrainTableSize) {
//
// offset exceeds the grain table size
// Probably VMDK header is corrupt and inconsistent
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Grain Table overflow\n"));
file_info = NULL;
break;
}
if (file_info->prm.vmdk->GrainTable[sector_map_idx] &&
file_info->prm.vmdk->GrainTable[sector_map_idx] < file_info->EndOfFile) {
//
// Target sector is allocated in this file
//
break;
}
else if (file_info->prm.vmdk->GrainTable[sector_map_idx] >= file_info->EndOfFile) {
//
// Grain offset exceeds current end of file
//
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Grain offset too large\n"));
file_info = NULL;
break;
}
//
// Grain is not allocated
//
}
//
// Look for the first file of the next generation
//
while (++file_info < no_more_file) {
if (file_info->StartSector == 0) {
//
// Found it
//
break;
}
}
if (file_info == no_more_file) {
//
// No more generation
//
file_info = NULL;
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] No more parent.\n"));
break;
}
//
// look for a file containing the target sector
//
read_offset = Offset;
while (read_offset >= file_info->Capacity) {
//
// data is stored past this file
//
read_offset -= file_info->Capacity;
file_info++;
}
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Checking file #%u.\n",
file_info - DiskInfo->Files));
} // for (;;)
//
// How many sectors can be read at once?
//
read_offset %= read_length;
read_length -= read_offset;
if (read_length > Length) {
read_length = Length;
}
//
// If the Target sector is not allocated on any file.
// Pretend it to be filled with zero.
//
if (file_info == NULL ||
file_info->FileType == VDK_FILETYPE_NONE) {
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Sector not present. %u sectors filled with Zero.\n",
read_length));
VdkZeroMem(Buffer,
read_length << VDK_BYTE_SHIFT_TO_SECTOR);
goto next_block;
}
//
// Get the actual sector offset in the file
//
if (file_info->FileType == VDK_FILETYPE_FLAT) {
read_offset += file_info->prm.SolidBackOffset;
}
if (file_info->FileType == VDK_FILETYPE_COWD) {
read_offset +=
file_info->prm.cowd->SecondaryMap[sector_map_idx];
}
else if (file_info->FileType == VDK_FILETYPE_VMDK) {
read_offset +=
file_info->prm.vmdk->GrainTable[sector_map_idx];
}
//
// Check if whole sector range is acturally presend in this file
//
actual_length = read_length;
if (read_offset + read_length > file_info->EndOfFile) {
if (read_offset > file_info->EndOfFile) {
//
// Offset is too large
//
actual_length = 0;
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Invalid Offset 0x%lu\n",
read_offset));
}
else {
//
// Length is too large. Data is clipped.
//
actual_length = file_info->EndOfFile - read_offset;
VDKTRACE(VDKREAD | VDKWARN,
("[VDK] Only %u sectors can be read\n",
actual_length));
}
VdkZeroMem(
Buffer + (actual_length << VDK_BYTE_SHIFT_TO_SECTOR),
(read_length - actual_length) << VDK_BYTE_SHIFT_TO_SECTOR);
}
if (!actual_length) {
goto next_block;
}
//
// Now read
//
VDKTRACE(VDKREAD | VDKINFO,
("[VDK] Reading %u sectors from file #%u, byte offset 0x%"INT64_PRINT_FORMAT"x\n",
actual_length,
file_info - DiskInfo->Files,
(INT64)read_offset << VDK_BYTE_SHIFT_TO_SECTOR));
status = VdkReadFileAt(
file_info->FileHandle,
(INT64)read_offset << VDK_BYTE_SHIFT_TO_SECTOR,
Buffer,
actual_length << VDK_BYTE_SHIFT_TO_SECTOR,
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
next_block:
//
// Prepare for the following data
//
Buffer += (read_length << VDK_BYTE_SHIFT_TO_SECTOR);
Offset += read_length;
Length -= read_length;
top_level_offset += read_length;
}
//
// Reading successfull
//
return VDK_OK;
}