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

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

Fixed closing of parent file handle by duplicate memory map; Compare file names instead of handles when checking for duplicate file maps

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