source: trunk/src/kernel32/mmap.cpp@ 10377

Last change on this file since 10377 was 10377, checked in by sandervl, 22 years ago

memory map functions for dib section code

File size: 36.3 KB
Line 
1/* $Id: mmap.cpp,v 1.67 2004-01-11 11:57:53 sandervl Exp $ */
2
3/*
4 * Win32 Memory mapped file & view classes
5 *
6 * Copyright 1999-2003 Sander van Leeuwen (sandervl@xs4all.nl)
7 *
8 * NOTE: Memory mapping DOES NOT work when kernel-mode code causes
9 * a pagefault in the memory mapped object. (exceptions aren't
10 * dispatched to our exception handler until after the kernel mode
11 * call returns (too late))
12 *
13 * NOTE: Are apps allowed to change the protection flags of memory mapped pages?
14 * I'm assuming they aren't for now.
15 *
16 * TODO: Handles returned should be usable by all apis that accept file handles
17 * TODO: Sharing memory mapped files between multiple processes
18 * TODO: Memory mapped files with views that extend the file (not 100% correct now)
19 * TODO: Suspend all threads when a page is committed (possible that another thread
20 * accesses the same memory before the page is read from disk
21 * TODO: File maps for new files (must select an initial size)!
22 *
23 * Project Odin Software License can be found in LICENSE.TXT
24 *
25 */
26#include <os2win.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <win\virtual.h>
31#include <odincrt.h>
32#include <handlemanager.h>
33#include "mmap.h"
34#include "oslibdos.h"
35#include "oslibmem.h"
36#include <winimagepeldr.h>
37#include <custombuild.h>
38#include "asmutil.h"
39
40#define DBG_LOCALLOG DBG_mmap
41#include "dbglocal.h"
42
43
44
45//Global DLL Data
46#pragma data_seg(_GLOBALDATA)
47Win32MemMap *Win32MemMap::memmaps = NULL;
48CRITICAL_SECTION_OS2 globalmapcritsect = {0};
49#pragma data_seg()
50
51
52static char *pszMMapSemName = MEMMAP_CRITSECTION_NAME;
53
54//******************************************************************************
55//******************************************************************************
56void WIN32API SetCustomMMapSemName(LPSTR pszSemName)
57{
58 pszMMapSemName = pszSemName;
59}
60//******************************************************************************
61//******************************************************************************
62void InitializeMemMaps()
63{
64 if(globalmapcritsect.hmtxLock == 0) {
65 dprintf(("InitializeMemMaps -> create shared critical section"));
66 DosInitializeCriticalSection(&globalmapcritsect, pszMMapSemName);
67 }
68 else {
69 dprintf(("InitializeMemMaps -> access shared critical section"));
70 DosAccessCriticalSection(&globalmapcritsect, pszMMapSemName);
71 }
72}
73//******************************************************************************
74//******************************************************************************
75void FinalizeMemMaps()
76{
77 DosDeleteCriticalSection(&globalmapcritsect);
78}
79//******************************************************************************
80//******************************************************************************
81Win32MemMap::Win32MemMap(HANDLE hfile, ULONG size, ULONG fdwProtect, LPSTR lpszName)
82 : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0),
83 image(0), pWriteBitmap(NULL), lpszFileName(NULL)
84{
85 DosEnterCriticalSection(&globalmapcritsect);
86 next = memmaps;
87 memmaps = this;
88 DosLeaveCriticalSection(&globalmapcritsect);
89
90 hMemFile = hOrgMemFile = hfile;
91
92 mSize = size;
93 mProtFlags = fdwProtect;
94
95 mProcessId = GetCurrentProcessId();
96
97 if(lpszName) {
98 lpszMapName = (char *)_smalloc(strlen(lpszName)+1);
99 strcpy(lpszMapName, lpszName);
100 }
101 else lpszMapName = NULL;
102 AddRef();
103}
104//******************************************************************************
105//Map constructor used for executable image maps (only used internally)
106//******************************************************************************
107Win32MemMap::Win32MemMap(Win32PeLdrImage *pImage, ULONG baseAddress, ULONG size)
108 : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0),
109 image(0), pWriteBitmap(NULL), lpszFileName(NULL)
110{
111 DosEnterCriticalSection(&globalmapcritsect);
112 next = memmaps;
113 memmaps = this;
114 DosLeaveCriticalSection(&globalmapcritsect);
115
116 hMemFile = hOrgMemFile = -1;
117
118 mSize = size;
119 mProtFlags = PAGE_READWRITE;
120 mProcessId = GetCurrentProcessId();
121
122 pMapping = (LPVOID)baseAddress;
123
124 image = pImage;
125 lpszMapName= NULL;
126 AddRef();
127}
128//******************************************************************************
129//******************************************************************************
130BOOL Win32MemMap::Init(DWORD aMSize)
131{
132 mapMutex.enter();
133 if(hMemFile != -1)
134 {
135 lpszFileName = (char *)_smalloc(MMAP_MAX_FILENAME_LENGTH);
136 if(HMGetFileNameFromHandle(hMemFile, lpszFileName, MMAP_MAX_FILENAME_LENGTH) == FALSE) {
137 return FALSE;
138 }
139#if 0
140 if(DuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(),
141 &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE)
142#else
143 if(HMDuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(),
144 &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE)
145#endif
146 {
147 dprintf(("Win32MemMap::Init: DuplicateHandle failed!"));
148 goto fail;
149 }
150 mSize = SetFilePointer(hMemFile, 0, NULL, FILE_BEGIN);
151 mSize = SetFilePointer(hMemFile, 0, NULL, FILE_END);
152 if(mSize == -1) {
153 dprintf(("Win32MemMap::init: SetFilePointer failed to set pos end"));
154 goto fail;
155 }
156 if (mSize < aMSize)
157 {
158 dprintf(("Win32MemMap::init: file size %d, memory map size %d", mSize, aMSize));
159 //Froloff: Need to check if exist the possibility of file to memory
160 // mapping not from the beginning of file
161 mSize = SetFilePointer(hMemFile, aMSize, NULL, FILE_BEGIN);
162 // Commit filesize changes onto disk
163 SetEndOfFile(hMemFile);
164 }
165#if 0
166 //SvL: Temporary limitation of size (Warp Server Advanced doesn't allow
167 // one to reserve more than 450 MB (unless you override the virtual
168 // memory max limit) of continuous memory; (Warp 4 much less))
169 if(mSize > 64*1024*1024) {
170 mSize = 64*1024*1024;
171 }
172#endif
173 }
174
175 dprintf(("CreateFileMappingA for file %x, prot %x size %d, name %s", hMemFile, mProtFlags, mSize, lpszMapName));
176 mapMutex.leave();
177 return TRUE;
178fail:
179 mapMutex.leave();
180 return FALSE;
181}
182//******************************************************************************
183//******************************************************************************
184Win32MemMap::~Win32MemMap()
185{
186 Win32MemMapView::deleteViews(this); //delete all views of our memory mapped file
187
188 dprintf(("Win32MemMap dtor: deleting map %x %x", pMapping, mSize));
189
190 mapMutex.enter();
191 if(lpszMapName) {
192 free(lpszMapName);
193 }
194 if(lpszFileName) {
195 free(lpszFileName);
196 }
197 if(pMapping && !image) {
198 if(lpszMapName) {
199 OSLibDosFreeMem(pMapping);
200 }
201 else VirtualFree(pMapping, 0, MEM_RELEASE);
202
203 pMapping = NULL;
204 }
205 if(hMemFile != -1) {
206 dprintf(("Win32MemMap dtor: closing memory file %x", hMemFile));
207 CloseHandle(hMemFile);
208 hMemFile = -1;
209 }
210 if(pWriteBitmap) free(pWriteBitmap);
211
212 mapMutex.leave();
213
214 DosEnterCriticalSection(&globalmapcritsect);
215 Win32MemMap *map = memmaps;
216
217 if(map == this) {
218 memmaps = next;
219 }
220 else {
221 while(map->next) {
222 if(map->next == this)
223 break;
224 map = map->next;
225 }
226 if(map->next) {
227 map->next = next;
228 }
229 else dprintf(("Win32MemMap::~Win32MemMap: map not found!! (%x)", this));
230 }
231 DosLeaveCriticalSection(&globalmapcritsect);
232}
233//******************************************************************************
234//******************************************************************************
235int Win32MemMap::Release()
236{
237 dprintf(("Win32MemMap::Release %s (%d)", lpszMapName, referenced-1));
238 --referenced;
239 if(referenced == 0) {
240 delete this;
241 return 0;
242 }
243 return referenced;
244}
245//******************************************************************************
246// Win32MemMap::commitRange
247//
248// Commit a range of pages
249//
250// Parameters:
251//
252// ULONG ulFaultAddr - exception address
253// ULONG ulOffset - offset in memory map
254// BOOL fWriteAccess - TRUE -> write exception
255// FALSE -> read exception
256// int nrpages - number of pages
257//
258// Returns:
259// TRUE - success
260// FALSE - failure
261//
262//******************************************************************************
263BOOL Win32MemMap::commitRange(ULONG ulFaultAddr, ULONG offset, BOOL fWriteAccess, int nrpages)
264{
265 LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset);
266 DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF;
267
268 dprintf(("Win32MemMap::commitRange %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
269
270 if(fWriteAccess)
271 {//writes are handled on a per-page basis
272 for(int i=i;i<nrpages;i++)
273 {
274 if(commitPage(ulFaultAddr, offset, TRUE, 1) == FALSE) {
275 dprintf(("Win32MemMap::commit: commitPage failed!!"));
276 return FALSE;
277 }
278 ulFaultAddr += PAGE_SIZE;
279 offset += PAGE_SIZE;
280 }
281 return TRUE;
282 }
283 else return commitPage(ulFaultAddr, offset, FALSE, nrpages);
284}
285//******************************************************************************
286// Win32MemMap::commitPage
287//
288// Handle a pagefault for a memory map
289//
290// Parameters:
291//
292// ULONG ulFaultAddr - exception address
293// ULONG ulOffset - offset in memory map
294// BOOL fWriteAccess - TRUE -> write exception
295// FALSE -> read exception
296// int nrpages - number of pages
297//
298// Returns:
299// TRUE - success
300// FALSE - failure
301//
302// NOTE:
303// We handle only one pages for write access!
304//
305// REMARKS:
306// We determine whether a page has been modified by checking it's protection flags
307// If the write flag is set, this means commitPage had to enable this due to a pagefault
308// (all pages are readonly until the app tries to write to it)
309//******************************************************************************
310BOOL Win32MemMap::commitPage(ULONG ulFaultAddr, ULONG offset, BOOL fWriteAccess, int nrpages)
311{
312 MEMORY_BASIC_INFORMATION memInfo;
313 LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset);
314 DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF;
315 DWORD oldProt, newProt, nrBytesRead;
316 int i;
317
318// mapMutex.enter();
319
320 if(image) {
321 return image->commitPage(pageAddr, fWriteAccess);
322 }
323
324 if(fWriteAccess && (mProtFlags & PAGE_WRITECOPY))
325 {//this is a COW map, call commitGuardPage to handle write faults
326 return commitGuardPage(ulFaultAddr, offset, fWriteAccess);
327 }
328
329 dprintf(("Win32MemMap::commitPage %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
330
331 //align at page boundary
332 offset &= ~0xFFF;
333
334 //If it's a write access violation and the view is readonly, then fail
335 if(fWriteAccess) {
336 Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr);
337 if(view) {
338 if(!(view->getAccessFlags() & MEMMAP_ACCESS_WRITE)) {
339 dprintf(("Write access for a readonly view!!"));
340 return FALSE;
341 }
342 }
343 else {
344 DebugInt3(); //can't happen
345 return FALSE;
346 }
347 }
348
349 int faultsize = nrpages*PAGE_SIZE;
350
351 if(fWriteAccess)
352 {//write access needs special care, so do that on a per page basis
353 dprintf(("Write access -> handle only one page"));
354 faultsize = PAGE_SIZE;
355 }
356
357 offset = pageAddr - (ULONG)pMapping;
358 if(offset + faultsize > mSize) {
359 faultsize = mSize - offset;
360 }
361
362 while(faultsize)
363 {
364 if(VirtualQuery((LPSTR)pageAddr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
365 dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
366 goto fail;
367 }
368 memInfo.RegionSize = min(memInfo.RegionSize, faultsize);
369 //Only changes the state of the pages with the same attribute flags
370 //(returned in memInfo.RegionSize)
371 //If it's smaller than the mNrPages, it simply means one or more of the
372 //other pages have already been committed
373 if(!(memInfo.State & MEM_COMMIT))
374 {
375 if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, PAGE_READWRITE) == FALSE) {
376 goto fail;
377 }
378
379 //Part of the memory map has been committed, so now we can change
380 //the protection flags of the aliases (DosSetMem fails for reserved pages)
381 updateViewPages(offset, memInfo.RegionSize, (fWriteAccess) ? PAGEVIEW_VIEW : PAGEVIEW_READONLY);
382
383 if(hMemFile != -1)
384 {//now read the page(s) from disk
385 DWORD size;
386
387 offset = pageAddr - (ULONG)pMapping;
388 size = memInfo.RegionSize;
389 if(offset + size > mSize) {
390 dprintf(("Adjusting size from %d to %d", size, mSize - offset));
391 size = mSize - offset;
392 }
393 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
394 dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
395 goto fail;
396 }
397 if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
398 dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
399 goto fail;
400 }
401 if(nrBytesRead != size) {
402 dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
403 goto fail;
404 }
405 }
406 //We set the protection flags to PAGE_READONLY, unless this pagefault
407 //was due to a write access
408 //This way we can track dirty pages which need to be flushed to
409 //disk when FlushViewOfFile is called or the map is closed.
410 if(!fWriteAccess)
411 {
412 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READONLY, &oldProt) == FALSE) {
413 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
414 goto fail;
415 }
416 }
417 else
418 {//make these pages as dirty
419 ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
420 ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT;
421
422 if(memInfo.RegionSize & 0xFFF)
423 nrPages++;
424
425 dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
426 markDirtyPages(startPage, nrPages);
427
428 //Write access means that the next time the corresponding COW page
429 //is touched, we need to reload it. So set the GUARD flags.
430 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_GUARD);
431 }
432 }
433 else
434 if(fWriteAccess)
435 {
436 //mark these pages as dirty
437 ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
438 ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT;
439
440 if(memInfo.RegionSize & 0xFFF)
441 nrPages++;
442
443 dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
444 markDirtyPages(startPage, nrPages);
445
446 //and turn on a write access
447 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READWRITE, &oldProt) == FALSE) {
448 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
449 goto fail;
450 }
451 //Now change all the aliased pages according to their view protection flags
452 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_VIEW);
453
454 //Write access means that the next time the corresponding COW page
455 //is touched, we need to reload it. So set the GUARD flags.
456 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_GUARD);
457 }
458 faultsize -= memInfo.RegionSize;
459 pageAddr += memInfo.RegionSize;
460 }
461
462// mapMutex.leave();
463 return TRUE;
464fail:
465// mapMutex.leave();
466 return FALSE;
467}
468//******************************************************************************
469// Win32MemMap::commitGuardPage
470//
471// Handle a guard page exception for a copy-on-write view (one page only)
472//
473// Parameters:
474//
475// ULONG ulFaultAddr - exception address
476// ULONG ulOffset - offset in memory map
477// BOOL fWriteAccess - TRUE -> write exception
478// FALSE -> read exception
479//
480// Returns:
481// TRUE - success
482// FALSE - failure
483//
484//******************************************************************************
485BOOL Win32MemMap::commitGuardPage(ULONG ulFaultAddr, ULONG ulOffset, BOOL fWriteAccess)
486{
487 MEMORY_BASIC_INFORMATION memInfo;
488 BOOL ret;
489 DWORD pageAddr = ulFaultAddr & ~0xFFF;
490 DWORD dwNewProt, dwOldProt;
491
492 dprintf(("Win32MemMap::commitGuardPage %x (faultaddr %x)", pageAddr, ulFaultAddr));
493
494 //align at page boundary
495 ulOffset &= ~0xFFF;
496
497 //commit a single page in the original mapping (pretend read access)
498 ret = commitPage(ulFaultAddr, ulOffset, FALSE, 1);
499 if(ret == FALSE) {
500 DebugInt3();
501 goto fail;
502 }
503 //clear PAGE_GUARD flag, since this page must now be made accessible
504 dwNewProt = mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY);
505 if(dwNewProt & PAGE_WRITECOPY) {
506 dwNewProt |= PAGE_GUARD;
507 }
508 dwNewProt &= ~PAGE_GUARD;
509 if(VirtualProtect((LPVOID)pageAddr, PAGE_SIZE, dwNewProt, &dwOldProt) == FALSE) {
510 dprintf(("Win32MemMap::commitGuardPage: VirtualProtect %x 0x1000 %x failed!!", pageAddr, dwNewProt));
511 goto fail;
512 }
513 //copy the data from the original mapping to the COW page
514 memcpy((LPVOID)pageAddr, (char *)pMapping+ulOffset, PAGE_SIZE);
515
516 if(fWriteAccess)
517 {//copy on write; mark pages as private
518 DWORD startpage = (ulOffset) >> PAGE_SHIFT;
519
520 dprintf(("Win32MemMap::commitGuardPage: write access -> mark page as private"));
521
522 Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr);
523 if(view)
524 {
525 view->markCOWPages(startpage, 1);
526 }
527 else DebugInt3(); //oh, oh
528 }
529 else
530 {//read access; must set the map + all views to READONLY to track write access
531
532 dprintf(("Win32MemMap::commitGuardPage: read access -> set page as READONLY in map + all views"));
533
534 //first the memory map page
535 if(VirtualProtect((LPVOID)pageAddr, PAGE_SIZE, PAGE_READONLY, &dwOldProt) == FALSE) {
536 dprintf(("Win32MemMap::commitGuardPage: VirtualProtect %x 0x1000 %x failed!!", pageAddr, dwNewProt));
537 goto fail;
538 }
539 //now for any views that exist
540 updateViewPages(ulOffset, PAGE_SIZE, PAGEVIEW_READONLY);
541 }
542
543 return TRUE;
544fail:
545 return FALSE;
546}
547//******************************************************************************
548// Win32MemMap::updateViewPages
549//
550// Update the page flags of all views
551//
552// Parameters:
553//
554// ULONG offset - offset in memory map
555// ULONG size - range size
556// PAGEVIEW flags - page flags
557// PAGEVIEW_READONLY -> set page flags to readonly
558// PAGEVIEW_VIEW -> set page flags to view default
559// PAGEVIEW_GUARD -> set page flags of COW view to GUARD
560//
561// Returns:
562// TRUE - success
563// FALSE - failure
564//
565//******************************************************************************
566BOOL Win32MemMap::updateViewPages(ULONG offset, ULONG size, PAGEVIEW flags)
567{
568 Win32MemMapView **views = (Win32MemMapView **)alloca(sizeof(Win32MemMapView*)*nrMappings);
569 if(views)
570 {
571 if(Win32MemMapView::findViews(this, nrMappings, views) == nrMappings)
572 {
573 for(int i=0;i<nrMappings;i++)
574 {
575 views[i]->changePageFlags(offset, size, flags);
576 }
577 }
578 else DebugInt3(); //oh, oh
579 }
580 return TRUE;
581}
582//******************************************************************************
583// Win32MemMap::invalidatePages
584//
585// Invalidate map pages. (called by WriteFile)
586//
587// Parameters:
588//
589// ULONG offset - offset in memory map
590// ULONG size - invalid range size
591//
592// Returns:
593// TRUE - success
594// FALSE - failure
595//
596//******************************************************************************
597BOOL Win32MemMap::invalidatePages(ULONG offset, ULONG size)
598{
599 ULONG diff = offset & 0xFFF;
600 BOOL ret;
601
602 offset &= ~0xFFF;
603 size += diff;
604
605 dprintf(("Win32MemMap::invalidatePages %x %x", offset, size));
606 ret = VirtualFree((LPSTR)pMapping + offset, size, MEM_DECOMMIT);
607 if(ret == FALSE) {
608 dprintf(("ERROR: Win32MemMap::invalidatePages: VirtualFree failed!!"));
609 }
610 //invalidate all shared COW pages too by setting the GUARD flag
611 //(which forces a resync the next time the app touches them)
612 return updateViewPages(offset, size, PAGEVIEW_GUARD);
613}
614//******************************************************************************
615// Win32MemMap::allocateMap
616//
617// Allocate memory for the map if not yet already done.
618//
619// Returns:
620// FALSE - success
621// TRUE - failure
622//
623//******************************************************************************
624BOOL Win32MemMap::allocateMap()
625{
626 ULONG fAlloc = 0;
627
628 fAlloc = MEM_RESERVE;
629
630 //Memory has already been allocated for executable image maps (only used internally)
631 if(!pMapping && nrMappings == 0)
632 {//if not mapped, reserve/commit entire view
633 //SvL: Always read/write access or else ReadFile will crash once we
634 // start committing pages.
635 // This is most likely an OS/2 bug and doesn't happen in Aurora
636 // when allocating memory with the PAG_ANY bit set. (without this
637 // flag it will also crash)
638 //NOTE: If this is ever changed, then we must update setProtFlags!!!!
639
640 //All named file mappings are shared (files & memory only)
641 if(lpszMapName) {
642 pMapping = VirtualAllocShared(mSize, fAlloc, PAGE_READWRITE, lpszMapName);
643 }
644 else {
645 pMapping = VirtualAlloc(0, mSize, fAlloc, PAGE_READWRITE);
646 }
647 if(pMapping == NULL) {
648 dprintf(("Win32MemMap::mapViewOfFile: VirtualAlloc %x %x failed!", mSize, fAlloc));
649 goto fail;
650 }
651 //Windows NT seems to commit memory for memory maps, regardsless of the SEC_COMMIT flag
652 if((hMemFile == -1 && !image)) {//commit memory
653 VirtualAlloc(pMapping, mSize, MEM_COMMIT, PAGE_READWRITE);
654 }
655
656 DWORD nrPages = mSize >> PAGE_SHIFT;
657 if(mSize & 0xFFF)
658 nrPages++;
659
660 if(hMemFile != -1 && (mProtFlags & SEC_COMMIT)) {
661 commitPage((ULONG)pMapping, 0, FALSE, nrPages);
662 }
663 //Allocate bitmap for all pages to keep track of write access (file maps only)
664 //Necessary for FlushViewOfFile.
665 if(hMemFile != -1) {
666 int sizebitmap = nrPages/8 + 1;
667
668 pWriteBitmap = (char *)_smalloc(sizebitmap);
669 if(pWriteBitmap == NULL) {
670 DebugInt3();
671 goto fail;
672 }
673 memset(pWriteBitmap, 0, sizebitmap);
674 }
675 }
676 return TRUE;
677
678fail:
679 return FALSE;
680}
681//******************************************************************************
682// Win32MemMap::mapViewOfFile
683//
684// Map the view identified by addr
685//
686// Parameters:
687//
688// ULONG size - size of view
689// ULONG offset - offset in memory map
690// ULONG fdwAccess - access flags
691// FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_COPY
692// FILE_MAP_ALL_ACCESS
693//
694//
695// Returns:
696// <>NULL - success, view address
697// NULL - failure
698//
699//******************************************************************************
700LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
701{
702 DWORD processId = GetCurrentProcessId();
703
704 mapMutex.enter();
705 ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
706 Win32MemMapView *mapview;
707
708 //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY))
709 // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though.
710 if(fdwAccess & ~FILE_MAP_ALL_ACCESS)
711 goto parmfail;
712 if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
713 goto parmfail;
714 if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
715 goto parmfail;
716
717 if (fdwAccess != FILE_MAP_ALL_ACCESS)
718 if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
719 goto parmfail;
720
721 if(offset+size > mSize && (!(fdwAccess & FILE_MAP_WRITE) || hMemFile == -1))
722 goto parmfail;
723
724 //SvL: TODO: Doesn't work for multiple views
725 if(offset+size > mSize) {
726 mSize = offset+size;
727 }
728
729 if(allocateMap() == FALSE) {
730 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
731 goto fail;
732 }
733
734 mapview = new Win32MemMapView(this, offset, (size == 0) ? (mSize - offset) : size, fdwAccess);
735 if(mapview == NULL) {
736 goto fail;
737 }
738 if(mapview->everythingOk() == FALSE) {
739 dprintf(("Win32MemMap::mapViewOfFile: !mapview->everythingOk"));
740 delete mapview;
741 goto fail;
742 }
743 mapMutex.leave();
744 SetLastError(ERROR_SUCCESS);
745 return mapview->getViewAddr();
746
747parmfail:
748 dprintf(("Win32MemMap::mapViewOfFile: invalid parameter (ERROR_ACCESS_DENIED)"));
749 //NT4 SP6 returns ERROR_ACCESS_DENIED for most invalid parameters
750 SetLastError(ERROR_ACCESS_DENIED);
751fail:
752 mapMutex.leave();
753 return 0;
754}
755//******************************************************************************
756// Win32MemMap::unmapViewOfFile
757//
758// Unmap the view identified by addr
759//
760// Parameters:
761//
762// LPVOID addr - view address; doesn't need to be the address
763// returned by MapViewOfFile(Ex) (as MSDN clearly says);
764// can be any address within the view range
765//
766// Returns:
767// TRUE - success
768// FALSE - failure
769//
770//******************************************************************************
771BOOL Win32MemMap::unmapViewOfFile(LPVOID addr)
772{
773 Win32MemMapView *view;
774
775 dprintf(("Win32MemMap::unmapViewOfFile %x (nrmaps=%d)", addr, nrMappings));
776 mapMutex.enter();
777
778 if(nrMappings == 0)
779 goto fail;
780
781 view = Win32MemMapView::findView((ULONG)addr);
782 if(view == NULL)
783 goto fail;
784
785 delete view;
786
787 if(nrMappings == 0) {
788 VirtualFree(pMapping, 0, MEM_RELEASE);
789 pMapping = NULL;
790 }
791 mapMutex.leave();
792
793 SetLastError(ERROR_SUCCESS);
794 return TRUE;
795fail:
796 mapMutex.leave();
797 SetLastError(ERROR_INVALID_ADDRESS);
798 return FALSE;
799}
800//******************************************************************************
801//We determine whether a page has been modified by checking it's protection flags
802//If the write flag is set, this means commitPage had to enable this due to a pagefault
803//(all pages are readonly until the app tries to modify the contents of the page)
804//
805//TODO: Are apps allowed to change the protection flags of memory mapped pages?
806// I'm assuming they aren't for now.
807//******************************************************************************
808BOOL Win32MemMap::flushView(ULONG viewaddr, ULONG offset, ULONG cbFlush)
809{
810 ULONG nrBytesWritten, size, accessflags, oldProt;
811 Win32MemMapView *view;
812 int i;
813
814 dprintf(("Win32MemMap::flushView: %x %x", (ULONG)pMapping+offset, cbFlush));
815
816 if(image) //no flushing for image maps
817 return TRUE;
818
819 if(hMemFile == -1)
820 goto success; //TODO: Return an error here?
821
822 if(offset > mSize)
823 goto parmfail;
824
825 if(viewaddr != MMAP_FLUSHVIEW_ALL)
826 {
827 view = Win32MemMapView::findView(viewaddr);
828 if(nrMappings == 0 || view == NULL) {
829 DebugInt3(); //should never happen
830 goto parmfail;
831 }
832 accessflags = view->getAccessFlags();
833 }
834 else {
835 //force a flush to disk; only those pages marked dirty are flushed anyway
836 accessflags = FILE_MAP_WRITE;
837 }
838 //If the view is readonly or copy on write, then the flush is ignored
839 if(!(accessflags & MEMMAP_ACCESS_WRITE) || (accessflags & MEMMAP_ACCESS_COPYONWRITE))
840 {
841 dprintf(("Readonly or Copy-On-Write memory map -> ignore flush"));
842 //this is not a failure; NT4 SP6 returns success
843 goto success;
844 }
845
846 if(cbFlush == 0)
847 cbFlush = mSize;
848
849 if(offset + cbFlush > mSize) {
850 cbFlush -= (offset + cbFlush - mSize);
851 }
852
853 //Check the write page bitmap for dirty pages and write them to disk
854 while(cbFlush)
855 {
856 int startPage = offset >> PAGE_SHIFT;
857 size = PAGE_SIZE;
858
859 if(isDirtyPage(startPage))
860 {
861 if(size > cbFlush) {
862 size = cbFlush;
863 }
864 dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
865
866 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
867 dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
868 goto fail;
869 }
870 if(WriteFile(hMemFile, (LPSTR)((ULONG)pMapping + offset), size, &nrBytesWritten, NULL) == FALSE) {
871 dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)pMapping + offset));
872 goto fail;
873 }
874 if(nrBytesWritten != size) {
875 dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)pMapping + offset));
876 goto fail;
877 }
878 clearDirtyPages(startPage, 1);
879
880 //We've just flushed the page to disk, so we need to track future writes
881 //again; Set page to readonly (first memory map, then alias(es))
882 if(VirtualProtect((LPVOID)((ULONG)pMapping + offset), size, PAGE_READONLY, &oldProt) == FALSE) {
883 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", (ULONG)pMapping + offset, size, GetLastError()));
884 goto fail;
885 }
886 updateViewPages(offset, size, PAGEVIEW_READONLY);
887 }
888
889 if(cbFlush < size)
890 break;
891
892 cbFlush -= size;
893 offset += size;
894 }
895success:
896 SetLastError(ERROR_SUCCESS);
897 return TRUE;
898
899parmfail:
900 SetLastError(ERROR_INVALID_PARAMETER);
901 return FALSE;
902fail:
903 return FALSE;
904}
905//******************************************************************************
906//******************************************************************************
907Win32MemMap *Win32MemMap::findMap(LPSTR lpszName)
908{
909 if(lpszName == NULL)
910 return NULL;
911
912 DosEnterCriticalSection(&globalmapcritsect);
913 Win32MemMap *map = memmaps;
914
915 if(map != NULL) {
916 while(map) {
917 if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName))
918 break;
919 map = map->next;
920 }
921 }
922 if(map) map->AddRef();
923
924 DosLeaveCriticalSection(&globalmapcritsect);
925 if(!map) dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName));
926 return map;
927}
928//******************************************************************************
929//******************************************************************************
930Win32MemMap *Win32MemMap::findMapByFile(HANDLE hFile)
931{
932 DWORD processId = GetCurrentProcessId();
933 char szFileName[260];
934
935 if(hFile == -1)
936 return NULL;
937
938 if(HMGetFileNameFromHandle(hFile, szFileName, sizeof(szFileName)) == FALSE)
939 return NULL;
940
941 DosEnterCriticalSection(&globalmapcritsect);
942 Win32MemMap *map = memmaps;
943
944 if(map != NULL)
945 {
946 while(map) {
947 //TODO: we currently don't support sharing file maps between processes
948 if(map->mProcessId == processId && map->lpszFileName)
949 {
950 if(!strcmp(map->lpszFileName, szFileName))
951 break;
952 }
953 map = map->next;
954 }
955 }
956 if(map) map->AddRef();
957 DosLeaveCriticalSection(&globalmapcritsect);
958 if(!map) dprintf2(("Win32MemMap::findMapByFile: couldn't find map with file handle %x", hFile));
959 return map;
960}
961//******************************************************************************
962//******************************************************************************
963Win32MemMap *Win32MemMap::findMap(ULONG address)
964{
965 DosEnterCriticalSection(&globalmapcritsect);
966 Win32MemMap *map = memmaps;
967
968 if(map != NULL) {
969 while(map) {
970 if(map->pMapping && (ULONG)map->pMapping <= address &&
971 (ULONG)map->pMapping + map->mSize > address)
972 {
973 break;
974 }
975 map = map->next;
976 }
977 }
978 if(map) map->AddRef();
979 DosLeaveCriticalSection(&globalmapcritsect);
980 return map;
981}
982//******************************************************************************
983//******************************************************************************
984void Win32MemMap::deleteAll()
985{
986 Win32MemMap *map, *nextmap;
987 DWORD processId = GetCurrentProcessId();
988
989 //delete all maps created by this process
990 DosEnterCriticalSection(&globalmapcritsect);
991
992startdeleteviews:
993 map = memmaps;
994 while(map) {
995 map->AddRef(); //make sure it doesn't get deleted
996
997 //delete all views created by this process for this map
998 Win32MemMapView::deleteViews(map);
999
1000 nextmap = map->next;
1001
1002 //map->Release can delete multiple objects (duplicate memory map), so make
1003 //sure our nextmap pointer remains valid by increasing the refcount
1004 if(nextmap) nextmap->AddRef();
1005 map->Release();
1006
1007 if(nextmap && nextmap->Release() == 0) {
1008 //oops, nextmap was just deleted and is no longer valid
1009 //can't continue from here, so let's start again
1010 dprintf(("oops, nextmap is invalid -> start again (1)"));
1011 goto startdeleteviews;
1012 }
1013
1014 map = nextmap;
1015 }
1016startdelete:
1017 map = memmaps;
1018 while(map) {
1019 nextmap = map->next;
1020 if(map->getProcessId() == processId)
1021 {
1022 //delete map can delete multiple objects (duplicate memory map), so make
1023 //sure our nextmap pointer remains valid by increasing the refcount
1024 if(nextmap) nextmap->AddRef();
1025 delete map;
1026
1027 if(nextmap && nextmap->Release() == 0) {
1028 //oops, nextmap was just deleted and is no longer valid
1029 //can't continue from here, so let's start again
1030 dprintf(("oops, nextmap is invalid -> start again (2)"));
1031 goto startdelete;
1032 }
1033 }
1034 map = nextmap;
1035 }
1036 DosLeaveCriticalSection(&globalmapcritsect);
1037}
1038//******************************************************************************
1039// Win32MemMapView::markDirtyPages
1040//
1041// Mark pages as dirty (changed) in the write page bitmap
1042//
1043// Parameters:
1044//
1045// int startpage - start page
1046// int nrpages - number of pages
1047//
1048//
1049//******************************************************************************
1050void Win32MemMap::markDirtyPages(int startpage, int nrpages)
1051{
1052 if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
1053
1054 for(int i=startpage;i<startpage+nrpages;i++) {
1055 set_bit(i, pWriteBitmap);
1056 }
1057}
1058//******************************************************************************
1059// Win32MemMapView::clearDirtyPages
1060//
1061// Mark pages as clean in the write page bitmap
1062//
1063// Parameters:
1064//
1065// int startpage - start page
1066// int nrpages - number of pages
1067//
1068//
1069//******************************************************************************
1070void Win32MemMap::clearDirtyPages(int startpage, int nrpages)
1071{
1072 if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
1073
1074 for(int i=startpage;i<startpage+nrpages;i++) {
1075 clear_bit(i, pWriteBitmap);
1076 }
1077}
1078//******************************************************************************
1079//******************************************************************************
Note: See TracBrowser for help on using the repository browser.