www.pudn.com > vdksrc.zip > vdkwrite.c
/*
vdkwrite.c
Virtual Disk write function
Copyright (C) 2003 Ken Kato
*/
#include "vdkbase.h"
#include "vdkutil.h"
#include "vdkaccess.h"
#include "cowdisk.h"
#include "vmdisk.h"
//
// Copy existing data block from underlying files to current file
//
static VDKSTAT VdkCopyDataBlock(
PVDK_DISK_INFO DiskInfo, // disk info
PVDK_FILE_INFO CurFile, // current file item to write to
ULONG Offset, // logical offset of the block to copy
ULONG Sectors); // number of sectors to copy
//
// Write requested sectors into file
//
VDKSTAT VdkWriteSector(
PVDK_DISK_INFO DiskInfo, // disk info
ULONG Offset, // logical offset of the sector to write
ULONG Length, // number of sectors to write
PUCHAR Buffer) // buffer containing the data to write
{
PVDK_FILE_INFO cur_file;
ULONG file_offset;
VDKSTAT status;
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] VdkWriteFile - Request Offset sec. %u, length %u\n",
Offset, Length));
if (DiskInfo == NULL || DiskInfo->Files == NULL) {
VDKTRACE(VDKWRITE,
("[VDK] null DiskInfo\n"));
return VDK_INTERNAL;
}
if (Offset + Length > DiskInfo->Capacity) {
VDKTRACE(VDKWRITE,
("[VDK] request out of range\n"));
return VDK_INTERNAL;
}
if (Buffer == NULL) {
VDKTRACE(VDKWRITE,
("[VDK] null write buffer\n"));
return VDK_INTERNAL;
}
cur_file = DiskInfo->Files;
file_offset = 0;
//
// Start reading data from file
//
while (Length) {
ULONG write_length;
ULONG write_offset;
//
// Look for a file which contains the first sector to write
//
while (Offset >= cur_file->Capacity) {
//
// target sector continues to the next file
//
file_offset += cur_file->Capacity;
Offset -= cur_file->Capacity;
cur_file++;
}
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Checking file #%u.\n",
cur_file - DiskInfo->Files));
//
// decide data length to be read at once
//
write_length = cur_file->Capacity;
if (cur_file->FileType == VDK_FILETYPE_NONE) {
VDKTRACE(VDKWRITE,
("[VDK] file is missing.\n"));
return VDK_INTERNAL;
}
else if (cur_file->FileType == VDK_FILETYPE_COWD) {
if (write_length > cur_file->prm.cowd->SecondaryGranularity) {
write_length = cur_file->prm.cowd->SecondaryGranularity;
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Using Granularity %u\n", write_length));
}
}
else if (cur_file->FileType == VDK_FILETYPE_VMDK) {
if (write_length > cur_file->prm.vmdk->SectorsPerGrain) {
write_length = cur_file->prm.vmdk->SectorsPerGrain;
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Using Granularity %u\n", write_length));
}
}
write_offset = Offset % write_length;
write_length -= write_offset;
if (write_length > Length) {
write_length = Length;
}
//
// Locate actual offset of the target sector in the file
//
if (cur_file->FileType == VDK_FILETYPE_FLAT) {
write_offset += cur_file->prm.SolidBackOffset;
}
else if (cur_file->FileType == VDK_FILETYPE_COWD) {
ULONG primary_idx, secondary_idx;
//
// check secondary map allocation
//
primary_idx = Offset / cur_file->prm.cowd->PrimaryGranularity;
if (primary_idx >= cur_file->prm.cowd->PrimaryMapSize) {
//
// offset exceeds the primary map size
// Probably COWD header is corrupt and inconsistent
//
VDKTRACE(VDKWRITE,
("[VDK] Primary Map overflow\n"));
return VDK_INTERNAL;
}
if (!cur_file->prm.cowd->PrimaryMap[primary_idx] ||
cur_file->prm.cowd->PrimaryMap[primary_idx]
>= cur_file->EndOfFile) {
//
// Need to allocate new Secondary Map
//
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Allocating SecondaryMap #%u at offset 0x%x\n",
primary_idx, cur_file->EndOfFile));
status = VdkSetFileSize(
cur_file->FileHandle,
((INT64)cur_file->EndOfFile << VDK_BYTE_SHIFT_TO_SECTOR) +
(cur_file->prm.cowd->SecondaryMapSize * sizeof(ULONG)));
if (!VDKSUCCESS(status)) {
return status;
}
//
// Update PrimaryMap
//
cur_file->prm.cowd->PrimaryMap[primary_idx] =
cur_file->EndOfFile;
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Writing PrimaryMap at offset %lu (%lu bytes.)\n",
((PCOWD_SECTOR_0)cur_file->prm.cowd->Sector0)->PrimaryMapOffset,
cur_file->prm.cowd->PrimaryMapSize * sizeof(ULONG)));
status = VdkWriteFileAt(
cur_file->FileHandle,
(INT64)((PCOWD_SECTOR_0)cur_file->prm.cowd->Sector0)->PrimaryMapOffset <<
VDK_BYTE_SHIFT_TO_SECTOR,
cur_file->prm.cowd->PrimaryMap,
cur_file->prm.cowd->PrimaryMapSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Update COWD header
//
cur_file->EndOfFile +=
(cur_file->prm.cowd->SecondaryMapSize * sizeof(ULONG))
>> VDK_BYTE_SHIFT_TO_SECTOR;
((PCOWD_SECTOR_0)cur_file->prm.cowd->Sector0)->EndOfFile = cur_file->EndOfFile;
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Updating COWD header\n"));
status = VdkWriteFileAt(
cur_file->FileHandle,
0,
cur_file->prm.cowd->Sector0,
VDK_BYTES_PER_SECTOR,
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Initialize new SecondaryMap
//
VdkZeroMem(
cur_file->prm.cowd->SecondaryMap,
cur_file->prm.cowd->SecondaryMapSize * sizeof(ULONG));
cur_file->prm.cowd->SecondaryMapIdx = primary_idx;
}
else if (cur_file->prm.cowd->SecondaryMapIdx != primary_idx) {
//
// Need to read secondary map from the file
//
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Reading Secondary Map #%u\n", primary_idx));
status = VdkReadFileAt(
cur_file->FileHandle,
(INT64)cur_file->prm.cowd->PrimaryMap[primary_idx] <<
VDK_BYTE_SHIFT_TO_SECTOR,
cur_file->prm.cowd->SecondaryMap,
cur_file->prm.cowd->SecondaryMapSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
cur_file->prm.cowd->SecondaryMapIdx = primary_idx;
}
//
// check sector allocation
//
secondary_idx =
(Offset % cur_file->prm.cowd->PrimaryGranularity) /
cur_file->prm.cowd->SecondaryGranularity;
if (secondary_idx >= cur_file->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(VDKWRITE,
("[VDK] Secondary Map overflow\n"));
return VDK_INTERNAL;
}
if (!cur_file->prm.cowd->SecondaryMap[secondary_idx] ||
cur_file->prm.cowd->SecondaryMap[secondary_idx]
>= cur_file->EndOfFile) {
//
// Need to allocate new sector block
//
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Allocating SectorBlock at offset 0x%x\n",
cur_file->EndOfFile));
status = VdkSetFileSize(
cur_file->FileHandle,
(INT64)(cur_file->EndOfFile + cur_file->prm.cowd->SecondaryGranularity)
<< VDK_BYTE_SHIFT_TO_SECTOR);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Copy existing blocks from underlying file
//
status = VdkCopyDataBlock(
DiskInfo,
cur_file,
file_offset + Offset,
cur_file->prm.cowd->SecondaryGranularity);
if (!VDKSUCCESS(status)) {
return status;
}
cur_file->prm.cowd->SecondaryMap[secondary_idx] =
cur_file->EndOfFile;
cur_file->EndOfFile +=
cur_file->prm.cowd->SecondaryGranularity;
//
// Update SecondaryMap
//
status = VdkWriteFileAt(
cur_file->FileHandle,
(INT64)cur_file->prm.cowd->PrimaryMap[primary_idx] <<
VDK_BYTE_SHIFT_TO_SECTOR,
cur_file->prm.cowd->SecondaryMap,
cur_file->prm.cowd->SecondaryMapSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Update COWD header
//
((PCOWD_SECTOR_0)cur_file->prm.cowd->Sector0)->EndOfFile = cur_file->EndOfFile;
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Updating COWD header\n"));
status = VdkWriteFileAt(
cur_file->FileHandle,
0,
cur_file->prm.cowd->Sector0,
VDK_BYTES_PER_SECTOR,
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
}
write_offset += cur_file->prm.cowd->SecondaryMap[secondary_idx];
}
else if (cur_file->FileType == VDK_FILETYPE_VMDK) {
ULONG table_idx, grain_idx;
//
// get grain table index
//
table_idx = Offset / cur_file->prm.vmdk->SectorsPerTable;
if (table_idx >= cur_file->prm.vmdk->DirectorySize ||
!cur_file->prm.vmdk->PrimaryDirectory[table_idx] ||
cur_file->prm.vmdk->PrimaryDirectory[table_idx]
>= cur_file->EndOfFile) {
//
// Grain table is not present;
// something's wrong with VMDK header
//
VDKTRACE(VDKWRITE,
("[VDK] Cannot locate Grain Table %lu\n",
table_idx));
return VDK_INTERNAL;
}
if (cur_file->prm.vmdk->GrainTableIdx != table_idx) {
//
// Need to read the target grain table from the file
//
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Reading Grain Table #%u\n", table_idx));
status = VdkReadFileAt(
cur_file->FileHandle,
(INT64)cur_file->prm.vmdk->PrimaryDirectory[table_idx] <<
VDK_BYTE_SHIFT_TO_SECTOR,
cur_file->prm.vmdk->GrainTable,
cur_file->prm.vmdk->GrainTableSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
cur_file->prm.vmdk->GrainTableIdx = table_idx;
}
//
// check grain allocation
//
grain_idx =
(Offset % cur_file->prm.vmdk->SectorsPerTable) /
cur_file->prm.vmdk->SectorsPerGrain;
if (grain_idx >= cur_file->prm.vmdk->GrainTableSize) {
//
// offset exceeds the secondary map size
// Probably VMDK header is corrupt and inconsistent
//
VDKTRACE(VDKWRITE,
("[VDK] Grain table overflow\n"));
return VDK_INTERNAL;
}
if (!cur_file->prm.vmdk->GrainTable[grain_idx] ||
cur_file->prm.vmdk->GrainTable[grain_idx]
>= cur_file->EndOfFile) {
//
// Need to allocate new grain
//
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Allocating Grain at offset 0x%x\n",
cur_file->EndOfFile));
status = VdkSetFileSize(
cur_file->FileHandle,
(INT64)(cur_file->EndOfFile + cur_file->prm.vmdk->SectorsPerGrain)
<< VDK_BYTE_SHIFT_TO_SECTOR);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Copy data from underlying file
//
status = VdkCopyDataBlock(
DiskInfo,
cur_file,
file_offset + Offset,
cur_file->prm.vmdk->SectorsPerGrain);
if (!VDKSUCCESS(status)) {
return status;
}
cur_file->prm.vmdk->GrainTable[grain_idx] =
cur_file->EndOfFile;
cur_file->EndOfFile +=
cur_file->prm.vmdk->SectorsPerGrain;
//
// Update Primary GrainTable
//
status = VdkWriteFileAt(
cur_file->FileHandle,
(INT64)cur_file->prm.vmdk->PrimaryDirectory[table_idx] <<
VDK_BYTE_SHIFT_TO_SECTOR,
cur_file->prm.vmdk->GrainTable,
cur_file->prm.vmdk->GrainTableSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Update Backup GrainTable
//
status = VdkWriteFileAt(
cur_file->FileHandle,
(INT64)cur_file->prm.vmdk->BackupDirectory[table_idx] <<
VDK_BYTE_SHIFT_TO_SECTOR,
cur_file->prm.vmdk->GrainTable,
cur_file->prm.vmdk->GrainTableSize * sizeof(ULONG),
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
}
write_offset += cur_file->prm.vmdk->GrainTable[grain_idx];
}
//
// Adjust EndOfFile parameter
//
if (write_offset + write_length > cur_file->EndOfFile) {
cur_file->EndOfFile = write_offset + write_length;
}
//
// Now write
//
VDKTRACE(VDKWRITE | VDKINFO,
("[VDK] Writing %u sectors at file #%u, byte offset 0x%"INT64_PRINT_FORMAT"x\n",
write_length,
cur_file - DiskInfo->Files,
(INT64)write_offset << VDK_BYTE_SHIFT_TO_SECTOR));
status = VdkWriteFileAt(
cur_file->FileHandle,
(INT64)write_offset << VDK_BYTE_SHIFT_TO_SECTOR,
Buffer,
write_length << VDK_BYTE_SHIFT_TO_SECTOR,
NULL);
if (!VDKSUCCESS(status)) {
return status;
}
//
// Prepare for the following data
//
Buffer += (write_length << VDK_BYTE_SHIFT_TO_SECTOR);
Offset += write_length;
Length -= write_length;
}
//
// Writing successfull
//
return VDK_OK;
}
//
// Copy data block from underlying files to current file
//
VDKSTAT VdkCopyDataBlock(
PVDK_DISK_INFO DiskInfo, // file info structure to read from
PVDK_FILE_INFO CurFile, // current file item to write to
ULONG Offset, // logical offset of the block to copy
ULONG Sectors) // number of sectors to copy
{
PVOID copy_buf;
VDKSTAT status;
copy_buf = VdkAllocMem(Sectors << VDK_BYTE_SHIFT_TO_SECTOR);
if (!copy_buf) {
VDKTRACE(VDKWRITE,
("[VDK] Failed to allocate copy buffer\n"));
return VDK_NOMEMORY;
}
//
// Read underlying data
//
status = VdkReadSector(
DiskInfo,
Offset / Sectors * Sectors,
Sectors,
copy_buf);
if (!VDKSUCCESS(status)) {
goto cleanup_exit;
}
//
// Write back new block data
//
status = VdkWriteFileAt(
CurFile->FileHandle,
(INT64)CurFile->EndOfFile << VDK_BYTE_SHIFT_TO_SECTOR,
copy_buf,
Sectors << VDK_BYTE_SHIFT_TO_SECTOR,
NULL);
cleanup_exit:
VdkFreeMem(copy_buf);
return status;
}