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

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

PF: Corrected HFILE definition as it is in Wine and in Win2k

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