📄 mapfile.c
字号:
// intentionally fall through
case RESTORE_FLAG_UNFLUSHED:
// Clear the restore flag
FlushStruct.dwRestoreFlags = RESTORE_FLAG_NONE;
if (!WriteFileWithSeek(lpm->hFile, &FlushStruct.dwRestoreFlags,
sizeof(DWORD), &bread, 0,
offsetof(fslog_t,dwRestoreFlags), 0)
|| (bread != sizeof(DWORD))) {
ERRORMSG(1,(L"Failed to clear flags on ValidateFile!\r\n"));
return FALSE;
}
FlushFileBuffers(lpm->hFile);
}
return TRUE;
}
#define FMBL_FLUSH_SKIP_LIMIT (PAGES_PER_BLOCK*2)
BOOL FlushMapBuffersLogged(LPFSMAP lpm, DWORD dwOffset, DWORD dwLength, DWORD dwFlags)
{
DWORD remain, page, end, dw1, count;
BOOL retval = FALSE;
HANDLE h;
LPBYTE pb;
ACCESSKEY ulOldKey;
SWITCHKEY(ulOldKey,0xffffffff);
//
// Figure out where we're starting and stopping, page-aligned
//
if (!dwLength)
dwLength = lpm->length;
end = dwOffset+dwLength;
dwOffset = PAGEALIGN_DOWN(dwOffset);
end = PAGEALIGN_UP(end);
if (end > lpm->length)
end = lpm->length;
if (end < dwOffset) {
if (dwFlags & FMB_LEAVENAMECS)
LeaveCriticalSection(&MapNameCS);
return FALSE;
}
if (lpm->pDirty == (LPBYTE)1) {
//
// Old-style (non-transactionable) file system
//
ERRORMSG(1,(L"FlushMapBuffersLogged being used on old-style " \
L"(non-transactionable) file system!\r\n"));
h = lpm->hFile;
pb = lpm->pBase;
if (dwFlags & FMB_LEAVENAMECS)
LeaveCriticalSection(&MapNameCS);
SetFilePointer(h, dwOffset, 0, FILE_BEGIN);
if (!WriteFile(h, pb, end-dwOffset, &remain, 0) || (remain != end-dwOffset)) {
DEBUGMSG(ZONE_PAGING,(L"FMB 5\r\n"));
DEBUGCHK(0);
goto exit;
}
FlushFileBuffers(h);
retval = TRUE;
} else if (lpm->pDirty) {
//
// The file is read/write -- we may have dirty pages to flush
//
EnterCriticalSection(&WriterCS);
if (!(dwFlags & FMB_NOWRITEOUT)) {
DWORD dwFilePos, dwMemPos;
// This struct is used to communicate with ValidateFile.
// Only dwRestoreStart (which we do not modify) is used by any
// other part of the file system.
struct {
DWORD dwRestoreFlags;
DWORD dwRestoreStart;
DWORD dwRestoreSize;
} FlushStruct;
// Track pages that are skipped
DWORD dwSkippedPage[FMBL_FLUSH_SKIP_LIMIT]; // List of page nums
WORD wSkippedPages = 0; // Count of pages
WORD wNextSkip; // Used to iterate list
count = 0;
//
// Read and update restore data
//
DEBUGMSG(ZONE_PAGING, (L"FMB 1\r\n"));
if (!ReadFileWithSeek(lpm->hFile,&FlushStruct.dwRestoreStart, sizeof(DWORD),
&dw1, 0, offsetof(fslog_t,dwRestoreStart), 0)
|| (dw1 != sizeof(DWORD))) {
// We can exit here because nothing has been changed.
RETAILMSG(1,(L"Read 0 failed on attempt at logged flush!\r\n"));
LeaveCriticalSection(&WriterCS);
if (dwFlags & FMB_LEAVENAMECS)
LeaveCriticalSection(&MapNameCS);
// debugchk because this shouldn't happen
DEBUGCHK(0);
goto exit;
}
dwFilePos = FlushStruct.dwRestoreStart;
FlushStruct.dwRestoreFlags = RESTORE_FLAG_UNFLUSHED;
FlushStruct.dwRestoreSize = PAGE_SIZE;
if (!WriteFileWithSeek(lpm->hFile, &FlushStruct, sizeof(FlushStruct),
&dw1, 0, offsetof(fslog_t,dwRestoreFlags), 0)
|| (dw1 != sizeof(FlushStruct))) {
// We can exit here because nothing has been changed.
RETAILMSG(1,(L"Write 0 failed on attempt at logged flush!\r\n"));
LeaveCriticalSection(&WriterCS);
if (dwFlags & FMB_LEAVENAMECS)
LeaveCriticalSection(&MapNameCS);
goto exit;
}
//
// Now flush the pages in memory back to the file. We write the
// new pages to the end of the file for safety, and only copy them
// into their rightful places when we are done, inside ValidateFile.
// We cannot clear the dirty flags until all pages are successfully
// flushed, so we only update their dirty flags at the end.
//
// Flush the dirty blocks in the cache back to the file
FlushFileBuffers(lpm->hFile);
EnterCriticalSection(&PagerCS);
for (page = dwOffset/PAGE_SIZE; (page+1)*PAGE_SIZE <= end; page++) {
// Flush the page only if it is dirty
if (lpm->pDirty[page/8] & (1<<(page%8))) {
// If it's the first page, make sure the restore data is correct.
if (!page)
memcpy(lpm->pBase + offsetof(fslog_t,dwRestoreFlags),
&FlushStruct, sizeof(FlushStruct));
if (VirtualProtect(lpm->pBase+page*PAGE_SIZE, PAGE_SIZE,
PAGE_READONLY, &dw1)) {
LeaveCriticalSection(&PagerCS);
// Write the page's location to the start of the page
dwMemPos = page*PAGE_SIZE;
if (!WriteFileWithSeek(lpm->hFile, &dwMemPos, sizeof(DWORD),
&dw1, 0, dwFilePos, 0)
|| (dw1 != sizeof(DWORD))) {
// We can exit here because nothing has really changed.
RETAILMSG(1, (L"Write 1 failed on attempt at logged flush!\r\n"));
goto exitUnlock;
}
// Now write the page of data
dwFilePos += sizeof(DWORD);
if (!WriteFileWithSeek(lpm->hFile, lpm->pBase+page*PAGE_SIZE, PAGE_SIZE,
&dw1, 0, dwFilePos, 0)
|| (dw1 != PAGE_SIZE)) {
// We can exit here because nothing has really changed.
RETAILMSG(1,(L"Write 2 failed on attempt at logged flush!\r\n"));
goto exitUnlock;
}
dwFilePos += PAGE_SIZE;
count++;
EnterCriticalSection(&PagerCS);
} else {
// Failed the VirtualProtect - skip the page
if (wSkippedPages < FMBL_FLUSH_SKIP_LIMIT) {
dwSkippedPage[wSkippedPages] = page;
wSkippedPages++;
} else {
// Reached our limit; this shouldn't happen
RETAILMSG(1, (TEXT("Logged flush failures exceeded limit! (file 0x%08x)\r\n"),
lpm->hFile));
DEBUGCHK(0);
}
}
}
}
// Flush the incomplete page at the end, if present
remain = end - (page*PAGE_SIZE);
if (remain && (lpm->pDirty[page/8] & (1<<(page%8)))) {
DEBUGCHK(remain <= PAGE_SIZE);
// If it's the first page, make sure the restore data is correct.
if (!page)
memcpy(lpm->pBase + offsetof(fslog_t,dwRestoreFlags),
&FlushStruct, sizeof(FlushStruct));
if (VirtualProtect(lpm->pBase+page*PAGE_SIZE, PAGE_SIZE,
PAGE_READONLY, &dw1)) {
LeaveCriticalSection(&PagerCS);
// Write the page's location to the start of the page
dwMemPos = page*PAGE_SIZE;
if (!WriteFileWithSeek(lpm->hFile, &dwMemPos, sizeof(DWORD),
&dw1, 0, dwFilePos, 0)
|| (dw1 != sizeof(DWORD))) {
// We can exit here because nothing has really changed.
RETAILMSG(1, (L"Write 3 failed on attempt at logged flush!\r\n"));
goto exitUnlock;
}
// Now write the page of data
dwFilePos += sizeof(DWORD);
if (!WriteFileWithSeek(lpm->hFile, lpm->pBase+page*PAGE_SIZE, PAGE_SIZE,
&dw1, 0, dwFilePos, 0)
|| (dw1 != PAGE_SIZE)) {
// We can exit here because nothing has really changed.
RETAILMSG(1, (L"Write 4 failed on attempt at logged flush!\r\n"));
goto exitUnlock;
}
dwFilePos += PAGE_SIZE;
count++;
} else {
// Failed the VirtualProtect - skip the page
if (wSkippedPages < FMBL_FLUSH_SKIP_LIMIT) {
dwSkippedPage[wSkippedPages] = page;
wSkippedPages++;
} else {
// Reached our limit; this shouldn't happen
RETAILMSG(1, (TEXT("Logged flush failures exceeded limit! (file 0x%08x)\r\n"),
lpm->hFile));
DEBUGCHK(0);
}
LeaveCriticalSection(&PagerCS);
}
} else
LeaveCriticalSection(&PagerCS);
// Flush the dirty blocks in the cache back to the file
FlushFileBuffers(lpm->hFile);
//
// Update restore data again
//
FlushStruct.dwRestoreFlags = RESTORE_FLAG_FLUSHED;
FlushStruct.dwRestoreSize |= count<<16;
if (!WriteFileWithSeek(lpm->hFile, &FlushStruct, sizeof(FlushStruct),
&dw1, 0, offsetof(fslog_t,dwRestoreFlags), 0)
|| (dw1 != sizeof(FlushStruct))) {
// We can exit here because nothing has really changed.
RETAILMSG(1,(L"Write 5 failed on attempt at logged flush!\r\n"));
goto exitUnlock;
}
// Flush the dirty blocks in the cache back to the file
FlushFileBuffers(lpm->hFile);
DEBUGMSG(ZONE_PAGING, (L"FMB 4\r\n"));
// Commit the flush.
if (!ValidateFile(lpm)) {
// We cannot validate the file, so we must fail the flush!
goto exitUnlock;
}
//
// Now that everything is totally done, we can mark the blocks
// as clean.
//
DEBUGMSG(wSkippedPages, (TEXT("FlushMapBuffersLogged: skipped %u pages\r\n"), wSkippedPages));
if (wSkippedPages > 0) {
wNextSkip = 0;
} else {
wNextSkip = FMBL_FLUSH_SKIP_LIMIT;
}
EnterCriticalSection(&PagerCS);
for (page = dwOffset/PAGE_SIZE; (page+1)*PAGE_SIZE <= end; page++) {
if (lpm->pDirty[page/8] & (1<<(page%8))) {
if ((wNextSkip < wSkippedPages)
&& (dwSkippedPage[wNextSkip] == page)) {
// The page was skipped -- leave it marked dirty
wNextSkip++;
} else {
// Mark as clean
lpm->bRestart = 1;
lpm->pDirty[page/8] &= ~(1<<(page%8));
lpm->dwDirty--;
DEBUGCHK(!(lpm->dwDirty & 0x80000000));
}
}
}
remain = end - (page*PAGE_SIZE);
if (remain && (lpm->pDirty[page/8] & (1<<(page%8)))) {
if ((wNextSkip < wSkippedPages)
&& (dwSkippedPage[wNextSkip] == page)) {
// The page was skipped -- leave it marked dirty
wNextSkip++;
} else {
// Mark as clean
lpm->bRestart = 1;
lpm->pDirty[page/8] &= ~(1<<(page%8));
lpm->dwDirty--;
DEBUGCHK(!(lpm->dwDirty & 0x80000000));
}
}
DEBUGCHK((wNextSkip == wSkippedPages) || (wNextSkip == FMBL_FLUSH_SKIP_LIMIT));
DEBUGCHK(!(lpm->dwDirty) || (wSkippedPages > 0));
LeaveCriticalSection(&PagerCS);
}
if (dwFlags & FMB_DOFULLDISCARD)
DecommitROPages(lpm->pBase, lpm->reslen);
LeaveCriticalSection(&WriterCS);
if (dwFlags & FMB_LEAVENAMECS)
LeaveCriticalSection(&MapNameCS);
} else {
//
// The file is read-only (no dirty pages to flush)
//
if ((lpm->hFile != INVALID_HANDLE_VALUE) && (dwFlags & FMB_DOFULLDISCARD))
FSMapMemFree(lpm->pBase, lpm->reslen, MEM_DECOMMIT);
if (dwFlags & FMB_LEAVENAMECS)
LeaveCriticalSection(&MapNameCS);
}
// Successful return
retval = TRUE;
goto exit;
exitUnlock:
// If an error occurred after some pages were VirtualProtected, we must
// go back and unlock them.
// File must be read/write
DEBUGCHK(lpm->pDirty > (LPBYTE)1);
RETAILMSG(1, (TEXT("FlushMapBuffersLogged: Unlocking protected pages\r\n")));
EnterCriticalSection(&PagerCS);
// Here we assume that all dirty pages should be read/write...
for (page = dwOffset/PAGE_SIZE; (page+1)*PAGE_SIZE <= end; page++) {
if ((lpm->pDirty[page/8] & (1<<(page%8)))
&& !VirtualProtect(lpm->pBase + page*PAGE_SIZE, PAGE_SIZE,
PAGE_READWRITE, &dw1)) {
// Is it possible to fail a call to VirtualProtect?
DEBUGCHK(0);
}
}
remain = end - (page*PAGE_SIZE);
if (remain && (lpm->pDirty[page/8] & (1<<(page%8)))
&& !VirtualProtect(lpm->pBase + page*PAGE_SIZE, PAGE_SIZE,
PAGE_READWRITE, &dw1)) {
// Is it possible to fail a call to VirtualProtect?
DEBUGCHK(0);
}
LeaveCriticalSection(&PagerCS);
// Flush the dirty blocks in the cache back to the file
FlushFileBuffers(lpm->hFile);
LeaveCriticalSection(&WriterCS);
if (dwFlags & FMB
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -