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

Last change on this file since 21472 was 21472, checked in by dmik, 15 years ago

kernel32: Allow FILE_MAP_READ access for PAGE_WRITECOPY mappings. Fixes #14.

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))
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.