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

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

Workaround for OS/2 bug + VirtualProtect/VirtualQuery bugfixes

File size: 12.5 KB
Line 
1/* $Id: mmap.cpp,v 1.16 1999-08-26 17:56:25 sandervl Exp $ */
2
3/*
4 * Win32 Memory mapped file class
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
31VMutex globalmapMutex;
32
33//******************************************************************************
34//TODO: sharing between processes
35//******************************************************************************
36Win32MemMap::Win32MemMap(HFILE hfile, ULONG size, ULONG fdwProtect, LPSTR lpszName)
37 : fMapped(FALSE), pMapping(NULL), mMapAccess(0), referenced(0)
38{
39 globalmapMutex.enter();
40 next = memmaps;
41 memmaps = this;
42 globalmapMutex.leave();
43
44 hMemFile = hfile;
45
46 mSize = size;
47 mProtFlags = fdwProtect;
48
49 if(lpszName) {
50 lpszMapName = (char *)malloc(strlen(lpszName)+1);
51 strcpy(lpszMapName, lpszName);
52 }
53 else lpszMapName = NULL;
54}
55//******************************************************************************
56//******************************************************************************
57BOOL Win32MemMap::Init(HANDLE hMemMap)
58{
59 mapMutex.enter();
60 if(hMemFile != -1)
61 {
62 if(DuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(),
63 &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE)
64 {
65 dprintf(("Win32MemMap::Init: DuplicateHandle failed!"));
66 goto fail;
67 }
68 mSize = SetFilePointer(hMemFile, 0, NULL, FILE_END);
69 if(mSize == -1) {
70 dprintf(("Win32MemMap::init: SetFilePointer failed to set pos end"));
71 goto fail;
72 }
73 }
74 this->hMemMap = hMemMap;
75 mapMutex.leave();
76 return TRUE;
77fail:
78 mapMutex.leave();
79 return FALSE;
80}
81//******************************************************************************
82//******************************************************************************
83Win32MemMap::~Win32MemMap()
84{
85 unmapViewOfFile();
86 if(lpszMapName) {
87 free(lpszMapName);
88 }
89 mapMutex.enter();
90 if(pMapping) {
91 VirtualFree(pMapping, mSize, MEM_RELEASE);
92 pMapping = NULL;
93 }
94 if(hMemFile != -1) {
95 CloseHandle(hMemFile);
96 hMemFile = -1;
97 }
98 mapMutex.leave();
99
100 globalmapMutex.enter();
101 Win32MemMap *map = memmaps;
102
103 if(map == this) {
104 memmaps = next;
105 }
106 else {
107 while(map->next) {
108 if(map->next == this)
109 break;
110 map = map->next;
111 }
112 if(map->next) {
113 map->next = next;
114 }
115 else dprintf(("Win32MemMap::~Win32MemMap: map not found!! (%x)", this));
116 }
117 globalmapMutex.leave();
118}
119//******************************************************************************
120//******************************************************************************
121BOOL Win32MemMap::hasReadAccess()
122{
123 return TRUE; //should have at least this
124}
125//******************************************************************************
126//******************************************************************************
127BOOL Win32MemMap::hasWriteAccess()
128{
129 return !(mProtFlags & PAGE_READONLY);
130}
131//******************************************************************************
132//Might want to add this feature for memory mapping executable & dll files in
133//the loader (done in Win32 with the SEC_IMAGE flag?)
134//******************************************************************************
135BOOL Win32MemMap::hasExecuteAccess()
136{
137 return FALSE;
138}
139//******************************************************************************
140//We determine whether a page has been modified by checking it's protection flags
141//If the write flag is set, this means commitPage had to enable this due to a pagefault
142//(all pages are readonly until the app tries to write to it)
143//******************************************************************************
144BOOL Win32MemMap::commitPage(LPVOID lpPageFaultAddr, ULONG nrpages, BOOL fWriteAccess)
145{
146 MEMORY_BASIC_INFORMATION memInfo;
147 DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF;
148 DWORD oldProt, newProt, nrBytesRead, offset, size;
149
150// mapMutex.enter();
151 newProt = mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY);
152
153 dprintf(("Win32MemMap::commitPage %x (faultaddr %x), nr of pages %d", pageAddr, lpPageFaultAddr, nrpages));
154 if(hMemFile != -1) {
155 if(VirtualQuery((LPSTR)pageAddr, &memInfo, nrpages*PAGE_SIZE) == 0) {
156 dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
157 goto fail;
158 }
159 if(memInfo.State & MEM_COMMIT)
160 {//if it's already committed, then the app tried to write to it
161 if(!fWriteAccess) {
162 dprintf(("Win32MemMap::commitPage: Huh? Already committed and not trying to write (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
163 goto fail;
164 }
165 if(VirtualProtect((LPVOID)pageAddr, nrpages*PAGE_SIZE, newProt, &oldProt) == FALSE) {
166 dprintf(("Win32MemMap::commitPage: Failed to set write flag on page (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
167 goto fail;
168 }
169 }
170 else {
171 if(VirtualAlloc((LPVOID)pageAddr, nrpages*PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE) == FALSE) {
172 goto fail;
173 }
174 offset = pageAddr - (ULONG)pMapping;
175 size = nrpages*PAGE_SIZE;
176 if(offset + size > mSize) {
177 size = mSize - offset;
178 }
179 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
180 dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
181 goto fail;
182 }
183 if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
184 dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
185 goto fail;
186 }
187 if(nrBytesRead != size) {
188 dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
189 goto fail;
190 }
191 if(mProtFlags & PAGE_READONLY) {
192 if(VirtualProtect((LPVOID)pageAddr, nrpages*PAGE_SIZE, newProt, &oldProt) == FALSE) {
193 goto fail;
194 }
195 }
196 }
197 }
198 else {
199 if(VirtualAlloc((LPVOID)pageAddr, nrpages*PAGE_SIZE, MEM_COMMIT, newProt) == FALSE) {
200 goto fail;
201 }
202 }
203
204// mapMutex.leave();
205 return TRUE;
206fail:
207// mapMutex.leave();
208 return FALSE;
209}
210//******************************************************************************
211//******************************************************************************
212BOOL Win32MemMap::unmapViewOfFile()
213{
214 if(fMapped == FALSE)
215 return FALSE;
216
217 flushView(pMapping, mSize);
218 mapMutex.enter();
219 if(pMapping) {
220 VirtualFree(pMapping, mSize, MEM_RELEASE);
221 }
222 pMapping = NULL;
223 fMapped = FALSE;
224 mapMutex.leave();
225 return TRUE;
226}
227//******************************************************************************
228//******************************************************************************
229LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
230{
231 mapMutex.enter();
232 ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
233 ULONG fAlloc = 0;
234 LPVOID mapview;
235
236 if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
237 goto parmfail;
238 if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
239 goto parmfail;
240 if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
241 goto parmfail;
242
243//TODO: If committed, read file into memory
244#if 0
245 if(mProtFlags & SEC_COMMIT)
246 fAlloc |= MEM_COMMIT;
247 else
248 if(mProtFlags & SEC_RESERVE)
249 fAlloc |= MEM_RESERVE;
250#else
251 fAlloc = MEM_RESERVE;
252#endif
253
254 if(fMapped == FALSE) {//if not mapped, reserve/commit entire view
255 //SvL: Always read/write access or else ReadFile will crash once we
256 // start decommitting pages.
257 // This is most likely an OS/2 bug and doesn't happen in Aurora
258 // when allocating memory with the PAG_ANY bit set. (without this
259 // flag it will also crash)
260 pMapping = VirtualAlloc(0, mSize, fAlloc, PAGE_READWRITE);
261 if(pMapping == NULL) {
262 dprintf(("Win32MemMap::mapFileView: VirtualAlloc %x %x %x failed!", mSize, fAlloc, memFlags));
263 goto fail;
264 }
265 fMapped = TRUE;
266 }
267 mapview = (LPVOID)((ULONG)pMapping + offset);
268 mapMutex.leave();
269 return mapview;
270
271parmfail:
272 dprintf(("Win32MemMap::mapFileView: ERROR_INVALID_PARAMETER"));
273 SetLastError(ERROR_INVALID_PARAMETER);
274fail:
275 mapMutex.leave();
276 return 0;
277}
278//******************************************************************************
279//We determine whether a page has been modified by checking it's protection flags
280//If the write flag is set, this means commitPage had to enable this due to a pagefault
281//(all pages are readonly until the app tries to modify it)
282//
283//TODO: Are apps allowed to change the protection flags of memory mapped pages?
284// I'm assuming they aren't for now.
285//******************************************************************************
286BOOL Win32MemMap::flushView(LPVOID lpvBase, ULONG cbFlush)
287{
288 MEMORY_BASIC_INFORMATION memInfo;
289 ULONG nrpages, nrBytesWritten, offset, size;
290 int i;
291
292// mapMutex.enter();
293 dprintf(("Win32MemMap::flushView: %x %x", lpvBase, cbFlush));
294 if(fMapped == FALSE)
295 goto parmfail;
296
297 if(cbFlush == 0)
298 cbFlush = mSize;
299
300 if(lpvBase < pMapping || (ULONG)lpvBase+cbFlush > (ULONG)pMapping+mSize)
301 goto parmfail;
302
303 if(mProtFlags & PAGE_READONLY)
304 goto parmfail;
305
306 if(hMemFile == -1)
307 goto success; //TODO: Return an error here?
308
309 nrpages = cbFlush/PAGE_SIZE;
310 if(cbFlush & 0xFFF) nrpages++;
311
312 for(i=0;i<nrpages;i++) {
313 if(VirtualQuery((LPSTR)lpvBase+i*PAGE_SIZE, &memInfo, PAGE_SIZE) == 0) {
314 dprintf(("Win32MemMap::flushView: VirtualQuery (%x,%x) failed for %x", lpvBase, cbFlush, (ULONG)lpvBase+i*PAGE_SIZE));
315 goto fail;
316 }
317 //If a page is reserved or write protected, we won't bother flushing it to disk
318 if(memInfo.State & MEM_COMMIT &&
319 memInfo.AllocationProtect & (PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))
320 {//committed and allowed for writing?
321 offset = (ULONG)lpvBase+i*PAGE_SIZE - (ULONG)pMapping;
322 size = PAGE_SIZE;
323 if(offset + size > mSize) {
324 size = mSize - offset;
325 }
326 dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
327
328 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
329 dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
330 goto fail;
331 }
332 if(WriteFile(hMemFile, (LPSTR)lpvBase+i*PAGE_SIZE, size, &nrBytesWritten, NULL) == FALSE) {
333 dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)lpvBase+i*PAGE_SIZE));
334 goto fail;
335 }
336 if(nrBytesWritten != size) {
337 dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)lpvBase+i*PAGE_SIZE));
338 goto fail;
339 }
340 }
341 }
342success:
343// mapMutex.leave();
344 return TRUE;
345parmfail:
346 SetLastError(ERROR_INVALID_PARAMETER);
347// mapMutex.leave();
348 return FALSE;
349fail:
350// mapMutex.leave();
351 return FALSE;
352}
353//******************************************************************************
354//******************************************************************************
355Win32MemMap *Win32MemMap::findMap(LPSTR lpszName)
356{
357 globalmapMutex.enter();
358 Win32MemMap *map = memmaps;
359
360 if(map != NULL) {
361 while(map) {
362 if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName))
363 break;
364 map = map->next;
365 }
366 }
367 globalmapMutex.leave();
368 dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName));
369 return map;
370}
371//******************************************************************************
372//******************************************************************************
373Win32MemMap *Win32MemMap::findMap(ULONG address)
374{
375 globalmapMutex.enter();
376 Win32MemMap *map = memmaps;
377
378 if(map != NULL) {
379 while(map) {
380 if(map->pMapping && (ULONG)map->pMapping <= address &&
381 (ULONG)map->pMapping + map->mSize > address)
382 {
383 break;
384 }
385 map = map->next;
386 }
387 }
388 globalmapMutex.leave();
389 return map;
390}
391//******************************************************************************
392//******************************************************************************
393void Win32MemMap::deleteAll()
394{
395 while(memmaps) {
396 CloseHandle(memmaps->hMemMap);
397 }
398}
399//******************************************************************************
400//******************************************************************************
401Win32MemMap *Win32MemMap::memmaps = NULL;
Note: See TracBrowser for help on using the repository browser.