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

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

Allocate memory for the map during CreateFileMapping and free it when the map is destroyed

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