DEV9: Create Sparse files

This commit is contained in:
TheLastRar 2021-05-18 21:20:42 +01:00 committed by refractionpcsx2
parent ec887d5aaf
commit 887a1685dd
2 changed files with 169 additions and 20 deletions

View File

@ -20,17 +20,29 @@
#include <fmt/format.h>
#include "HddCreate.h"
#if _WIN32
#include "common/RedtapeWindows.h"
#include "common/StringUtil.h"
#include <winioctl.h>
#include <io.h>
#elif defined(__POSIX__)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
void HddCreate::Start()
{
Init();
WriteImage(filePath, neededSize);
WriteImage(filePath, neededSize, 1024);
Cleanup();
}
void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
void HddCreate::WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes)
{
constexpr int buffsize = 4 * 1024;
u8 buff[buffsize] = {0}; //4kb
u8 buff[buffsize] = {0}; // 4kb.
if (FileSystem::FileExists(hddPath.c_str()))
{
@ -47,35 +59,154 @@ void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
return;
}
//Size file
const char zero = 0;
bool success = FileSystem::FSeek64(newImage.get(), reqSizeBytes - 1, SEEK_SET) == 0;
success = success && std::fwrite(&zero, 1, 1, newImage.get()) == 1;
success = success && FileSystem::FSeek64(newImage.get(), 0, SEEK_SET) == 0;
bool sparseSupported = false;
#ifdef _WIN32
// Handle owned by CFile.
HANDLE nativeFile = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(newImage.get())));
if (!success)
if (nativeFile == INVALID_HANDLE_VALUE)
{
Console.Error("DEV9: HddCreate: failed to get handle");
newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str());
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
// Check if we support sparse files.
DWORD dwFlags;
if (GetVolumeInformationByHandleW(nativeFile, nullptr, 0, nullptr, nullptr, &dwFlags, nullptr, 0) == FALSE)
{
Console.Error("DEV9: HddCreate: failed to check sparse");
newImage.reset();
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
if (dwFlags & FILE_SUPPORTS_SPARSE_FILES)
{
// Sparse files supported.
FILE_SET_SPARSE_BUFFER sparseSetting;
sparseSetting.SetSparse = true;
FILE_ZERO_DATA_INFORMATION sparseRange;
sparseRange.FileOffset.QuadPart = 0;
sparseRange.BeyondFinalZero.QuadPart = fileBytes;
DWORD dwTemp;
if ((DeviceIoControl(nativeFile, FSCTL_SET_SPARSE, &sparseSetting, sizeof(sparseSetting), nullptr, 0, &dwTemp, nullptr) == FALSE) ||
(DeviceIoControl(nativeFile, FSCTL_SET_ZERO_DATA, &sparseRange, sizeof(sparseRange), nullptr, 0, &dwTemp, nullptr) == FALSE))
{
Console.Error("DEV9: HddCreate: Failed to set sparse");
newImage.reset();
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
sparseSupported = true;
}
// Set filesize.
LARGE_INTEGER seekStart;
seekStart.QuadPart = 0;
LARGE_INTEGER seekEnd;
seekEnd.QuadPart = fileBytes;
if ((SetFilePointerEx(nativeFile, seekEnd, nullptr, FILE_BEGIN) == FALSE) ||
(SetEndOfFile(nativeFile) == FALSE) ||
(SetFilePointerEx(nativeFile, seekStart, nullptr, FILE_BEGIN) == FALSE))
{
Console.Error("DEV9: HddCreate: Failed to set size");
newImage.reset();
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
#elif defined(__POSIX__)
// Handle owned by CFile.
int nativeFile = fileno(newImage.get());
if (nativeFile == -1)
{
Console.Error("DEV9: HddCreate: failed to get handle");
newImage.reset();
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
// Set filesize.
if ((ftruncate(nativeFile, fileBytes) == -1) ||
(lseek(nativeFile, 0, SEEK_SET) == -1))
{
Console.Error("DEV9: HddCreate: Failed to set size");
newImage.reset();
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
// Check the blocks allocated to determine if file is spasre.
// Assume that we don't get a false positive from filesystems only supporting ValidDataLength.
struct stat fileInfo;
if (fstat(nativeFile, &fileInfo) == -1)
{
Console.Error("DEV9: HddCreate: Failed to check sparse");
// Set filesize to zero to avoid potential freeze on close.
// Ignore any error, can't do much if this fails anyway.
[[maybe_unused]] int i = ftruncate(nativeFile, 0);
newImage.reset();
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
}
if (fileInfo.st_blocks != static_cast<s64>(fileBytes / 512))
{
// Sparse files supported.
sparseSupported = true;
// File is automatically sparse.
}
#endif
lastUpdate = std::chrono::steady_clock::now();
//Round up
const s32 reqMiB = (reqSizeBytes + ((1024 * 1024) - 1)) / (1024 * 1024);
for (s32 iMiB = 0; iMiB < reqMiB; iMiB++)
// Round up.
const s32 reqMiB = (fileBytes + ((1024 * 1024) - 1)) / (1024 * 1024);
const s32 zeroMiB = (zeroSizeBytes + ((1024 * 1024) - 1)) / (1024 * 1024);
s32 iMiB = 0;
if (sparseSupported)
iMiB = reqMiB - zeroMiB;
for (; iMiB < reqMiB; iMiB++)
{
//Round down
const s32 req4Kib = std::min<s32>(1024, (reqSizeBytes / 1024) - (u64)iMiB * 1024) / 4;
// Round down.
const s32 req4Kib = std::min<s32>(1024, (fileBytes / 1024) - (u64)iMiB * 1024) / 4;
for (s32 i4kb = 0; i4kb < req4Kib; i4kb++)
{
if (std::fwrite(buff, buffsize, 1, newImage.get()) != 1)
{
std::fflush(newImage.get());
// Set filesize to zero to avoid potential freeze on close.
#ifdef _WIN32
SetFilePointerEx(nativeFile, seekStart, nullptr, FILE_BEGIN);
SetEndOfFile(nativeFile);
#elif defined(__POSIX__)
// Ignore any error, can't do much if this fails anyway.
[[maybe_unused]] int i = ftruncate(nativeFile, 0);
#endif
newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str());
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
@ -84,11 +215,20 @@ void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
if (req4Kib != 256)
{
const s32 remainingBytes = reqSizeBytes - (((u64)iMiB) * (1024 * 1024) + req4Kib * 4096);
const s32 remainingBytes = fileBytes - (((u64)iMiB) * (1024 * 1024) + req4Kib * 4096);
if (std::fwrite(buff, remainingBytes, 1, newImage.get()) != 1)
{
std::fflush(newImage.get());
// Set filesize to zero to avoid potential freeze on close.
#ifdef _WIN32
SetFilePointerEx(nativeFile, seekStart, nullptr, FILE_BEGIN);
SetEndOfFile(nativeFile);
#elif defined(__POSIX__)
// Ignore any error, can't do much if this fails anyway.
[[maybe_unused]] int i = ftruncate(nativeFile, 0);
#endif
newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str());
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;
@ -103,8 +243,17 @@ void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
}
if (canceled.load())
{
std::fflush(newImage.get());
// Set filesize to zero to avoid potential freeze on close.
#ifdef _WIN32
SetFilePointerEx(nativeFile, seekStart, nullptr, FILE_BEGIN);
SetEndOfFile(nativeFile);
#elif defined(__POSIX__)
// Ignore any error, can't do much if this fails anyway.
[[maybe_unused]] int i = ftruncate(nativeFile, 0);
#endif
newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str());
FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true);
SetError();
return;

View File

@ -49,5 +49,5 @@ protected:
void SetCanceled();
private:
void WriteImage(std::string hddPath, u64 reqSizeBytes);
void WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes);
};