Fix possible file corruption during backup or power loss or other abnormal N++ termination

Fixes #240, Fixes #2381, Fixes #2883, Fixes #4346, Fixes #4655 and probably more issues related to loss of data.
Close #4803
This commit is contained in:
Pavel Nedev 2018-08-02 17:09:38 +03:00 committed by Don HO
parent c6e1a95098
commit abc6bc144b
5 changed files with 36 additions and 104 deletions

View File

@ -1479,6 +1479,8 @@ void Notepad_plus::getMatchedFileNames(const TCHAR *dir, const vector<generic_st
bool Notepad_plus::replaceInFiles()
{
LongRunningOperation op;
const TCHAR *dir2Search = _findReplaceDlg.getDir2Search();
if (!dir2Search[0] || !::PathFileExists(dir2Search))
{
@ -6633,7 +6635,7 @@ DWORD WINAPI Notepad_plus::backupDocument(void * /*param*/)
if (!isSnapshotMode)
break;
MainFileManager->backupCurrentBuffer();
::PostMessage(Notepad_plus_Window::gNppHWND, NPPM_INTERNAL_SAVEBACKUP, 0, 0);
}
return TRUE;
}

View File

@ -623,7 +623,7 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
case NPPM_INTERNAL_SAVECURRENTSESSION:
{
NppParameters *nppParam = NppParameters::getInstance();
const NppGUI nppGui = nppParam->getNppGUI();
const NppGUI& nppGui = nppParam->getNppGUI();
if (nppGui._rememberLastSession && !nppGui._isCmdlineNosessionActivated)
{
@ -634,6 +634,16 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
return TRUE;
}
case NPPM_INTERNAL_SAVEBACKUP:
{
if (NppParameters::getInstance()->getNppGUI().isSnapshotMode())
{
MainFileManager->backupCurrentBuffer();
}
return TRUE;
}
case NPPM_INTERNAL_RELOADNATIVELANG:
{
reloadLang();

View File

@ -1464,23 +1464,35 @@ void Notepad_plus::command(int id)
break;
case IDM_EDIT_TRIMTRAILING:
{
LongRunningOperation op;
_pEditView->execute(SCI_BEGINUNDOACTION);
doTrim(lineTail);
_pEditView->execute(SCI_ENDUNDOACTION);
break;
}
case IDM_EDIT_TRIMLINEHEAD:
{
LongRunningOperation op;
_pEditView->execute(SCI_BEGINUNDOACTION);
doTrim(lineHeader);
_pEditView->execute(SCI_ENDUNDOACTION);
break;
}
case IDM_EDIT_TRIM_BOTH:
{
LongRunningOperation op;
_pEditView->execute(SCI_BEGINUNDOACTION);
doTrim(lineTail);
doTrim(lineHeader);
_pEditView->execute(SCI_ENDUNDOACTION);
break;
}
case IDM_EDIT_EOL2WS:
_pEditView->execute(SCI_BEGINUNDOACTION);
@ -1490,6 +1502,9 @@ void Notepad_plus::command(int id)
break;
case IDM_EDIT_TRIMALL:
{
LongRunningOperation op;
_pEditView->execute(SCI_BEGINUNDOACTION);
doTrim(lineTail);
doTrim(lineHeader);
@ -1497,6 +1512,7 @@ void Notepad_plus::command(int id)
_pEditView->execute(SCI_LINESJOIN);
_pEditView->execute(SCI_ENDUNDOACTION);
break;
}
case IDM_EDIT_TAB2SW:
wsTabConvert(tab2Space);

View File

@ -68,6 +68,7 @@ namespace // anonymous
return defvalue; // fallback unknown
}
} // anonymous namespace
@ -795,35 +796,6 @@ bool FileManager::backupCurrentBuffer()
{
if (buffer->isModified()) // buffer dirty and modified, write the backup file
{
// Synchronization
// This method is called from 2 differents place, so synchronization is important
HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent"));
if (not writeEvent)
{
// no thread yet, create a event with non-signaled, to block all threads
writeEvent = ::CreateEvent(NULL, TRUE, FALSE, TEXT("nppWrittingEvent"));
if (not writeEvent)
{
printStr(TEXT("CreateEvent problem in backupCurrentBuffer()!"));
return false;
}
}
else
{
if (::WaitForSingleObject(writeEvent, INFINITE) != WAIT_OBJECT_0)
{
printStr(TEXT("WaitForSingleObject problem in backupCurrentBuffer()!"));
return false;
}
// unlocled here, set to non-signaled state, to block all threads
if (not ::ResetEvent(writeEvent))
{
printStr(TEXT("ResetEvent problem in backupCurrentBuffer()!"));
return false;
}
}
UniMode mode = buffer->getUnicodeMode();
if (mode == uniCookie)
mode = uni8Bit; //set the mode to ANSI to prevent converter from adding BOM and performing conversions, Scintilla's data can be copied directly
@ -918,13 +890,6 @@ bool FileManager::backupCurrentBuffer()
result = true; //all done
}
}
// set to signaled state
if (::SetEvent(writeEvent) == NULL)
{
printStr(TEXT("oups!"));
}
// printStr(TEXT("Event released!"));
::CloseHandle(writeEvent);
}
else // buffer dirty but unmodified
{
@ -957,47 +922,8 @@ bool FileManager::backupCurrentBuffer()
return result;
}
class EventReset final
{
public:
explicit EventReset(HANDLE h)
{
_h = h;
}
~EventReset()
{
::SetEvent(_h);
::CloseHandle(_h);
}
private:
HANDLE _h;
};
bool FileManager::deleteBufferBackup(BufferID id)
{
HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent"));
if (!writeEvent)
{
// no thread yet, create a event with non-signaled, to block all threads
writeEvent = ::CreateEvent(NULL, TRUE, FALSE, TEXT("nppWrittingEvent"));
}
else
{
if (::WaitForSingleObject(writeEvent, INFINITE) != WAIT_OBJECT_0)
{
// problem!!!
printStr(TEXT("WaitForSingleObject problem in deleteBufferBackup()!"));
return false;
}
// unlocled here, set to non-signaled state, to block all threads
::ResetEvent(writeEvent);
}
EventReset reset(writeEvent); // Will reset event in destructor.
Buffer* buffer = getBufferByID(id);
bool result = true;
generic_string backupFilePath = buffer->getBackupFileName();
@ -1008,33 +934,13 @@ bool FileManager::deleteBufferBackup(BufferID id)
result = (::DeleteFile(backupFilePath.c_str()) != 0);
}
// set to signaled state via destructor EventReset.
return result;
}
bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, generic_string * error_msg)
{
HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent"));
if (!writeEvent)
{
// no thread yet, create a event with non-signaled, to block all threads
writeEvent = ::CreateEvent(NULL, TRUE, FALSE, TEXT("nppWrittingEvent"));
}
else
{ //printStr(TEXT("Locked. I wait."));
if (::WaitForSingleObject(writeEvent, INFINITE) != WAIT_OBJECT_0)
{
// problem!!!
printStr(TEXT("WaitForSingleObject problem in saveBuffer()!"));
return false;
}
LongRunningOperation op;
// unlocled here, set to non-signaled state, to block all threads
::ResetEvent(writeEvent);
}
EventReset reset(writeEvent); // Will reset event in destructor.
Buffer* buffer = getBufferByID(id);
bool isHiddenOrSys = false;
DWORD attrib = 0;
@ -1100,7 +1006,7 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g
if (lengthDoc == 0)
items_written = 1;
}
// check the language du fichier
LangType language = detectLanguageFromTextBegining((unsigned char *)buf, lengthDoc);
@ -1113,7 +1019,6 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g
if(error_msg != NULL)
*error_msg = TEXT("Failed to save file.\nNot enough space on disk to save file?");
// set to signaled state via destructor EventReset.
return false;
}
@ -1135,7 +1040,6 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g
}
*/
// set to signaled state via destructor EventReset.
return true; //all done
}
@ -1154,10 +1058,9 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g
::DeleteFile(backupFilePath.c_str());
}
// set to signaled state via destructor EventReset.
return true;
}
// set to signaled state via destructor EventReset.
return false;
}
@ -1499,7 +1402,7 @@ bool FileManager::loadFileData(Document doc, const TCHAR * filename, char* data,
const NewDocDefaultSettings & ndds = (pNppParamInst->getNppGUI()).getNewDocDefaultSettings(); // for ndds._format
eolFormat = ndds._format;
//for empty files, if the default for new files is UTF8, and "Apply to opened ANSI files" is set, apply it
//for empty files, if the default for new files is UTF8, and "Apply to opened ANSI files" is set, apply it
if (fileSize == 0)
{
if (ndds._unicodeMode == uniCookie && ndds._openAnsiAsUtf8)

View File

@ -431,6 +431,7 @@
#define NPPM_INTERNAL_SETWORDCHARS (NOTEPADPLUS_USER_INTERNAL + 45)
#define NPPM_INTERNAL_EXPORTFUNCLISTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 46)
#define NPPM_INTERNAL_PRNTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 47)
#define NPPM_INTERNAL_SAVEBACKUP (NOTEPADPLUS_USER_INTERNAL + 48)
//wParam: 0
//lParam: document new index