From 590ea74bee727216036583743dcc3abfac4f13f7 Mon Sep 17 00:00:00 2001 From: Don Ho Date: Thu, 14 Mar 2024 18:03:19 +0100 Subject: [PATCH] Fix NUL characters file corruption after power outrages (1st step solution) ========= Sernario: ========= When a user modifies a file in Notepad++, and the time of periodic backup (defaulted to 7 seconds) is reached, the backup of the modified file is being written. However, if a power outage occurs during this precise moment while the file is being written, file corruption may occur. ======= Remedy: ======= The goal is to maintain a non-corrupted file sample even during power outages. Here are the steps: 0. Begin 1. Write the file A as A.temp 2. Replace A by A.temp 3. End During these steps, the cutoff can happen at any moment, but the user will always have a non-corrupted file sample (either A or A.temp). ===== Note: ===== The solution is only applied to "new #" files, since these files are generally small in length and do not have a second "physical" file existing on the hard drive. ref: https://github.com/notepad-plus-plus/notepad-plus-plus/issues/6133#issuecomment-1987037043 Fix #6133, close #14860 --- PowerEditor/src/ScintillaComponent/Buffer.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/PowerEditor/src/ScintillaComponent/Buffer.cpp b/PowerEditor/src/ScintillaComponent/Buffer.cpp index dae500349..2b6d34740 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.cpp +++ b/PowerEditor/src/ScintillaComponent/Buffer.cpp @@ -1028,7 +1028,7 @@ bool FileManager::backupCurrentBuffer() hasModifForSession = true; } - TCHAR fullpath[MAX_PATH]; + TCHAR fullpath[MAX_PATH]{}; ::GetFullPathName(backupFilePath.c_str(), MAX_PATH, fullpath, NULL); if (wcschr(fullpath, '~')) { @@ -1038,8 +1038,9 @@ bool FileManager::backupCurrentBuffer() // Make sure the backup file is not read only removeReadOnlyFlagFromFileAttributes(fullpath); - - if (UnicodeConvertor.openFile(fullpath)) + std::wstring fullpathTemp = fullpath; + fullpathTemp += L".tmp"; + if (UnicodeConvertor.openFile(buffer->isUntitled() ? fullpathTemp.c_str() : fullpath)) // Use temp only for "new #" due to they don't have the original physical existance on the hard drive { size_t lengthDoc = _pNotepadPlus->_pEditView->getCurrentDocLen(); char* buf = (char*)_pNotepadPlus->_pEditView->execute(SCI_GETCHARACTERPOINTER); //to get characters directly from Scintilla buffer @@ -1074,6 +1075,14 @@ bool FileManager::backupCurrentBuffer() if (isWrittenSuccessful) // backup file has been saved { + if (buffer->isUntitled()) // "new #" file is saved successfully, then we replace its only physical existence by its temp + { + if (::PathFileExists(fullpath)) + ::ReplaceFile(fullpath, fullpathTemp.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS, 0, 0); + else + ::MoveFileEx(fullpathTemp.c_str(), fullpath, MOVEFILE_REPLACE_EXISTING); + } + buffer->setModifiedStatus(false); result = true; //all done }