Sandboxie/SandboxieTools/ImBox/ImageFileIO.cpp

213 lines
6.3 KiB
C++

#include "framework.h"
#include "ImageFileIO.h"
#include "ImBox.h"
#include "..\Common\helpers.h"
BOOL GetSparseRanges(HANDLE hFile);
struct SImageFileIO
{
std::wstring FilePath;
ULONG64 uSize = 0;
HANDLE Handle = INVALID_HANDLE_VALUE;
};
CImageFileIO::CImageFileIO(std::wstring& FilePath, ULONG64 uSize)
{
m = new SImageFileIO;
m->FilePath = FilePath;
m->uSize = uSize;
}
CImageFileIO::~CImageFileIO()
{
if (m->Handle != INVALID_HANDLE_VALUE)
CloseHandle(m->Handle);
delete m;
}
ULONG64 CImageFileIO::GetDiskSize() const
{
return m->uSize;
}
ULONG64 CImageFileIO::GetAllocSize() const
{
LARGE_INTEGER liSparseFileCompressedSize;
liSparseFileCompressedSize.LowPart = GetCompressedFileSize(m->FilePath.c_str(), (LPDWORD)&liSparseFileCompressedSize.HighPart);
return liSparseFileCompressedSize.QuadPart;
}
bool CImageFileIO::CanBeFormated() const
{
if (m->Handle == INVALID_HANDLE_VALUE)
return false;
ULONGLONG uSize;
GetFileSizeEx(m->Handle, (LARGE_INTEGER*)&uSize);
return (uSize == 0);
}
int CImageFileIO::Init()
{
//
// !!! WARNING !!!
//
// Without disabling tile caching ImDisk will cause a deadlock in
// ntoskrnl.exe!CcCanIWrite
// Ntfs.sys!NtfsCopyWriteA
//
// This issue also affects DiscUtilsDevio.exe
//
m->Handle = CreateFile(m->FilePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL);
if (m->Handle == INVALID_HANDLE_VALUE) {
//
// Create new image, but only if a size was specified
//
if(m->uSize)
m->Handle = CreateFile(m->FilePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL);
if (m->Handle == INVALID_HANDLE_VALUE)
return ERR_FILE_NOT_OPENED;
}
else {
//
// check if the image is not empty, and take its size
//
ULONGLONG uSize;
GetFileSizeEx(m->Handle, (LARGE_INTEGER*)&uSize);
if (uSize != 0)
m->uSize = uSize;
}
//
// make file sparse if it is not yet already
//
DWORD dwVolFlags;
GetVolumeInformation(m->FilePath.substr(0, 3).c_str(), NULL, MAX_PATH, NULL, NULL, &dwVolFlags, NULL, MAX_PATH);
if (!(dwVolFlags & FILE_SUPPORTS_SPARSE_FILES)) {
DbgPrint(L"Volume %s does not support sparse files.\n", m->FilePath.substr(0, 3).c_str());
}
else {
BY_HANDLE_FILE_INFORMATION bhfi;
GetFileInformationByHandle(m->Handle, &bhfi);
if ((bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
// Use the DeviceIoControl function with the FSCTL_SET_SPARSE control
// code to mark the file as sparse. If you don't mark the file as sparse,
// the FSCTL_SET_ZERO_DATA control code will actually write zero bytes to
// the file instead of marking the region as sparse zero area.
DWORD dwTemp;
if (!DeviceIoControl(m->Handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL)) {
DbgPrint(L"Failed to make image file sparse: %s\n", m->FilePath.c_str());
}
}
//else {
// GetSparseRanges(m->Handle);
//}
}
return ERR_OK;
}
bool CImageFileIO::DiskWrite(void* buf, int size, __int64 offset)
{
SetFilePointerEx(m->Handle, *(LARGE_INTEGER*)&offset, NULL, FILE_BEGIN);
DWORD BytesWritten;
return !!WriteFile(m->Handle, buf, size, &BytesWritten, NULL);
}
bool CImageFileIO::DiskRead(void* buf, int size, __int64 offset)
{
SetFilePointerEx(m->Handle, *(LARGE_INTEGER*)&offset, NULL, FILE_BEGIN);
DWORD BytesRead;
return !!ReadFile(m->Handle, buf, size, &BytesRead, NULL);
}
void CImageFileIO::TrimProcess(DEVICE_DATA_SET_RANGE* range, int n)
{
while (n) {
// Specify the starting and the ending address (not the size) of the sparse zero block
FILE_ZERO_DATA_INFORMATION fzdi;
fzdi.FileOffset.QuadPart = range->StartingOffset;
fzdi.BeyondFinalZero.QuadPart = range->StartingOffset + range->LengthInBytes;
DWORD dwTemp;
DeviceIoControl(m->Handle, FSCTL_SET_ZERO_DATA, &fzdi, sizeof(fzdi), NULL, 0, &dwTemp, NULL);
range++;
n--;
}
}
BOOL GetSparseRanges(HANDLE hFile)
{
LARGE_INTEGER liFileSize;
GetFileSizeEx(hFile, &liFileSize);
// Range to be examined (the whole file)
FILE_ALLOCATED_RANGE_BUFFER queryRange;
queryRange.FileOffset.QuadPart = 0;
queryRange.Length = liFileSize;
// Allocated areas info
FILE_ALLOCATED_RANGE_BUFFER allocRanges[1024];
DWORD nbytes;
BOOL fFinished;
DbgPrint(L"\nAllocated ranges in the file:");
do
{
fFinished = DeviceIoControl(hFile,
FSCTL_QUERY_ALLOCATED_RANGES,
&queryRange,
sizeof(queryRange),
allocRanges,
sizeof(allocRanges),
&nbytes,
NULL);
if (!fFinished)
{
DWORD dwError = GetLastError();
// ERROR_MORE_DATA is the only error that is normal
if (dwError != ERROR_MORE_DATA)
{
DbgPrint(L"DeviceIoControl failed w/err 0x%08lx\n", dwError);
CloseHandle(hFile);
return FALSE;
}
}
// Calculate the number of records returned
DWORD dwAllocRangeCount = nbytes / sizeof(FILE_ALLOCATED_RANGE_BUFFER);
// Print each allocated range
for (DWORD i = 0; i < dwAllocRangeCount; i++)
{
DbgPrint(L"allocated range: [%I64u] [%I64u]\n",
allocRanges[i].FileOffset.QuadPart,
allocRanges[i].Length.QuadPart);
}
// Set starting address and size for the next query
if (!fFinished && dwAllocRangeCount > 0)
{
queryRange.FileOffset.QuadPart = allocRanges[dwAllocRangeCount - 1].FileOffset.QuadPart +
allocRanges[dwAllocRangeCount - 1].Length.QuadPart;
queryRange.Length.QuadPart = liFileSize.QuadPart - queryRange.FileOffset.QuadPart;
}
} while (!fFinished);
return TRUE;
}