mirror of
https://github.com/reactos/reactos.git
synced 2024-11-19 21:48:10 +00:00
83d8fbf434
This fixes back journal in ReactOS "at low costs". Indeed, because write are improperly aligned right now, journaling just fails. With that patch, Cc will take care of aligning writes and journal will be written again. Because flush operations happen at each and every write to the journal, we expect changes to land on disk quickly (not as quickly as if they were directly written). But that's a good trade off between over engineering and fixing a broken feature. CORE-15973
303 lines
8.3 KiB
C++
303 lines
8.3 KiB
C++
/*
|
|
* PROJECT: ReactOS Automatic Testing Utility
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Class implementing a journaled test list for the Crash Recovery feature
|
|
* COPYRIGHT: Copyright 2009 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
static const char szJournalHeader[] = "RAT_J-V1";
|
|
static const WCHAR szJournalFileName[] = L"rosautotest.journal";
|
|
|
|
/**
|
|
* Constructs a CJournaledTestList object for an associated CTest-derived object.
|
|
*
|
|
* @param Test
|
|
* Pointer to a CTest-derived object, for which this test list shall serve.
|
|
*/
|
|
CJournaledTestList::CJournaledTestList(CTest* Test)
|
|
: CTestList(Test)
|
|
{
|
|
WCHAR JournalFile[MAX_PATH];
|
|
|
|
m_hJournal = INVALID_HANDLE_VALUE;
|
|
|
|
/* Build the path to the journal file */
|
|
if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, JournalFile) != S_OK)
|
|
FATAL("SHGetFolderPathW failed\n");
|
|
|
|
m_JournalFile = JournalFile;
|
|
m_JournalFile += L"\\rosautotest\\";
|
|
|
|
/* Create the directory if necessary */
|
|
if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
|
|
CreateDirectoryW(m_JournalFile.c_str(), NULL);
|
|
|
|
m_JournalFile += szJournalFileName;
|
|
|
|
/* Check if the journal already exists */
|
|
if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
|
|
WriteInitialJournalFile();
|
|
else
|
|
LoadJournalFile();
|
|
}
|
|
|
|
/**
|
|
* Destructs a CJournaledTestList object.
|
|
*/
|
|
CJournaledTestList::~CJournaledTestList()
|
|
{
|
|
if(m_hJournal != INVALID_HANDLE_VALUE)
|
|
CloseHandle(m_hJournal);
|
|
}
|
|
|
|
/**
|
|
* Opens the journal file through the CreateFileW API using the m_hJournal handle.
|
|
*
|
|
* @param DesiredAccess
|
|
* dwDesiredAccess parameter passed to CreateFileW
|
|
*
|
|
* @param CreateNew
|
|
* true if the journal file shall be created, false if an existing one shall be opened
|
|
*/
|
|
void
|
|
CJournaledTestList::OpenJournal(DWORD DesiredAccess, bool CreateNew)
|
|
{
|
|
m_hJournal = CreateFileW(
|
|
m_JournalFile.c_str(),
|
|
DesiredAccess,
|
|
0,
|
|
NULL,
|
|
(CreateNew ? CREATE_ALWAYS : OPEN_EXISTING),
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
|
|
NULL
|
|
);
|
|
|
|
if(m_hJournal == INVALID_HANDLE_VALUE)
|
|
FATAL("CreateFileW failed\n");
|
|
}
|
|
|
|
/**
|
|
* Serializes a std::string and writes it into the opened journal file.
|
|
*
|
|
* @param String
|
|
* The std::string to serialize
|
|
*
|
|
* @see UnserializeFromBuffer
|
|
*/
|
|
void
|
|
CJournaledTestList::SerializeIntoJournal(const string& String)
|
|
{
|
|
DWORD BytesWritten;
|
|
WriteFile(m_hJournal, String.c_str(), String.size() + 1, &BytesWritten, NULL);
|
|
FlushFileBuffers(m_hJournal);
|
|
}
|
|
|
|
/**
|
|
* Serializes a std::wstring and writes it into the opened journal file.
|
|
*
|
|
* @param String
|
|
* The std::wstring to serialize
|
|
*
|
|
* @see UnserializeFromBuffer
|
|
*/
|
|
void
|
|
CJournaledTestList::SerializeIntoJournal(const wstring& String)
|
|
{
|
|
DWORD BytesWritten;
|
|
WriteFile(m_hJournal, String.c_str(), (String.size() + 1) * sizeof(WCHAR), &BytesWritten, NULL);
|
|
FlushFileBuffers(m_hJournal);
|
|
}
|
|
|
|
/**
|
|
* Unserializes the next std::string from the journal buffer.
|
|
* The passed buffer pointer will point at the next element afterwards.
|
|
*
|
|
* @param Buffer
|
|
* Pointer to a pointer to a char array containing the journal buffer
|
|
*
|
|
* @param Output
|
|
* The std::string to unserialize the value into.
|
|
*/
|
|
void
|
|
CJournaledTestList::UnserializeFromBuffer(char** Buffer, string& Output)
|
|
{
|
|
Output = string(*Buffer);
|
|
*Buffer += Output.size() + 1;
|
|
}
|
|
|
|
/**
|
|
* Unserializes the next std::wstring from the journal buffer.
|
|
* The passed buffer pointer will point at the next element afterwards.
|
|
*
|
|
* @param Buffer
|
|
* Pointer to a pointer to a char array containing the journal buffer
|
|
*
|
|
* @param Output
|
|
* The std::wstring to unserialize the value into.
|
|
*/
|
|
void
|
|
CJournaledTestList::UnserializeFromBuffer(char** Buffer, wstring& Output)
|
|
{
|
|
Output = wstring((PWSTR)*Buffer);
|
|
*Buffer += (Output.size() + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
/**
|
|
* Gets all tests to be run and writes an initial journal file with this information.
|
|
*/
|
|
void
|
|
CJournaledTestList::WriteInitialJournalFile()
|
|
{
|
|
char TerminatingNull = 0;
|
|
CTestInfo* TestInfo;
|
|
DWORD BytesWritten;
|
|
|
|
StringOut("Writing initial journal file...\n\n");
|
|
|
|
m_ListIterator = 0;
|
|
|
|
/* Store all command lines in the m_List vector */
|
|
while((TestInfo = m_Test->GetNextTestInfo()) != 0)
|
|
{
|
|
m_List.push_back(*TestInfo);
|
|
delete TestInfo;
|
|
}
|
|
|
|
/* Serialize the vector and the iterator into a file */
|
|
OpenJournal(GENERIC_WRITE, true);
|
|
|
|
WriteFile(m_hJournal, szJournalHeader, sizeof(szJournalHeader), &BytesWritten, NULL);
|
|
WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
|
|
|
|
for(size_t i = 0; i < m_List.size(); i++)
|
|
{
|
|
SerializeIntoJournal(m_List[i].CommandLine);
|
|
SerializeIntoJournal(m_List[i].Module);
|
|
SerializeIntoJournal(m_List[i].Test);
|
|
}
|
|
|
|
WriteFile(m_hJournal, &TerminatingNull, sizeof(TerminatingNull), &BytesWritten, NULL);
|
|
FlushFileBuffers(m_hJournal);
|
|
|
|
CloseHandle(m_hJournal);
|
|
m_hJournal = INVALID_HANDLE_VALUE;
|
|
|
|
/* m_ListIterator will be incremented before its first use */
|
|
m_ListIterator = (size_t)-1;
|
|
}
|
|
|
|
/**
|
|
* Loads the existing journal file and sets all members to the values saved in that file.
|
|
*/
|
|
void
|
|
CJournaledTestList::LoadJournalFile()
|
|
{
|
|
char* Buffer;
|
|
char* pBuffer;
|
|
char FileHeader[sizeof(szJournalHeader)];
|
|
DWORD BytesRead;
|
|
DWORD RemainingSize;
|
|
|
|
StringOut("Loading journal file...\n\n");
|
|
|
|
OpenJournal(GENERIC_READ);
|
|
RemainingSize = GetFileSize(m_hJournal, NULL);
|
|
|
|
/* Verify the header of the journal file */
|
|
ReadFile(m_hJournal, FileHeader, sizeof(szJournalHeader), &BytesRead, NULL);
|
|
RemainingSize -= BytesRead;
|
|
|
|
if(BytesRead != sizeof(szJournalHeader))
|
|
EXCEPTION("Journal file contains no header!\n");
|
|
|
|
if(strcmp(FileHeader, szJournalHeader))
|
|
EXCEPTION("Journal file has an unsupported header!\n");
|
|
|
|
/* Read the iterator */
|
|
ReadFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesRead, NULL);
|
|
RemainingSize -= BytesRead;
|
|
|
|
if(BytesRead != sizeof(m_ListIterator))
|
|
EXCEPTION("Journal file contains no m_ListIterator member!\n");
|
|
|
|
/* Read the rest of the file into a buffer */
|
|
Buffer = new char[RemainingSize];
|
|
pBuffer = Buffer;
|
|
ReadFile(m_hJournal, Buffer, RemainingSize, &BytesRead, NULL);
|
|
|
|
CloseHandle(m_hJournal);
|
|
m_hJournal = NULL;
|
|
|
|
/* Now recreate the m_List vector out of that information */
|
|
while(*pBuffer)
|
|
{
|
|
CTestInfo TestInfo;
|
|
|
|
UnserializeFromBuffer(&pBuffer, TestInfo.CommandLine);
|
|
UnserializeFromBuffer(&pBuffer, TestInfo.Module);
|
|
UnserializeFromBuffer(&pBuffer, TestInfo.Test);
|
|
|
|
m_List.push_back(TestInfo);
|
|
}
|
|
|
|
delete[] Buffer;
|
|
}
|
|
|
|
/**
|
|
* Writes the current m_ListIterator value into the journal.
|
|
*/
|
|
void
|
|
CJournaledTestList::UpdateJournal()
|
|
{
|
|
DWORD BytesWritten;
|
|
|
|
OpenJournal(GENERIC_WRITE);
|
|
|
|
/* Skip the header */
|
|
SetFilePointer(m_hJournal, sizeof(szJournalHeader), NULL, FILE_CURRENT);
|
|
|
|
WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
|
|
FlushFileBuffers(m_hJournal);
|
|
|
|
CloseHandle(m_hJournal);
|
|
m_hJournal = NULL;
|
|
}
|
|
|
|
/**
|
|
* Interface to other classes for receiving information about the next test to be run.
|
|
*
|
|
* @return
|
|
* A pointer to a CTestInfo object, which contains all available information about the next test.
|
|
* The caller needs to free that object.
|
|
*/
|
|
CTestInfo*
|
|
CJournaledTestList::GetNextTestInfo()
|
|
{
|
|
CTestInfo* TestInfo;
|
|
|
|
/* Always jump to the next test here.
|
|
- If we're at the beginning of a new test list, the iterator will be set to 0.
|
|
- If we started with a loaded one, we assume that the test m_ListIterator is currently set
|
|
to crashed, so we move to the next test. */
|
|
++m_ListIterator;
|
|
|
|
/* Check whether the iterator would already exceed the number of stored elements */
|
|
if(m_ListIterator == m_List.size())
|
|
{
|
|
/* Delete the journal and return no pointer */
|
|
DeleteFileW(m_JournalFile.c_str());
|
|
|
|
TestInfo = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Update the journal with the current iterator and return the test information */
|
|
UpdateJournal();
|
|
|
|
TestInfo = new CTestInfo(m_List[m_ListIterator]);
|
|
}
|
|
|
|
return TestInfo;
|
|
}
|