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

Last change on this file since 10542 was 10542, checked in by sandervl, 21 years ago

compile fix

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