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

Last change on this file since 1641 was 1641, checked in by sandervl, 26 years ago

Critical section Wine port + mmap fix (limit size of mmap files)

File size: 18.5 KB
Line 
1/* $Id: mmap.cpp,v 1.21 1999-11-08 20:53:24 sandervl Exp $ */
2
3/*
4 * Win32 Memory mapped file & view classes
5 *
6 * Copyright 1999 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 *
19 * Project Odin Software License can be found in LICENSE.TXT
20 *
21 */
22#include <os2win.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <win\virtual.h>
27#include <vmutex.h>
28#include <handlemanager.h>
29#include "mmap.h"
30#include "oslibdos.h"
31
32//Global DLL Data
33#pragma data_seg(_GLOBALDATA)
34Win32MemMap *Win32MemMap::memmaps = NULL;
35VMutex globalmapMutex(VMUTEX_SHARED);
36#pragma data_seg()
37VMutex globalviewMutex;
38Win32MemMapView *Win32MemMapView::mapviews = NULL;
39
40//******************************************************************************
41//TODO: sharing between processes
42//******************************************************************************
43Win32MemMap::Win32MemMap(HFILE hfile, ULONG size, ULONG fdwProtect, LPSTR lpszName)
44 : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0)
45{
46 globalmapMutex.enter();
47 next = memmaps;
48 memmaps = this;
49 globalmapMutex.leave();
50
51 hMemFile = hfile;
52
53 mSize = size;
54 mProtFlags = fdwProtect;
55 mProcessId = GetCurrentProcess();
56
57 if(lpszName) {
58 lpszMapName = (char *)_smalloc(strlen(lpszName)+1);
59 strcpy(lpszMapName, lpszName);
60 }
61 else lpszMapName = NULL;
62}
63//******************************************************************************
64//******************************************************************************
65BOOL Win32MemMap::Init(HANDLE hMemMap)
66{
67 mapMutex.enter();
68 if(hMemFile != -1)
69 {
70 if(DuplicateHandle(mProcessId, hMemFile, GetCurrentProcess(),
71 &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE)
72 {
73 dprintf(("Win32MemMap::Init: DuplicateHandle failed!"));
74 goto fail;
75 }
76 mSize = SetFilePointer(hMemFile, 0, NULL, FILE_END);
77 if(mSize == -1) {
78 dprintf(("Win32MemMap::init: SetFilePointer failed to set pos end"));
79 goto fail;
80 }
81 //SvL: Temporary limitation of size (Warp Server Advanced doesn't allow
82 // one to reserve more than 450 MB of continuous memory; (Warp 4
83 // much less))
84 if(mSize > 64*1024*1024) {
85 mSize = 64*1024*1024;
86 }
87 }
88
89 dprintf(("CreateFileMappingA for file %x, prot %x size %d, name %s", hMemFile, mProtFlags, mSize, lpszMapName));
90 this->hMemMap = hMemMap;
91 mapMutex.leave();
92 return TRUE;
93fail:
94 mapMutex.leave();
95 return FALSE;
96}
97//******************************************************************************
98//******************************************************************************
99Win32MemMap::~Win32MemMap()
100{
101 Win32MemMapView::deleteViews(this); //delete all views of our memory mapped file
102
103 dprintf(("Win32MemMap dtor: deleting view %x %x", pMapping, mSize));
104
105 mapMutex.enter();
106 if(lpszMapName) {
107 free(lpszMapName);
108 }
109 if(pMapping) {
110 if(lpszMapName) {
111 OSLibDosFreeMem(pMapping);
112 }
113 else VirtualFree(pMapping, mSize, MEM_RELEASE);
114
115 pMapping = NULL;
116 }
117 if(hMemFile != -1) {
118 CloseHandle(hMemFile);
119 hMemFile = -1;
120 }
121 mapMutex.leave();
122
123 globalmapMutex.enter();
124 Win32MemMap *map = memmaps;
125
126 if(map == this) {
127 memmaps = next;
128 }
129 else {
130 while(map->next) {
131 if(map->next == this)
132 break;
133 map = map->next;
134 }
135 if(map->next) {
136 map->next = next;
137 }
138 else dprintf(("Win32MemMap::~Win32MemMap: map not found!! (%x)", this));
139 }
140 globalmapMutex.leave();
141}
142//******************************************************************************
143//We determine whether a page has been modified by checking it's protection flags
144//If the write flag is set, this means commitPage had to enable this due to a pagefault
145//(all pages are readonly until the app tries to write to it)
146//******************************************************************************
147BOOL Win32MemMap::commitPage(ULONG offset, BOOL fWriteAccess)
148{
149 MEMORY_BASIC_INFORMATION memInfo;
150 LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset);
151 DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF;
152 DWORD oldProt, newProt, nrBytesRead, size;
153
154// mapMutex.enter();
155
156 newProt = mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY);
157
158 dprintf(("Win32MemMap::commitPage %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
159 if(hMemFile != -1) {
160 if(VirtualQuery((LPSTR)pageAddr, &memInfo, NRPAGES_TOCOMMIT*PAGE_SIZE) == 0) {
161 dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, NRPAGES_TOCOMMIT*PAGE_SIZE));
162 goto fail;
163 }
164 //Only changes the state of the pages with the same attribute flags
165 //(returned in memInfo.RegionSize)
166 //If it's smaller than the mNrPages, it simply means one or more of the
167 //other pages have already been committed
168 if(memInfo.State & MEM_COMMIT)
169 {//if it's already committed, then the app tried to write to it
170 if(!fWriteAccess) {
171 dprintf(("Win32MemMap::commitPage: Huh? Already committed and not trying to write (%x,%x) failed for %x", pageAddr, memInfo.RegionSize));
172 goto fail;
173 }
174 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, newProt, &oldProt) == FALSE) {
175 dprintf(("Win32MemMap::commitPage: Failed to set write flag on page (%x,%x) failed for %x", pageAddr, memInfo.RegionSize));
176 goto fail;
177 }
178 }
179 else {
180 if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, PAGE_READWRITE) == FALSE) {
181 goto fail;
182 }
183 offset = pageAddr - (ULONG)pMapping;
184 size = memInfo.RegionSize;
185 if(offset + size > mSize) {
186 dprintf(("Adjusting size from %d to %d", size, mSize - offset));
187 size = mSize - offset;
188 }
189 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
190 dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
191 goto fail;
192 }
193 if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
194 dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
195 goto fail;
196 }
197 if(nrBytesRead != size) {
198 dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
199 goto fail;
200 }
201 if(mProtFlags & PAGE_READONLY) {
202 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, newProt, &oldProt) == FALSE) {
203 goto fail;
204 }
205 }
206 }
207 }
208 else {
209 if(VirtualQuery((LPSTR)pageAddr, &memInfo, NRPAGES_TOCOMMIT*PAGE_SIZE) == 0) {
210 dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, NRPAGES_TOCOMMIT*PAGE_SIZE));
211 goto fail;
212 }
213 if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, newProt) == FALSE) {
214 goto fail;
215 }
216 }
217
218// mapMutex.leave();
219 return TRUE;
220fail:
221// mapMutex.leave();
222 return FALSE;
223}
224//******************************************************************************
225//******************************************************************************
226BOOL Win32MemMap::unmapViewOfFile(Win32MemMapView *view)
227{
228 mapMutex.enter();
229
230 if(nrMappings == 0)
231 goto fail;
232
233 delete view;
234
235 if(--nrMappings) {
236 VirtualFree(pMapping, mSize, MEM_RELEASE);
237 pMapping = NULL;
238 }
239 mapMutex.leave();
240 return TRUE;
241fail:
242 mapMutex.leave();
243 return FALSE;
244}
245//******************************************************************************
246//******************************************************************************
247LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
248{
249 DWORD processId = GetCurrentProcess();
250
251 mapMutex.enter();
252 ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
253 ULONG fAlloc = 0;
254 Win32MemMapView *mapview;
255
256 //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY))
257 // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though.
258 if(fdwAccess & ~FILE_MAP_ALL_ACCESS)
259 goto parmfail;
260 if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
261 goto parmfail;
262 if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
263 goto parmfail;
264
265 //@@@PH
266 if (fdwAccess != FILE_MAP_ALL_ACCESS)
267 if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
268 goto parmfail;
269
270 if(offset+size > mSize)
271 goto parmfail;
272
273//TODO: If committed, read file into memory
274#if 0
275 if(mProtFlags & SEC_COMMIT)
276 fAlloc |= MEM_COMMIT;
277 else
278 if(mProtFlags & SEC_RESERVE)
279 fAlloc |= MEM_RESERVE;
280#else
281 fAlloc = MEM_RESERVE;
282#endif
283
284 if(nrMappings == 0) {//if not mapped, reserve/commit entire view
285 //SvL: Always read/write access or else ReadFile will crash once we
286 // start committing pages.
287 // This is most likely an OS/2 bug and doesn't happen in Aurora
288 // when allocating memory with the PAG_ANY bit set. (without this
289 // flag it will also crash)
290 if(lpszMapName) {
291 pMapping = VirtualAllocShared(mSize, fAlloc, PAGE_READWRITE, lpszMapName);
292 }
293 else {
294 pMapping = VirtualAlloc(0, mSize, fAlloc, PAGE_READWRITE);
295 }
296 if(pMapping == NULL) {
297 dprintf(("Win32MemMap::mapFileView: VirtualAlloc %x %x %x failed!", mSize, fAlloc, memFlags));
298 goto fail;
299 }
300 }
301 mapview = new Win32MemMapView(this, offset, (size == 0) ? mSize : size, fdwAccess);
302 if(mapview == NULL) {
303 goto fail;
304 }
305 if(mapview->everythingOk() == FALSE) {
306 delete mapview;
307 goto fail;
308 }
309 nrMappings++;
310 mapMutex.leave();
311 return mapview->getViewAddr();
312
313parmfail:
314 dprintf(("Win32MemMap::mapFileView: ERROR_INVALID_PARAMETER"));
315 SetLastError(ERROR_INVALID_PARAMETER);
316fail:
317 mapMutex.leave();
318 return 0;
319}
320//******************************************************************************
321//We determine whether a page has been modified by checking it's protection flags
322//If the write flag is set, this means commitPage had to enable this due to a pagefault
323//(all pages are readonly until the app tries to modify the contents of the page)
324//
325//TODO: Are apps allowed to change the protection flags of memory mapped pages?
326// I'm assuming they aren't for now.
327//******************************************************************************
328BOOL Win32MemMap::flushView(ULONG offset, ULONG cbFlush)
329{
330 LPVOID lpvBase = (LPVOID)((ULONG)pMapping+offset);
331 MEMORY_BASIC_INFORMATION memInfo;
332 ULONG nrBytesWritten, size;
333 int i;
334
335 dprintf(("Win32MemMap::flushView: %x %x", lpvBase, cbFlush));
336 if(nrMappings == 0)
337 goto parmfail;
338
339 if(cbFlush == 0)
340 cbFlush = mSize;
341
342 if(lpvBase < pMapping || (ULONG)lpvBase+cbFlush > (ULONG)pMapping+mSize)
343 goto parmfail;
344
345 if(mProtFlags & PAGE_READONLY)
346 goto parmfail;
347
348 if(hMemFile == -1)
349 goto success; //TODO: Return an error here?
350
351 while(cbFlush) {
352 if(VirtualQuery((LPSTR)lpvBase, &memInfo, cbFlush) == 0) {
353 dprintf(("Win32MemMap::flushView: VirtualQuery (%x,%x) failed for %x", lpvBase, cbFlush, (ULONG)lpvBase+i*PAGE_SIZE));
354 goto fail;
355 }
356 //If a page (or range of pages) is reserved or write protected, we
357 //won't bother flushing it to disk
358 if(memInfo.State & MEM_COMMIT &&
359 memInfo.AllocationProtect & (PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))
360 {//committed and allowed for writing?
361 offset = (ULONG)lpvBase - (ULONG)pMapping;
362 size = memInfo.RegionSize;
363 if(size > cbFlush) {
364 size = cbFlush;
365 }
366 dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
367
368 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
369 dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
370 goto fail;
371 }
372 if(WriteFile(hMemFile, (LPSTR)lpvBase, size, &nrBytesWritten, NULL) == FALSE) {
373 dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)lpvBase));
374 goto fail;
375 }
376 if(nrBytesWritten != size) {
377 dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)lpvBase));
378 goto fail;
379 }
380 }
381 lpvBase = (LPVOID)((ULONG)lpvBase + memInfo.RegionSize);
382
383 if(cbFlush < memInfo.RegionSize)
384 break;
385
386 cbFlush -= memInfo.RegionSize;
387 }
388success:
389 return TRUE;
390parmfail:
391 SetLastError(ERROR_INVALID_PARAMETER);
392 return FALSE;
393fail:
394 return FALSE;
395}
396//******************************************************************************
397//******************************************************************************
398Win32MemMap *Win32MemMap::findMap(LPSTR lpszName)
399{
400 if(lpszName == NULL)
401 return NULL;
402
403 globalmapMutex.enter();
404 Win32MemMap *map = memmaps;
405
406 if(map != NULL) {
407 while(map) {
408 if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName))
409 break;
410 map = map->next;
411 }
412 }
413 globalmapMutex.leave();
414 if(!map) dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName));
415 return map;
416}
417//******************************************************************************
418//******************************************************************************
419Win32MemMap *Win32MemMap::findMap(ULONG address)
420{
421 globalmapMutex.enter();
422 Win32MemMap *map = memmaps;
423
424 if(map != NULL) {
425 while(map) {
426 if(map->pMapping && (ULONG)map->pMapping <= address &&
427 (ULONG)map->pMapping + map->mSize > address)
428 {
429 break;
430 }
431 map = map->next;
432 }
433 }
434 globalmapMutex.leave();
435 return map;
436}
437//******************************************************************************
438//Assumes mutex has been acquired
439//******************************************************************************
440void Win32MemMap::deleteAll()
441{
442 Win32MemMap *map = memmaps, *nextmap;
443 DWORD processId = GetCurrentProcess();
444
445 //delete all maps created by this process
446 while(map) {
447 nextmap = map->next;
448 if(map->getProcessId() == processId) {
449 CloseHandle(memmaps->hMemMap);
450 }
451 else {
452 //delete all views created by this process for this map
453 Win32MemMapView::deleteViews(map);
454 }
455 map = nextmap;
456 }
457}
458//******************************************************************************
459//******************************************************************************
460Win32MemMapView::Win32MemMapView(Win32MemMap *map, ULONG offset, ULONG size,
461 ULONG fdwAccess)
462{
463 LPVOID viewaddr = (LPVOID)((ULONG)map->getMappingAddr()+offset);
464 ULONG accessAttr = 0;
465 Win32MemMapView *tmpview = mapviews;
466
467 errorState = 0;
468 mParentMap = map;
469 mSize = size;
470 mOffset = offset;
471 mProcessId = GetCurrentProcess();
472
473 switch(fdwAccess) {
474 case FILE_MAP_READ:
475 accessAttr = PAG_READ;
476 mfAccess = MEMMAP_ACCESS_READ;
477 break;
478 case FILE_MAP_WRITE:
479 case FILE_MAP_COPY:
480 accessAttr = (PAG_READ|PAG_WRITE);
481 mfAccess = MEMMAP_ACCESS_WRITE;
482 break;
483 }
484 if(map->getMemName() != NULL) {
485 //shared memory map, so map it into our address space
486 if(OSLibDosGetNamedSharedMem((LPVOID *)&viewaddr, map->getMemName()) != OSLIB_NOERROR) {
487 dprintf(("new OSLibDosGetNamedSharedMem FAILED"));
488 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
489 errorState = 1;
490 return;
491 }
492 }
493
494 if(OSLibDosAliasMem(viewaddr, size, &pMapView, accessAttr) != OSLIB_NOERROR) {
495 dprintf(("new OSLibDosAliasMem FAILED"));
496 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
497 errorState = 1;
498 return;
499 }
500
501 dprintf(("Win32MemMapView::Win32MemMapView: created %x (alias for %x), size %d", pMapView, viewaddr, size));
502
503 globalviewMutex.enter();
504 if(tmpview == NULL || tmpview->getViewAddr() > pMapView) {
505 next = mapviews;
506 mapviews = this;
507 }
508 else {
509 while(tmpview->next) {
510 if(tmpview->next->getViewAddr() > pMapView) {
511 break;
512 }
513 tmpview = tmpview->next;
514 }
515 next = tmpview->next;
516 tmpview->next = this;
517 }
518 globalviewMutex.leave();
519}
520//******************************************************************************
521//******************************************************************************
522Win32MemMapView::~Win32MemMapView()
523{
524 if(errorState != 0)
525 return;
526
527 dprintf(("Win32MemMapView dtor: deleting view %x %x", mOffset, mSize));
528
529 if(mfAccess != MEMMAP_ACCESS_READ)
530 mParentMap->flushView(mOffset, mSize);
531
532 OSLibDosFreeMem(pMapView);
533
534 globalviewMutex.enter();
535 Win32MemMapView *view = mapviews;
536
537 if(view == this) {
538 mapviews = next;
539 }
540 else {
541 while(view->next) {
542 if(view->next == this)
543 break;
544 view = view->next;
545 }
546 if(view->next) {
547 view->next = next;
548 }
549 else dprintf(("Win32MemMapView::~Win32MemMapView: map not found!! (%x)", this));
550 }
551 globalviewMutex.leave();
552}
553//******************************************************************************
554//******************************************************************************
555void Win32MemMapView::deleteViews(Win32MemMap *map)
556{
557 globalviewMutex.enter();
558 Win32MemMapView *view = mapviews, *nextview;
559
560 if(view != NULL) {
561 while(view) {
562 nextview = view->next;
563 if(view->getParentMap() == map)
564 {
565 globalviewMutex.leave();
566 delete view;
567 globalviewMutex.enter();
568 }
569 view = nextview;
570 }
571 }
572 globalviewMutex.leave();
573}
574//******************************************************************************
575//******************************************************************************
576Win32MemMap *Win32MemMapView::findMapByView(ULONG address, ULONG *offset,
577 ULONG accessType,
578 Win32MemMapView **pView)
579{
580 globalviewMutex.enter();
581 Win32MemMapView *view = mapviews;
582
583 *offset = 0;
584
585 if(view != NULL) {
586 while(view && (ULONG)view->getViewAddr() <= address) {
587 if((ULONG)view->getViewAddr() <= address &&
588 (ULONG)view->getViewAddr() + view->getSize() > address &&
589 view->getAccessFlags() >= accessType)
590 {
591 *offset = view->getOffset() + (address - (ULONG)view->getViewAddr());
592 goto success;
593 }
594 view = view->next;
595 }
596 //failure if we get here
597 view = NULL;
598 }
599success:
600 if(view)
601 dprintf(("findMapByView %x %x -> %x off %x", address, accessType, view->getViewAddr(), *offset));
602
603 globalviewMutex.leave();
604 if(pView) *pView = view;
605 return (view) ? view->getParentMap() : NULL;
606}
607//******************************************************************************
608//******************************************************************************
609Win32MemMapView *Win32MemMapView::findView(LPVOID address)
610{
611 Win32MemMapView *view = mapviews;
612
613 if(view != NULL) {
614 while(view) {
615 if(view->getViewAddr() == address)
616 {
617 break;
618 }
619 view = view->next;
620 }
621 }
622 return view;
623}
624//******************************************************************************
625//******************************************************************************
626
Note: See TracBrowser for help on using the repository browser.