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

Last change on this file since 21673 was 21667, checked in by dmik, 14 years ago

kernel32: Fixed a regression in r21655 that caused crashes in DOSCALL1 when writing to memory mapped files. The wrong code path would incorrectly invalidate memory pages on each write. Closes Java ticket 129.

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