ByteStream: Fix atomic updates on external storage with UWP

This commit is contained in:
Connor McLaughlin 2021-07-11 19:05:30 +10:00
parent e8c16056b6
commit 3be6270b2d
2 changed files with 52 additions and 6 deletions

View file

@ -262,9 +262,9 @@ protected:
class AtomicUpdatedFileByteStream : public FileByteStream
{
public:
AtomicUpdatedFileByteStream(FILE* pFile, const char* originalFileName, const char* temporaryFileName)
: FileByteStream(pFile), m_committed(false), m_discarded(false), m_originalFileName(originalFileName),
m_temporaryFileName(temporaryFileName)
AtomicUpdatedFileByteStream(FILE* pFile, std::string originalFileName, std::string temporaryFileName)
: FileByteStream(pFile), m_committed(false), m_discarded(false), m_originalFileName(std::move(originalFileName)),
m_temporaryFileName(std::move(temporaryFileName))
{
}
@ -272,7 +272,7 @@ public:
{
if (m_discarded)
{
#if _WIN32
#if defined(_WIN32) && !defined(_UWP)
// delete the temporary file
if (!DeleteFileW(StringUtil::UTF8StringToWideString(m_temporaryFileName).c_str()))
{
@ -280,6 +280,14 @@ public:
"AtomicUpdatedFileByteStream::~AtomicUpdatedFileByteStream(): Failed to delete temporary file '%s'",
m_temporaryFileName.c_str());
}
#elif defined(_UWP)
// delete the temporary file
if (!DeleteFileFromAppW(StringUtil::UTF8StringToWideString(m_temporaryFileName).c_str()))
{
Log_WarningPrintf(
"AtomicUpdatedFileByteStream::~AtomicUpdatedFileByteStream(): Failed to delete temporary file '%s'",
m_temporaryFileName.c_str());
}
#else
// delete the temporary file
if (remove(m_temporaryFileName.c_str()) < 0)
@ -315,7 +323,7 @@ public:
fflush(m_pFile);
#ifdef _WIN32
#if defined(_WIN32) && !defined(_UWP)
// move the atomic file name to the original file name
if (!MoveFileExW(StringUtil::UTF8StringToWideString(m_temporaryFileName).c_str(),
StringUtil::UTF8StringToWideString(m_originalFileName).c_str(), MOVEFILE_REPLACE_EXISTING))
@ -328,6 +336,17 @@ public:
{
m_committed = true;
}
#elif defined(_UWP)
if (!FileSystem::RenamePath(m_temporaryFileName.c_str(), m_originalFileName.c_str()))
{
Log_WarningPrintf("AtomicUpdatedFileByteStream::Commit(): Failed to rename temporary file '%s' to '%s'",
m_temporaryFileName.c_str(), m_originalFileName.c_str());
m_discarded = true;
}
else
{
m_committed = true;
}
#else
// move the atomic file name to the original file name
if (rename(m_temporaryFileName.c_str(), m_originalFileName.c_str()) < 0)
@ -1000,6 +1019,7 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
{
DebugAssert(openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE));
#ifndef _UWP
// generate the temporary file name
u32 fileNameLength = static_cast<u32>(std::strlen(fileName));
char* temporaryFileName = (char*)alloca(fileNameLength + 8);
@ -1008,6 +1028,22 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
// fill in random characters
_mktemp_s(temporaryFileName, fileNameLength + 8);
const std::wstring wideTemporaryFileName(StringUtil::UTF8StringToWideString(temporaryFileName));
#else
// On UWP, preserve the extension, as it affects permissions.
std::string temporaryFileName;
const char* extension = std::strrchr(fileName, '.');
if (extension)
temporaryFileName.append(fileName, extension - fileName);
else
temporaryFileName.append(fileName);
temporaryFileName.append("_XXXXXX");
_mktemp_s(temporaryFileName.data(), temporaryFileName.size() + 1);
if (extension)
temporaryFileName.append(extension);
const std::wstring wideTemporaryFileName(StringUtil::UTF8StringToWideString(temporaryFileName));
#endif
// massive hack here
DWORD desiredAccess = GENERIC_WRITE;

View file

@ -1794,7 +1794,17 @@ bool FileSystem::RenamePath(const char* OldPath, const char* NewPath)
return false;
}
#else
if (!ReplaceFileFromAppW(old_wpath.c_str(), new_wpath.c_str(), nullptr, 0, nullptr, nullptr))
// try moving if it doesn't exist, since ReplaceFile fails on non-existing destinations
if (WrapGetFileAttributes(new_wpath.c_str()) != INVALID_FILE_ATTRIBUTES)
{
if (!DeleteFileFromAppW(new_wpath.c_str()))
{
Log_ErrorPrintf("DeleteFileFromAppW('%s') failed: %08X", new_wpath.c_str(), GetLastError());
return false;
}
}
if (!MoveFileFromAppW(old_wpath.c_str(), new_wpath.c_str()))
{
Log_ErrorPrintf("MoveFileFromAppW('%s', '%s') failed: %08X", OldPath, NewPath, GetLastError());
return false;