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

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

memory map updates

File size: 32.7 KB
Line 
1/* $Id: mmap.cpp,v 1.64 2003-03-27 14:13: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(HFILE 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 dprintf(("Win32MemMap::commitPage %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
312
313 //align at page boundary
314 offset &= ~0xFFF;
315
316 //If it's a write access violation and the view is readonly, then fail
317 if(fWriteAccess) {
318 Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr);
319 if(view) {
320 if(!(view->getAccessFlags() & MEMMAP_ACCESS_WRITE)) {
321 dprintf(("Write access for a readonly view!!"));
322 return FALSE;
323 }
324 }
325 else {
326 DebugInt3(); //can't happen
327 return FALSE;
328 }
329 }
330
331 int faultsize = nrpages*PAGE_SIZE;
332
333 if(fWriteAccess)
334 {//write access needs special care, so do that on a per page basis
335 dprintf(("Write access -> handle only one page"));
336 faultsize = PAGE_SIZE;
337 }
338
339 offset = pageAddr - (ULONG)pMapping;
340 if(offset + faultsize > mSize) {
341 faultsize = mSize - offset;
342 }
343
344 while(faultsize)
345 {
346 if(VirtualQuery((LPSTR)pageAddr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
347 dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
348 goto fail;
349 }
350 memInfo.RegionSize = min(memInfo.RegionSize, faultsize);
351 //Only changes the state of the pages with the same attribute flags
352 //(returned in memInfo.RegionSize)
353 //If it's smaller than the mNrPages, it simply means one or more of the
354 //other pages have already been committed
355 if(!(memInfo.State & MEM_COMMIT))
356 {
357 if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, PAGE_READWRITE) == FALSE) {
358 goto fail;
359 }
360
361 //Part of the memory map has been committed, so now we can change
362 //the protection flags of the aliases (DosSetMem fails for reserved pages)
363 updateViewPages(offset, memInfo.RegionSize, (fWriteAccess) ? PAGEVIEW_VIEW : PAGEVIEW_READONLY);
364
365 if(hMemFile != -1)
366 {//now read the page(s) from disk
367 DWORD size;
368
369 offset = pageAddr - (ULONG)pMapping;
370 size = memInfo.RegionSize;
371 if(offset + size > mSize) {
372 dprintf(("Adjusting size from %d to %d", size, mSize - offset));
373 size = mSize - offset;
374 }
375 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
376 dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
377 goto fail;
378 }
379 if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
380 dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
381 goto fail;
382 }
383 if(nrBytesRead != size) {
384 dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
385 goto fail;
386 }
387 }
388 //We set the protection flags to PAGE_READONLY, unless this pagefault
389 //was due to a write access
390 //This way we can track dirty pages which need to be flushed to
391 //disk when FlushViewOfFile is called or the map is closed.
392 if(!fWriteAccess)
393 {
394 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READONLY, &oldProt) == FALSE) {
395 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
396 goto fail;
397 }
398 }
399 else
400 {//make these pages as dirty
401 ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
402 ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT;
403
404 if(memInfo.RegionSize & 0xFFF)
405 nrPages++;
406
407 dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
408 markDirtyPages(startPage, nrPages);
409 }
410 }
411 else
412 if(fWriteAccess)
413 {
414 //mark these pages as dirty
415 ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
416 ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT;
417
418 if(memInfo.RegionSize & 0xFFF)
419 nrPages++;
420
421 dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
422 markDirtyPages(startPage, nrPages);
423
424 //and turn on a write access
425 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READWRITE, &oldProt) == FALSE) {
426 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
427 goto fail;
428 }
429 //Now change all the aliased pages according to their view protection flags
430 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_VIEW);
431 }
432 faultsize -= memInfo.RegionSize;
433 pageAddr += memInfo.RegionSize;
434 }
435
436// mapMutex.leave();
437 return TRUE;
438fail:
439// mapMutex.leave();
440 return FALSE;
441}
442//******************************************************************************
443// Win32MemMap::commitGuardPage
444//
445// Handle a guard page exception
446//
447// Parameters:
448//
449// ULONG ulFaultAddr - exception address
450// ULONG ulOffset - offset in memory map
451// BOOL fWriteAccess - TRUE -> write exception
452// FALSE -> read exception
453//
454// Returns:
455// TRUE - success
456// FALSE - failure
457//
458//******************************************************************************
459BOOL Win32MemMap::commitGuardPage(ULONG ulFaultAddr, ULONG ulOffset, BOOL fWriteAccess)
460{
461 MEMORY_BASIC_INFORMATION memInfo;
462 BOOL ret;
463 DWORD pageAddr = ulFaultAddr & ~0xFFF;
464 DWORD dwNewProt, dwOldProt;
465
466 dprintf(("Win32MemMap::commitGuardPage %x (faultaddr %x)", pageAddr, ulFaultAddr));
467
468 //align at page boundary
469 ulOffset &= ~0xFFF;
470
471 return TRUE;
472fail:
473 return FALSE;
474}
475//******************************************************************************
476// Win32MemMap::updateViewPages
477//
478// Update the page flags of all views
479//
480// Parameters:
481//
482// ULONG offset - offset in memory map
483// ULONG size - range size
484// PAGEVIEW flags - page flags
485// PAGEVIEW_READONLY -> set page flags to readonly
486// PAGEVIEW_VIEW -> set page flags to view default
487//
488// Returns:
489// TRUE - success
490// FALSE - failure
491//
492//******************************************************************************
493BOOL Win32MemMap::updateViewPages(ULONG offset, ULONG size, PAGEVIEW flags)
494{
495 Win32MemMapView **views = (Win32MemMapView **)alloca(sizeof(Win32MemMapView*)*nrMappings);
496 if(views)
497 {
498 if(Win32MemMapView::findViews(this, nrMappings, views) == nrMappings)
499 {
500 for(int i=0;i<nrMappings;i++)
501 {
502 views[i]->changePageFlags(offset, size, flags);
503 }
504 }
505 else DebugInt3(); //oh, oh
506 }
507 return TRUE;
508}
509//******************************************************************************
510// Win32MemMap::invalidatePages
511//
512// Invalidate map pages. (called by WriteFile)
513//
514// Parameters:
515//
516// ULONG offset - offset in memory map
517// ULONG size - invalid range size
518//
519// Returns:
520// TRUE - success
521// FALSE - failure
522//
523//******************************************************************************
524BOOL Win32MemMap::invalidatePages(ULONG offset, ULONG size)
525{
526 ULONG diff = offset & 0xFFF;
527 BOOL ret;
528
529 offset &= ~0xFFF;
530 size += diff;
531
532 dprintf(("Win32MemMap::invalidatePages %x %x", offset, size));
533 ret = VirtualFree((LPSTR)pMapping + offset, size, MEM_DECOMMIT);
534 if(ret == FALSE) {
535 dprintf(("ERROR: Win32MemMap::invalidatePages: VirtualFree failed!!"));
536 }
537 return TRUE;
538}
539//******************************************************************************
540// Win32MemMap::allocateMap
541//
542// Allocate memory for the map if not yet already done.
543//
544// Returns:
545// FALSE - success
546// TRUE - failure
547//
548//******************************************************************************
549BOOL Win32MemMap::allocateMap()
550{
551 ULONG fAlloc = 0;
552
553 fAlloc = MEM_RESERVE;
554
555 //Memory has already been allocated for executable image maps (only used internally)
556 if(!pMapping && nrMappings == 0)
557 {//if not mapped, reserve/commit entire view
558 //SvL: Always read/write access or else ReadFile will crash once we
559 // start committing pages.
560 // This is most likely an OS/2 bug and doesn't happen in Aurora
561 // when allocating memory with the PAG_ANY bit set. (without this
562 // flag it will also crash)
563 //NOTE: If this is ever changed, then we must update setProtFlags!!!!
564
565 //All named file mappings are shared (files & memory only)
566 if(lpszMapName) {
567 pMapping = VirtualAllocShared(mSize, fAlloc, PAGE_READWRITE, lpszMapName);
568 }
569 else {
570 pMapping = VirtualAlloc(0, mSize, fAlloc, PAGE_READWRITE);
571 }
572 if(pMapping == NULL) {
573 dprintf(("Win32MemMap::mapViewOfFile: VirtualAlloc %x %x failed!", mSize, fAlloc));
574 goto fail;
575 }
576 //Windows NT seems to commit memory for memory maps, regardsless of the SEC_COMMIT flag
577 if((hMemFile == -1 && !image)) {//commit memory
578 VirtualAlloc(pMapping, mSize, MEM_COMMIT, PAGE_READWRITE);
579 }
580
581 DWORD nrPages = mSize >> PAGE_SHIFT;
582 if(mSize & 0xFFF)
583 nrPages++;
584
585 if(hMemFile != -1 && (mProtFlags & SEC_COMMIT)) {
586 commitPage((ULONG)pMapping, 0, FALSE, nrPages);
587 }
588 //Allocate bitmap for all pages to keep track of write access (file maps only)
589 //Necessary for FlushViewOfFile.
590 if(hMemFile != -1) {
591 int sizebitmap = nrPages/8 + 1;
592
593 pWriteBitmap = (char *)_smalloc(sizebitmap);
594 if(pWriteBitmap == NULL) {
595 DebugInt3();
596 goto fail;
597 }
598 memset(pWriteBitmap, 0, sizebitmap);
599 }
600 }
601 return TRUE;
602
603fail:
604 return FALSE;
605}
606//******************************************************************************
607// Win32MemMap::mapViewOfFile
608//
609// Map the view identified by addr
610//
611// Parameters:
612//
613// ULONG size - size of view
614// ULONG offset - offset in memory map
615// ULONG fdwAccess - access flags
616// FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_COPY
617// FILE_MAP_ALL_ACCESS
618//
619//
620// Returns:
621// <>NULL - success, view address
622// NULL - failure
623//
624//******************************************************************************
625LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
626{
627 DWORD processId = GetCurrentProcessId();
628
629 mapMutex.enter();
630 ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
631 Win32MemMapView *mapview;
632
633 //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY))
634 // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though.
635 if(fdwAccess & ~FILE_MAP_ALL_ACCESS)
636 goto parmfail;
637 if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
638 goto parmfail;
639 if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
640 goto parmfail;
641
642 if (fdwAccess != FILE_MAP_ALL_ACCESS)
643 if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
644 goto parmfail;
645
646 if(offset+size > mSize && (!(fdwAccess & FILE_MAP_WRITE) || hMemFile == -1))
647 goto parmfail;
648
649 //SvL: TODO: Doesn't work for multiple views
650 if(offset+size > mSize) {
651 mSize = offset+size;
652 }
653
654 if(allocateMap() == FALSE) {
655 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
656 goto fail;
657 }
658
659 mapview = new Win32MemMapView(this, offset, (size == 0) ? (mSize - offset) : size, fdwAccess);
660 if(mapview == NULL) {
661 goto fail;
662 }
663 if(mapview->everythingOk() == FALSE) {
664 dprintf(("Win32MemMap::mapViewOfFile: !mapview->everythingOk"));
665 delete mapview;
666 goto fail;
667 }
668 mapMutex.leave();
669 SetLastError(ERROR_SUCCESS);
670 return mapview->getViewAddr();
671
672parmfail:
673 dprintf(("Win32MemMap::mapViewOfFile: invalid parameter (ERROR_ACCESS_DENIED)"));
674 //NT4 SP6 returns ERROR_ACCESS_DENIED for most invalid parameters
675 SetLastError(ERROR_ACCESS_DENIED);
676fail:
677 mapMutex.leave();
678 return 0;
679}
680//******************************************************************************
681// Win32MemMap::unmapViewOfFile
682//
683// Unmap the view identified by addr
684//
685// Parameters:
686//
687// LPVOID addr - view address; doesn't need to be the address
688// returned by MapViewOfFile(Ex) (as MSDN clearly says);
689// can be any address within the view range
690//
691// Returns:
692// TRUE - success
693// FALSE - failure
694//
695//******************************************************************************
696BOOL Win32MemMap::unmapViewOfFile(LPVOID addr)
697{
698 Win32MemMapView *view;
699
700 dprintf(("Win32MemMap::unmapViewOfFile %x (nrmaps=%d)", addr, nrMappings));
701 mapMutex.enter();
702
703 if(nrMappings == 0)
704 goto fail;
705
706 view = Win32MemMapView::findView((ULONG)addr);
707 if(view == NULL)
708 goto fail;
709
710 delete view;
711
712 if(nrMappings == 0) {
713 VirtualFree(pMapping, 0, MEM_RELEASE);
714 pMapping = NULL;
715 }
716 mapMutex.leave();
717
718 SetLastError(ERROR_SUCCESS);
719 return TRUE;
720fail:
721 mapMutex.leave();
722 SetLastError(ERROR_INVALID_ADDRESS);
723 return FALSE;
724}
725//******************************************************************************
726//We determine whether a page has been modified by checking it's protection flags
727//If the write flag is set, this means commitPage had to enable this due to a pagefault
728//(all pages are readonly until the app tries to modify the contents of the page)
729//
730//TODO: Are apps allowed to change the protection flags of memory mapped pages?
731// I'm assuming they aren't for now.
732//******************************************************************************
733BOOL Win32MemMap::flushView(ULONG viewaddr, ULONG offset, ULONG cbFlush)
734{
735 ULONG nrBytesWritten, size, accessflags, oldProt;
736 Win32MemMapView *view;
737 int i;
738
739 dprintf(("Win32MemMap::flushView: %x %x", (ULONG)pMapping+offset, cbFlush));
740
741 if(image) //no flushing for image maps
742 return TRUE;
743
744 if(hMemFile == -1)
745 goto success; //TODO: Return an error here?
746
747 if(offset > mSize)
748 goto parmfail;
749
750 if(viewaddr != MMAP_FLUSHVIEW_ALL)
751 {
752 view = Win32MemMapView::findView(viewaddr);
753 if(nrMappings == 0 || view == NULL) {
754 DebugInt3(); //should never happen
755 goto parmfail;
756 }
757 accessflags = view->getAccessFlags();
758 }
759 else {
760 //force a flush to disk; only those pages marked dirty are flushed anyway
761 accessflags = FILE_MAP_WRITE;
762 }
763 //If the view is readonly or copy on write, then the flush is ignored
764 if(!(accessflags & MEMMAP_ACCESS_WRITE) || (accessflags & MEMMAP_ACCESS_COPYONWRITE))
765 {
766 dprintf(("Readonly or Copy-On-Write memory map -> ignore flush"));
767 //this is not a failure; NT4 SP6 returns success
768 goto success;
769 }
770
771 if(cbFlush == 0)
772 cbFlush = mSize;
773
774 if(offset + cbFlush > mSize) {
775 cbFlush -= (offset + cbFlush - mSize);
776 }
777
778 //Check the write page bitmap for dirty pages and write them to disk
779 while(cbFlush)
780 {
781 int startPage = offset >> PAGE_SHIFT;
782 size = PAGE_SIZE;
783
784 if(isDirtyPage(startPage))
785 {
786 if(size > cbFlush) {
787 size = cbFlush;
788 }
789 dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
790
791 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
792 dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
793 goto fail;
794 }
795 if(WriteFile(hMemFile, (LPSTR)((ULONG)pMapping + offset), size, &nrBytesWritten, NULL) == FALSE) {
796 dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)pMapping + offset));
797 goto fail;
798 }
799 if(nrBytesWritten != size) {
800 dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)pMapping + offset));
801 goto fail;
802 }
803 clearDirtyPages(startPage, 1);
804
805 //We've just flushed the page to disk, so we need to track future writes
806 //again; Set page to readonly (first memory map, then alias(es))
807 if(VirtualProtect((LPVOID)((ULONG)pMapping + offset), size, PAGE_READONLY, &oldProt) == FALSE) {
808 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", (ULONG)pMapping + offset, size, GetLastError()));
809 goto fail;
810 }
811 updateViewPages(offset, size, PAGEVIEW_READONLY);
812 }
813
814 if(cbFlush < size)
815 break;
816
817 cbFlush -= size;
818 offset += size;
819 }
820success:
821 SetLastError(ERROR_SUCCESS);
822 return TRUE;
823
824parmfail:
825 SetLastError(ERROR_INVALID_PARAMETER);
826 return FALSE;
827fail:
828 return FALSE;
829}
830//******************************************************************************
831//******************************************************************************
832Win32MemMap *Win32MemMap::findMap(LPSTR lpszName)
833{
834 if(lpszName == NULL)
835 return NULL;
836
837 DosEnterCriticalSection(&globalmapcritsect);
838 Win32MemMap *map = memmaps;
839
840 if(map != NULL) {
841 while(map) {
842 if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName))
843 break;
844 map = map->next;
845 }
846 }
847 if(map) map->AddRef();
848
849 DosLeaveCriticalSection(&globalmapcritsect);
850 if(!map) dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName));
851 return map;
852}
853//******************************************************************************
854//******************************************************************************
855Win32MemMap *Win32MemMap::findMapByFile(HANDLE hFile)
856{
857 if(hFile == -1)
858 return NULL;
859
860 DosEnterCriticalSection(&globalmapcritsect);
861 Win32MemMap *map = memmaps;
862
863 if(map != NULL)
864 {
865 while(map) {
866 if(map->hOrgMemFile == hFile)
867 break;
868 map = map->next;
869 }
870 }
871 if(map) map->AddRef();
872 DosLeaveCriticalSection(&globalmapcritsect);
873 if(!map) dprintf2(("Win32MemMap::findMapByFile: couldn't find map with file handle %x", hFile));
874 return map;
875}
876//******************************************************************************
877//******************************************************************************
878Win32MemMap *Win32MemMap::findMap(ULONG address)
879{
880 DosEnterCriticalSection(&globalmapcritsect);
881 Win32MemMap *map = memmaps;
882
883 if(map != NULL) {
884 while(map) {
885 if(map->pMapping && (ULONG)map->pMapping <= address &&
886 (ULONG)map->pMapping + map->mSize > address)
887 {
888 break;
889 }
890 map = map->next;
891 }
892 }
893 if(map) map->AddRef();
894 DosLeaveCriticalSection(&globalmapcritsect);
895 return map;
896}
897//******************************************************************************
898//******************************************************************************
899void Win32MemMap::deleteAll()
900{
901 Win32MemMap *map, *nextmap;
902 DWORD processId = GetCurrentProcessId();
903
904 //delete all maps created by this process
905 DosEnterCriticalSection(&globalmapcritsect);
906
907startdeleteviews:
908 map = memmaps;
909 while(map) {
910 map->AddRef(); //make sure it doesn't get deleted
911
912 //delete all views created by this process for this map
913 Win32MemMapView::deleteViews(map);
914
915 nextmap = map->next;
916
917 //map->Release can delete multiple objects (duplicate memory map), so make
918 //sure our nextmap pointer remains valid by increasing the refcount
919 if(nextmap) nextmap->AddRef();
920 map->Release();
921
922 if(nextmap && nextmap->Release() == 0) {
923 //oops, nextmap was just deleted and is no longer valid
924 //can't continue from here, so let's start again
925 dprintf(("oops, nextmap is invalid -> start again (1)"));
926 goto startdeleteviews;
927 }
928
929 map = nextmap;
930 }
931startdelete:
932 map = memmaps;
933 while(map) {
934 nextmap = map->next;
935 if(map->getProcessId() == processId)
936 {
937 //delete map can delete multiple objects (duplicate memory map), so make
938 //sure our nextmap pointer remains valid by increasing the refcount
939 if(nextmap) nextmap->AddRef();
940 delete map;
941
942 if(nextmap && nextmap->Release() == 0) {
943 //oops, nextmap was just deleted and is no longer valid
944 //can't continue from here, so let's start again
945 dprintf(("oops, nextmap is invalid -> start again (2)"));
946 goto startdelete;
947 }
948 }
949 map = nextmap;
950 }
951 DosLeaveCriticalSection(&globalmapcritsect);
952}
953//******************************************************************************
954// Win32MemMapView::markDirtyPages
955//
956// Mark pages as dirty (changed) in the write page bitmap
957//
958// Parameters:
959//
960// int startpage - start page
961// int nrpages - number of pages
962//
963//
964//******************************************************************************
965void Win32MemMap::markDirtyPages(int startpage, int nrpages)
966{
967 if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
968
969 for(int i=startpage;i<startpage+nrpages;i++) {
970 set_bit(i, pWriteBitmap);
971 }
972}
973//******************************************************************************
974// Win32MemMapView::clearDirtyPages
975//
976// Mark pages as clean in the write page bitmap
977//
978// Parameters:
979//
980// int startpage - start page
981// int nrpages - number of pages
982//
983//
984//******************************************************************************
985void Win32MemMap::clearDirtyPages(int startpage, int nrpages)
986{
987 if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
988
989 for(int i=startpage;i<startpage+nrpages;i++) {
990 clear_bit(i, pWriteBitmap);
991 }
992}
993//******************************************************************************
994//******************************************************************************
Note: See TracBrowser for help on using the repository browser.