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 <fmt/format.h>
#include "HddCreate.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() void HddCreate::Start()
{ {
Init(); Init();
WriteImage(filePath, neededSize); WriteImage(filePath, neededSize, 1024);
Cleanup(); Cleanup();
} }
void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes) void HddCreate::WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes)
{ {
constexpr int buffsize = 4 * 1024; constexpr int buffsize = 4 * 1024;
u8 buff[buffsize] = {0}; //4kb u8 buff[buffsize] = {0}; // 4kb.
if (FileSystem::FileExists(hddPath.c_str())) if (FileSystem::FileExists(hddPath.c_str()))
{ {
@ -47,35 +59,154 @@ void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
return; return;
} }
//Size file bool sparseSupported = false;
const char zero = 0; #ifdef _WIN32
bool success = FileSystem::FSeek64(newImage.get(), reqSizeBytes - 1, SEEK_SET) == 0; // Handle owned by CFile.
success = success && std::fwrite(&zero, 1, 1, newImage.get()) == 1; HANDLE nativeFile = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(newImage.get())));
success = success && FileSystem::FSeek64(newImage.get(), 0, SEEK_SET) == 0;
if (!success) if (nativeFile == INVALID_HANDLE_VALUE)
{ {
Console.Error("DEV9: HddCreate: failed to get handle");
newImage.reset(); newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str()); FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true); errored.store(true);
SetError(); SetError();
return; 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(); lastUpdate = std::chrono::steady_clock::now();
//Round up // Round up.
const s32 reqMiB = (reqSizeBytes + ((1024 * 1024) - 1)) / (1024 * 1024); const s32 reqMiB = (fileBytes + ((1024 * 1024) - 1)) / (1024 * 1024);
for (s32 iMiB = 0; iMiB < reqMiB; iMiB++) const s32 zeroMiB = (zeroSizeBytes + ((1024 * 1024) - 1)) / (1024 * 1024);
s32 iMiB = 0;
if (sparseSupported)
iMiB = reqMiB - zeroMiB;
for (; iMiB < reqMiB; iMiB++)
{ {
//Round down // Round down.
const s32 req4Kib = std::min<s32>(1024, (reqSizeBytes / 1024) - (u64)iMiB * 1024) / 4; const s32 req4Kib = std::min<s32>(1024, (fileBytes / 1024) - (u64)iMiB * 1024) / 4;
for (s32 i4kb = 0; i4kb < req4Kib; i4kb++) for (s32 i4kb = 0; i4kb < req4Kib; i4kb++)
{ {
if (std::fwrite(buff, buffsize, 1, newImage.get()) != 1) 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(); newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str()); FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true); errored.store(true);
SetError(); SetError();
return; return;
@ -84,11 +215,20 @@ void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
if (req4Kib != 256) 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) 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(); newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str()); FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true); errored.store(true);
SetError(); SetError();
return; return;
@ -103,8 +243,17 @@ void HddCreate::WriteImage(std::string hddPath, u64 reqSizeBytes)
} }
if (canceled.load()) 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(); newImage.reset();
FileSystem::DeleteFilePath(filePath.c_str()); FileSystem::DeleteFilePath(hddPath.c_str());
errored.store(true); errored.store(true);
SetError(); SetError();
return; return;

View File

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