mirror of https://github.com/PCSX2/pcsx2.git
DEV9: Create Sparse files
This commit is contained in:
parent
ec887d5aaf
commit
887a1685dd
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue