/* $Id: mmap.cpp,v 1.72 2004-04-06 14:20:20 sandervl Exp $ */ /* * Win32 Memory mapped file & view classes * * Copyright 1999-2003 Sander van Leeuwen (sandervl@xs4all.nl) * * NOTE: Memory mapping DOES NOT work when kernel-mode code causes * a pagefault in the memory mapped object. (exceptions aren't * dispatched to our exception handler until after the kernel mode * call returns (too late)) * * NOTE: Are apps allowed to change the protection flags of memory mapped pages? * I'm assuming they aren't for now. * * TODO: Handles returned should be usable by all apis that accept file handles * TODO: Sharing memory mapped files between multiple processes * TODO: Memory mapped files with views that extend the file (not 100% correct now) * TODO: Suspend all threads when a page is committed (possible that another thread * accesses the same memory before the page is read from disk * TODO: File maps for new files (must select an initial size)! * * Project Odin Software License can be found in LICENSE.TXT * */ #include #include #include #include #include #include #include #include "mmap.h" #include "oslibdos.h" #include "oslibmem.h" #include "winimagepeldr.h" #include #include "asmutil.h" #define DBG_LOCALLOG DBG_mmap #include "dbglocal.h" static LPCSTR pszMMapSemName = MEMMAP_CRITSECTION_NAME; //****************************************************************************** //****************************************************************************** void WIN32API SetCustomMMapSemName(LPSTR pszSemName) { pszMMapSemName = pszSemName; } //****************************************************************************** //****************************************************************************** void InitializeMemMaps() { if(globalmapcritsect.hevLock == 0) { dprintf(("InitializeMemMaps -> create shared critical section")); } else { dprintf(("InitializeMemMaps -> access shared critical section")); } DosAccessCriticalSection(&globalmapcritsect, pszMMapSemName); } //****************************************************************************** //****************************************************************************** void FinalizeMemMaps() { DosDeleteCriticalSection(&globalmapcritsect); } //****************************************************************************** //****************************************************************************** Win32MemMap::Win32MemMap(HANDLE hfile, ULONG size, ULONG fdwProtect, LPSTR lpszName) : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0), image(0), pWriteBitmap(NULL), lpszFileName(NULL) { DosEnterCriticalSection(&globalmapcritsect); next = memmaps; memmaps = this; DosLeaveCriticalSection(&globalmapcritsect); hMemFile = hOrgMemFile = hfile; mSize = size; mProtFlags = fdwProtect; mProcessId = GetCurrentProcessId(); if(lpszName) { lpszMapName = (char *)_smalloc(strlen(lpszName)+1); strcpy(lpszMapName, lpszName); } else lpszMapName = NULL; AddRef(); } //****************************************************************************** //Map constructor used for executable image maps (only used internally) //****************************************************************************** Win32MemMap::Win32MemMap(Win32PeLdrImage *pImage, ULONG baseAddress, ULONG size) : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0), image(0), pWriteBitmap(NULL), lpszFileName(NULL) { DosEnterCriticalSection(&globalmapcritsect); next = memmaps; memmaps = this; DosLeaveCriticalSection(&globalmapcritsect); hMemFile = hOrgMemFile = -1; mSize = size; mProtFlags = PAGE_READWRITE; mProcessId = GetCurrentProcessId(); pMapping = (LPVOID)baseAddress; image = pImage; lpszMapName= NULL; AddRef(); } //****************************************************************************** //****************************************************************************** BOOL Win32MemMap::Init(DWORD aMSize) { mapMutex.enter(); if(hMemFile != -1) { lpszFileName = (char *)_smalloc(MMAP_MAX_FILENAME_LENGTH); if(HMGetFileNameFromHandle(hMemFile, lpszFileName, MMAP_MAX_FILENAME_LENGTH) == FALSE) { return FALSE; } #if 0 if(DuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(), &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) #else if(HMDuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(), &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) #endif { dprintf(("Win32MemMap::Init: DuplicateHandle failed!")); goto fail; } mSize = SetFilePointer(hMemFile, 0, NULL, FILE_BEGIN); mSize = SetFilePointer(hMemFile, 0, NULL, FILE_END); if(mSize == -1) { dprintf(("Win32MemMap::init: SetFilePointer failed to set pos end")); goto fail; } if (mSize < aMSize) { dprintf(("Win32MemMap::init: file size %d, memory map size %d", mSize, aMSize)); //Froloff: Need to check if exist the possibility of file to memory // mapping not from the beginning of file mSize = SetFilePointer(hMemFile, aMSize, NULL, FILE_BEGIN); // Commit filesize changes onto disk SetEndOfFile(hMemFile); } #if 0 //SvL: Temporary limitation of size (Warp Server Advanced doesn't allow // one to reserve more than 450 MB (unless you override the virtual // memory max limit) of continuous memory; (Warp 4 much less)) if(mSize > 64*1024*1024) { mSize = 64*1024*1024; } #endif } // Allocate the memory for the map right now if(allocateMap() == FALSE) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto fail; } dprintf(("CreateFileMappingA for file %x, prot %x size %d, name %s", hMemFile, mProtFlags, mSize, lpszMapName)); mapMutex.leave(); return TRUE; fail: mapMutex.leave(); return FALSE; } //****************************************************************************** //****************************************************************************** Win32MemMap::~Win32MemMap() { Win32MemMapView::deleteViews(this); //delete all views of our memory mapped file dprintf(("Win32MemMap dtor: deleting map %x %x", pMapping, mSize)); mapMutex.enter(); if(lpszMapName) { free(lpszMapName); } if(lpszFileName) { free(lpszFileName); } if(pMapping && !image) { dprintf(("Free map memory")); if(lpszMapName) { OSLibDosFreeMem(pMapping); } else VirtualFree(pMapping, 0, MEM_RELEASE); pMapping = NULL; } if(hMemFile != -1) { dprintf(("Win32MemMap dtor: closing memory file %x", hMemFile)); CloseHandle(hMemFile); hMemFile = -1; } if(pWriteBitmap) free(pWriteBitmap); mapMutex.leave(); DosEnterCriticalSection(&globalmapcritsect); Win32MemMap *map = memmaps; if(map == this) { memmaps = next; } else { while(map->next) { if(map->next == this) break; map = map->next; } if(map->next) { map->next = next; } else dprintf(("Win32MemMap::~Win32MemMap: map not found!! (%x)", this)); } DosLeaveCriticalSection(&globalmapcritsect); } //****************************************************************************** //****************************************************************************** int Win32MemMap::Release() { dprintf(("Win32MemMap::Release %s (%d)", lpszMapName, referenced-1)); --referenced; if(referenced == 0) { delete this; return 0; } return referenced; } //****************************************************************************** // Win32MemMap::commitRange // // Commit a range of pages // // Parameters: // // ULONG ulFaultAddr - exception address // ULONG ulOffset - offset in memory map // BOOL fWriteAccess - TRUE -> write exception // FALSE -> read exception // int nrpages - number of pages // // Returns: // TRUE - success // FALSE - failure // //****************************************************************************** BOOL Win32MemMap::commitRange(ULONG ulFaultAddr, ULONG offset, BOOL fWriteAccess, int nrpages) { LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset); DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF; dprintf(("Win32MemMap::commitRange %x (faultaddr %x)", pageAddr, lpPageFaultAddr)); if(fWriteAccess) {//writes are handled on a per-page basis for(int i=0;i write exception // FALSE -> read exception // int nrpages - number of pages // // Returns: // TRUE - success // FALSE - failure // // NOTE: // We handle only one pages for write access! // // REMARKS: // We determine whether a page has been modified by checking it's protection flags // If the write flag is set, this means commitPage had to enable this due to a pagefault // (all pages are readonly until the app tries to write to it) //****************************************************************************** BOOL Win32MemMap::commitPage(ULONG ulFaultAddr, ULONG offset, BOOL fWriteAccess, int nrpages) { MEMORY_BASIC_INFORMATION memInfo; LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset); DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF; DWORD oldProt, newProt, nrBytesRead; int i; // mapMutex.enter(); if(image) { return image->commitPage(pageAddr, fWriteAccess); } if(fWriteAccess && (mProtFlags & PAGE_WRITECOPY)) {//this is a COW map, call commitGuardPage to handle write faults return commitGuardPage(ulFaultAddr, offset, fWriteAccess); } dprintf(("Win32MemMap::commitPage %x (faultaddr %x)", pageAddr, lpPageFaultAddr)); //align at page boundary offset &= ~0xFFF; //If it's a write access violation and the view is readonly, then fail if(fWriteAccess) { Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr); if(view) { if(!(view->getAccessFlags() & MEMMAP_ACCESS_WRITE)) { dprintf(("Write access for a readonly view!!")); return FALSE; } } else { DebugInt3(); //can't happen return FALSE; } } int faultsize = nrpages*PAGE_SIZE; if(fWriteAccess) {//write access needs special care, so do that on a per page basis dprintf(("Write access -> handle only one page")); faultsize = PAGE_SIZE; } offset = pageAddr - (ULONG)pMapping; if(offset + faultsize > mSize) { faultsize = mSize - offset; } while(faultsize) { if(VirtualQuery((LPSTR)pageAddr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) { dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE)); goto fail; } memInfo.RegionSize = min(memInfo.RegionSize, faultsize); //Only changes the state of the pages with the same attribute flags //(returned in memInfo.RegionSize) //If it's smaller than the mNrPages, it simply means one or more of the //other pages have already been committed if(!(memInfo.State & MEM_COMMIT)) { if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, PAGE_READWRITE) == FALSE) { goto fail; } //Part of the memory map has been committed, so now we can change //the protection flags of the aliases (DosSetMem fails for reserved pages) updateViewPages(offset, memInfo.RegionSize, (fWriteAccess) ? PAGEVIEW_VIEW : PAGEVIEW_READONLY); if(hMemFile != -1) {//now read the page(s) from disk DWORD size; offset = pageAddr - (ULONG)pMapping; size = memInfo.RegionSize; if(offset + size > mSize) { dprintf(("Adjusting size from %d to %d", size, mSize - offset)); size = mSize - offset; } if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) { dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset)); goto fail; } if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) { dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr)); goto fail; } if(nrBytesRead != size) { dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr)); goto fail; } } //We set the protection flags to PAGE_READONLY, unless this pagefault //was due to a write access //This way we can track dirty pages which need to be flushed to //disk when FlushViewOfFile is called or the map is closed. if(!fWriteAccess) { if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READONLY, &oldProt) == FALSE) { dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError())); goto fail; } } else {//make these pages as dirty ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT; ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT; if(memInfo.RegionSize & 0xFFF) nrPages++; dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr)); markDirtyPages(startPage, nrPages); //Write access means that the next time the corresponding COW page //is touched, we need to reload it. So set the GUARD flags. updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_GUARD); } } else if(fWriteAccess) { //mark these pages as dirty ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT; ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT; if(memInfo.RegionSize & 0xFFF) nrPages++; dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr)); markDirtyPages(startPage, nrPages); //and turn on a write access if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READWRITE, &oldProt) == FALSE) { dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError())); goto fail; } //Now change all the aliased pages according to their view protection flags updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_VIEW); //Write access means that the next time the corresponding COW page //is touched, we need to reload it. So set the GUARD flags. updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_GUARD); } faultsize -= memInfo.RegionSize; pageAddr += memInfo.RegionSize; } // mapMutex.leave(); return TRUE; fail: // mapMutex.leave(); return FALSE; } //****************************************************************************** // Win32MemMap::commitGuardPage // // Handle a guard page exception for a copy-on-write view (one page only) // // Parameters: // // ULONG ulFaultAddr - exception address // ULONG ulOffset - offset in memory map // BOOL fWriteAccess - TRUE -> write exception // FALSE -> read exception // // Returns: // TRUE - success // FALSE - failure // //****************************************************************************** BOOL Win32MemMap::commitGuardPage(ULONG ulFaultAddr, ULONG ulOffset, BOOL fWriteAccess) { MEMORY_BASIC_INFORMATION memInfo; BOOL ret; DWORD pageAddr = ulFaultAddr & ~0xFFF; DWORD dwNewProt, dwOldProt; dprintf(("Win32MemMap::commitGuardPage %x (faultaddr %x)", pageAddr, ulFaultAddr)); //align at page boundary ulOffset &= ~0xFFF; //commit a single page in the original mapping (pretend read access) ret = commitPage(ulFaultAddr, ulOffset, FALSE, 1); if(ret == FALSE) { DebugInt3(); goto fail; } //clear PAGE_GUARD flag, since this page must now be made accessible dwNewProt = mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY); if(dwNewProt & PAGE_WRITECOPY) { dwNewProt |= PAGE_GUARD; } dwNewProt &= ~PAGE_GUARD; if(VirtualProtect((LPVOID)pageAddr, PAGE_SIZE, dwNewProt, &dwOldProt) == FALSE) { dprintf(("Win32MemMap::commitGuardPage: VirtualProtect %x 0x1000 %x failed!!", pageAddr, dwNewProt)); goto fail; } //copy the data from the original mapping to the COW page memcpy((LPVOID)pageAddr, (char *)pMapping+ulOffset, PAGE_SIZE); if(fWriteAccess) {//copy on write; mark pages as private DWORD startpage = (ulOffset) >> PAGE_SHIFT; dprintf(("Win32MemMap::commitGuardPage: write access -> mark page as private")); Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr); if(view) { view->markCOWPages(startpage, 1); } else DebugInt3(); //oh, oh } else {//read access; must set the map + all views to READONLY to track write access dprintf(("Win32MemMap::commitGuardPage: read access -> set page as READONLY in map + all views")); //first the memory map page if(VirtualProtect((LPVOID)pageAddr, PAGE_SIZE, PAGE_READONLY, &dwOldProt) == FALSE) { dprintf(("Win32MemMap::commitGuardPage: VirtualProtect %x 0x1000 %x failed!!", pageAddr, dwNewProt)); goto fail; } //now for any views that exist updateViewPages(ulOffset, PAGE_SIZE, PAGEVIEW_READONLY); } return TRUE; fail: return FALSE; } //****************************************************************************** // Win32MemMap::updateViewPages // // Update the page flags of all views // // Parameters: // // ULONG offset - offset in memory map // ULONG size - range size // PAGEVIEW flags - page flags // PAGEVIEW_READONLY -> set page flags to readonly // PAGEVIEW_VIEW -> set page flags to view default // PAGEVIEW_GUARD -> set page flags of COW view to GUARD // // Returns: // TRUE - success // FALSE - failure // //****************************************************************************** BOOL Win32MemMap::updateViewPages(ULONG offset, ULONG size, PAGEVIEW flags) { Win32MemMapView **views = (Win32MemMapView **)alloca(sizeof(Win32MemMapView*)*nrMappings); int localmaps; if(views) { localmaps = Win32MemMapView::findViews(this, nrMappings, views); if(localmaps <= nrMappings) { for(int i=0;ichangePageFlags(offset, size, flags); } } else { dprintf(("unexpected number of views %d vs %d", localmaps, nrMappings)); DebugInt3(); //oh, oh } } return TRUE; } //****************************************************************************** // Win32MemMap::invalidatePages // // Invalidate map pages. (called by WriteFile) // // Parameters: // // ULONG offset - offset in memory map // ULONG size - invalid range size // // Returns: // TRUE - success // FALSE - failure // //****************************************************************************** BOOL Win32MemMap::invalidatePages(ULONG offset, ULONG size) { ULONG diff = offset & 0xFFF; BOOL ret; offset &= ~0xFFF; size += diff; dprintf(("Win32MemMap::invalidatePages %x %x", offset, size)); ret = VirtualFree((LPSTR)pMapping + offset, size, MEM_DECOMMIT); if(ret == FALSE) { dprintf(("ERROR: Win32MemMap::invalidatePages: VirtualFree failed!!")); } //invalidate all shared COW pages too by setting the GUARD flag //(which forces a resync the next time the app touches them) return updateViewPages(offset, size, PAGEVIEW_GUARD); } //****************************************************************************** // Win32MemMap::allocateMap // // Allocate memory for the map if not yet already done. // // Returns: // FALSE - success // TRUE - failure // //****************************************************************************** BOOL Win32MemMap::allocateMap() { ULONG fAlloc = 0; fAlloc = MEM_RESERVE; //Memory has already been allocated for executable image maps (only used internally) if(!pMapping && nrMappings == 0) {//if not mapped, reserve/commit entire view //SvL: Always read/write access or else ReadFile will crash once we // start committing pages. // This is most likely an OS/2 bug and doesn't happen in Aurora // when allocating memory with the PAG_ANY bit set. (without this // flag it will also crash) //NOTE: If this is ever changed, then we must update setProtFlags!!!! //All named file mappings are shared (files & memory only) if(lpszMapName) { pMapping = VirtualAllocShared(mSize, fAlloc, PAGE_READWRITE, lpszMapName); } else { pMapping = VirtualAlloc(0, mSize, fAlloc, PAGE_READWRITE); } if(pMapping == NULL) { dprintf(("Win32MemMap::mapViewOfFile: VirtualAlloc %x %x failed!", mSize, fAlloc)); goto fail; } //Windows NT seems to commit memory for memory maps, regardsless of the SEC_COMMIT flag if((hMemFile == -1 && !image)) {//commit memory VirtualAlloc(pMapping, mSize, MEM_COMMIT, PAGE_READWRITE); } DWORD nrPages = mSize >> PAGE_SHIFT; if(mSize & 0xFFF) nrPages++; if(hMemFile != -1 && (mProtFlags & SEC_COMMIT)) { commitPage((ULONG)pMapping, 0, FALSE, nrPages); } //Allocate bitmap for all pages to keep track of write access (file maps only) //Necessary for FlushViewOfFile. if(hMemFile != -1) { int sizebitmap = nrPages/8 + 1; pWriteBitmap = (char *)_smalloc(sizebitmap); if(pWriteBitmap == NULL) { DebugInt3(); goto fail; } memset(pWriteBitmap, 0, sizebitmap); } } return TRUE; fail: return FALSE; } //****************************************************************************** // Win32MemMap::mapViewOfFile // // Map the view identified by addr // // Parameters: // // ULONG size - size of view // ULONG offset - offset in memory map // ULONG fdwAccess - access flags // FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_COPY // FILE_MAP_ALL_ACCESS // // // Returns: // <>NULL - success, view address // NULL - failure // //****************************************************************************** LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess) { DWORD processId = GetCurrentProcessId(); mapMutex.enter(); ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY)); Win32MemMapView *mapview; //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY)) // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though. if(fdwAccess & ~FILE_MAP_ALL_ACCESS) goto parmfail; if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE)) goto parmfail; // CreateFileMapping docs say that PAGE_WRITECOPY is equivalent to PAGE_READONLY and thus // is okay for FILE_MAP_READ too if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY|PAGE_WRITECOPY))) goto parmfail; if (fdwAccess != FILE_MAP_ALL_ACCESS) if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY)) goto parmfail; if(offset+size > mSize && (!(fdwAccess & FILE_MAP_WRITE) || hMemFile == -1)) goto parmfail; //SvL: TODO: Doesn't work for multiple views if(offset+size > mSize) { mSize = offset+size; } mapview = new Win32MemMapView(this, offset, (size == 0) ? (mSize - offset) : size, fdwAccess); if(mapview == NULL) { goto fail; } if(mapview->everythingOk() == FALSE) { dprintf(("Win32MemMap::mapViewOfFile: !mapview->everythingOk")); delete mapview; goto fail; } mapMutex.leave(); SetLastError(ERROR_SUCCESS); return mapview->getViewAddr(); parmfail: dprintf(("Win32MemMap::mapViewOfFile: invalid parameter (ERROR_ACCESS_DENIED)")); //NT4 SP6 returns ERROR_ACCESS_DENIED for most invalid parameters SetLastError(ERROR_ACCESS_DENIED); fail: mapMutex.leave(); return 0; } //****************************************************************************** // Win32MemMap::unmapViewOfFile // // Unmap the view identified by addr // // Parameters: // // LPVOID addr - view address; doesn't need to be the address // returned by MapViewOfFile(Ex) (as MSDN clearly says); // can be any address within the view range // // Returns: // TRUE - success // FALSE - failure // //****************************************************************************** BOOL Win32MemMap::unmapViewOfFile(LPVOID addr) { Win32MemMapView *view; dprintf(("Win32MemMap::unmapViewOfFile %x (nrmaps=%d)", addr, nrMappings)); mapMutex.enter(); if(nrMappings == 0) goto fail; view = Win32MemMapView::findView((ULONG)addr); if(view == NULL) goto fail; delete view; mapMutex.leave(); SetLastError(ERROR_SUCCESS); return TRUE; fail: mapMutex.leave(); SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } //****************************************************************************** //We determine whether a page has been modified by checking it's protection flags //If the write flag is set, this means commitPage had to enable this due to a pagefault //(all pages are readonly until the app tries to modify the contents of the page) // //TODO: Are apps allowed to change the protection flags of memory mapped pages? // I'm assuming they aren't for now. //****************************************************************************** BOOL Win32MemMap::flushView(ULONG viewaddr, ULONG offset, ULONG cbFlush) { ULONG nrBytesWritten, size, accessflags, oldProt; Win32MemMapView *view; int i; dprintf(("Win32MemMap::flushView: %x %x", (ULONG)pMapping+offset, cbFlush)); if(image) //no flushing for image maps return TRUE; if(hMemFile == -1) goto success; //TODO: Return an error here? if(offset > mSize) goto parmfail; if(viewaddr != MMAP_FLUSHVIEW_ALL) { view = Win32MemMapView::findView(viewaddr); if(nrMappings == 0 || view == NULL) { DebugInt3(); //should never happen goto parmfail; } accessflags = view->getAccessFlags(); } else { //force a flush to disk; only those pages marked dirty are flushed anyway accessflags = FILE_MAP_WRITE; } //If the view is readonly or copy on write, then the flush is ignored if(!(accessflags & MEMMAP_ACCESS_WRITE) || (accessflags & MEMMAP_ACCESS_COPYONWRITE)) { dprintf(("Readonly or Copy-On-Write memory map -> ignore flush")); //this is not a failure; NT4 SP6 returns success goto success; } if(cbFlush == 0) cbFlush = mSize; if(offset + cbFlush > mSize) { cbFlush -= (offset + cbFlush - mSize); } //Check the write page bitmap for dirty pages and write them to disk while(cbFlush) { int startPage = offset >> PAGE_SHIFT; size = PAGE_SIZE; if(isDirtyPage(startPage)) { if(size > cbFlush) { size = cbFlush; } dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size)); if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) { dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset)); goto fail; } if(WriteFile(hMemFile, (LPSTR)((ULONG)pMapping + offset), size, &nrBytesWritten, NULL) == FALSE) { dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)pMapping + offset)); goto fail; } if(nrBytesWritten != size) { dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)pMapping + offset)); goto fail; } clearDirtyPages(startPage, 1); //We've just flushed the page to disk, so we need to track future writes //again; Set page to readonly (first memory map, then alias(es)) if(VirtualProtect((LPVOID)((ULONG)pMapping + offset), size, PAGE_READONLY, &oldProt) == FALSE) { dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", (ULONG)pMapping + offset, size, GetLastError())); goto fail; } updateViewPages(offset, size, PAGEVIEW_READONLY); } if(cbFlush < size) break; cbFlush -= size; offset += size; } success: SetLastError(ERROR_SUCCESS); return TRUE; parmfail: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; fail: return FALSE; } //****************************************************************************** //****************************************************************************** Win32MemMap *Win32MemMap::findMap(LPSTR lpszName) { if(lpszName == NULL) return NULL; DosEnterCriticalSection(&globalmapcritsect); Win32MemMap *map = memmaps; if(map != NULL) { while(map) { if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName)) break; map = map->next; } } if(map) map->AddRef(); DosLeaveCriticalSection(&globalmapcritsect); if(!map) dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName)); return map; } //****************************************************************************** //****************************************************************************** Win32MemMap *Win32MemMap::findMapByFile(HANDLE hFile) { DWORD processId = GetCurrentProcessId(); char szFileName[260]; if(hFile == -1) return NULL; if(HMGetFileNameFromHandle(hFile, szFileName, sizeof(szFileName)) == FALSE) return NULL; DosEnterCriticalSection(&globalmapcritsect); Win32MemMap *map = memmaps; if(map != NULL) { while(map) { //TODO: we currently don't support sharing file maps between processes if(map->mProcessId == processId && map->lpszFileName) { if(!strcmp(map->lpszFileName, szFileName) && map->getFileHandle() != hFile) break; } map = map->next; } } if(map) map->AddRef(); DosLeaveCriticalSection(&globalmapcritsect); if(!map) dprintf2(("Win32MemMap::findMapByFile: couldn't find map with file handle %x", hFile)); return map; } //****************************************************************************** //****************************************************************************** Win32MemMap *Win32MemMap::findMap(ULONG address) { DosEnterCriticalSection(&globalmapcritsect); Win32MemMap *map = memmaps; if(map != NULL) { while(map) { if(map->pMapping && (ULONG)map->pMapping <= address && (ULONG)map->pMapping + map->mSize > address) { break; } map = map->next; } } if(map) map->AddRef(); DosLeaveCriticalSection(&globalmapcritsect); return map; } //****************************************************************************** //****************************************************************************** void Win32MemMap::deleteAll() { Win32MemMap *map, *nextmap; DWORD processId = GetCurrentProcessId(); //delete all maps created by this process DosEnterCriticalSection(&globalmapcritsect); startdeleteviews: map = memmaps; while(map) { map->AddRef(); //make sure it doesn't get deleted //delete all views created by this process for this map Win32MemMapView::deleteViews(map); nextmap = map->next; //map->Release can delete multiple objects (duplicate memory map), so make //sure our nextmap pointer remains valid by increasing the refcount if(nextmap) nextmap->AddRef(); map->Release(); if(nextmap && nextmap->Release() == 0) { //oops, nextmap was just deleted and is no longer valid //can't continue from here, so let's start again dprintf(("oops, nextmap is invalid -> start again (1)")); goto startdeleteviews; } map = nextmap; } startdelete: map = memmaps; while(map) { nextmap = map->next; if(map->getProcessId() == processId) { //delete map can delete multiple objects (duplicate memory map), so make //sure our nextmap pointer remains valid by increasing the refcount if(nextmap) nextmap->AddRef(); delete map; if(nextmap && nextmap->Release() == 0) { //oops, nextmap was just deleted and is no longer valid //can't continue from here, so let's start again dprintf(("oops, nextmap is invalid -> start again (2)")); goto startdelete; } } map = nextmap; } DosLeaveCriticalSection(&globalmapcritsect); } //****************************************************************************** // Win32MemMapView::markDirtyPages // // Mark pages as dirty (changed) in the write page bitmap // // Parameters: // // int startpage - start page // int nrpages - number of pages // // //****************************************************************************** void Win32MemMap::markDirtyPages(int startpage, int nrpages) { if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings for(int i=startpage;i