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

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

VirtualProtect bugfix + some additions to oslib

File size: 12.5 KB
Line 
1/* $Id: mmap.cpp,v 1.15 1999-08-26 15:05:14 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//DosSetMem returns flags with EXECUTE bit set, even though the initial allocation is without this bit set!
193//Also returns access denied when trying to set it back to READONLY
194 VirtualProtect((LPVOID)pageAddr, nrpages*PAGE_SIZE, newProt, &oldProt);
195// if(VirtualProtect((LPVOID)pageAddr, nrpages*PAGE_SIZE, newProt, &oldProt) == FALSE) {
196// goto fail;
197// }
198 }
199 }
200 }
201 else {
202 if(VirtualAlloc((LPVOID)pageAddr, nrpages*PAGE_SIZE, MEM_COMMIT, newProt) == FALSE) {
203 goto fail;
204 }
205 }
206
207// mapMutex.leave();
208 return TRUE;
209fail:
210// mapMutex.leave();
211 return FALSE;
212}
213//******************************************************************************
214//******************************************************************************
215BOOL Win32MemMap::unmapViewOfFile()
216{
217 if(fMapped == FALSE)
218 return FALSE;
219
220 flushView(pMapping, mSize);
221 mapMutex.enter();
222 if(pMapping) {
223 VirtualFree(pMapping, mSize, MEM_RELEASE);
224 }
225 pMapping = NULL;
226 fMapped = FALSE;
227 mapMutex.leave();
228 return TRUE;
229}
230//******************************************************************************
231//******************************************************************************
232LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
233{
234 mapMutex.enter();
235 ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
236 ULONG fAlloc = 0;
237 LPVOID mapview;
238
239 if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
240 goto parmfail;
241 if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
242 goto parmfail;
243 if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
244 goto parmfail;
245
246//TODO: If committed, read file into memory
247#if 0
248 if(mProtFlags & SEC_COMMIT)
249 fAlloc |= MEM_COMMIT;
250 else
251 if(mProtFlags & SEC_RESERVE)
252 fAlloc |= MEM_RESERVE;
253#else
254 fAlloc = MEM_RESERVE;
255#endif
256
257 if(fMapped == FALSE) {//if not mapped, reserve/commit entire view
258 pMapping = VirtualAlloc(0, mSize, fAlloc, memFlags);
259 if(pMapping == NULL) {
260 dprintf(("Win32MemMap::mapFileView: VirtualAlloc %x %x %x failed!", mSize, fAlloc, memFlags));
261 goto fail;
262 }
263 fMapped = TRUE;
264 }
265 mapview = (LPVOID)((ULONG)pMapping + offset);
266 mapMutex.leave();
267 return mapview;
268
269parmfail:
270 dprintf(("Win32MemMap::mapFileView: ERROR_INVALID_PARAMETER"));
271 SetLastError(ERROR_INVALID_PARAMETER);
272fail:
273 mapMutex.leave();
274 return 0;
275}
276//******************************************************************************
277//We determine whether a page has been modified by checking it's protection flags
278//If the write flag is set, this means commitPage had to enable this due to a pagefault
279//(all pages are readonly until the app tries to modify it)
280//
281//TODO: Are apps allowed to change the protection flags of memory mapped pages?
282// I'm assuming they aren't for now.
283//******************************************************************************
284BOOL Win32MemMap::flushView(LPVOID lpvBase, ULONG cbFlush)
285{
286 MEMORY_BASIC_INFORMATION memInfo;
287 ULONG nrpages, nrBytesWritten, offset, size;
288 int i;
289
290// mapMutex.enter();
291 dprintf(("Win32MemMap::flushView: %x %x", lpvBase, cbFlush));
292 if(fMapped == FALSE)
293 goto parmfail;
294
295 if(cbFlush == 0)
296 cbFlush = mSize;
297
298 if(lpvBase < pMapping || (ULONG)lpvBase+cbFlush > (ULONG)pMapping+mSize)
299 goto parmfail;
300
301 if(mProtFlags & PAGE_READONLY)
302 goto parmfail;
303
304 if(hMemFile == -1)
305 goto success; //TODO: Return an error here?
306
307 nrpages = cbFlush/PAGE_SIZE;
308 if(cbFlush & 0xFFF) nrpages++;
309
310 for(i=0;i<nrpages;i++) {
311 if(VirtualQuery((LPSTR)lpvBase+i*PAGE_SIZE, &memInfo, PAGE_SIZE) == 0) {
312 dprintf(("Win32MemMap::flushView: VirtualQuery (%x,%x) failed for %x", lpvBase, cbFlush, (ULONG)lpvBase+i*PAGE_SIZE));
313 goto fail;
314 }
315 //If a page is reserved or write protected, we won't bother flushing it to disk
316 if(memInfo.State & MEM_COMMIT &&
317 memInfo.AllocationProtect & (PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))
318 {//committed and allowed for writing?
319 offset = (ULONG)lpvBase+i*PAGE_SIZE - (ULONG)pMapping;
320 size = PAGE_SIZE;
321 if(offset + size > mSize) {
322 size = mSize - offset;
323 }
324 dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
325
326 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
327 dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
328 goto fail;
329 }
330 if(WriteFile(hMemFile, (LPSTR)lpvBase+i*PAGE_SIZE, size, &nrBytesWritten, NULL) == FALSE) {
331 dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)lpvBase+i*PAGE_SIZE));
332 goto fail;
333 }
334 if(nrBytesWritten != size) {
335 dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)lpvBase+i*PAGE_SIZE));
336 goto fail;
337 }
338 }
339 }
340success:
341// mapMutex.leave();
342 return TRUE;
343parmfail:
344 SetLastError(ERROR_INVALID_PARAMETER);
345// mapMutex.leave();
346 return FALSE;
347fail:
348// mapMutex.leave();
349 return FALSE;
350}
351//******************************************************************************
352//******************************************************************************
353Win32MemMap *Win32MemMap::findMap(LPSTR lpszName)
354{
355 globalmapMutex.enter();
356 Win32MemMap *map = memmaps;
357
358 if(map != NULL) {
359 while(map) {
360 if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName))
361 break;
362 map = map->next;
363 }
364 }
365 globalmapMutex.leave();
366 dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName));
367 return map;
368}
369//******************************************************************************
370//******************************************************************************
371Win32MemMap *Win32MemMap::findMap(ULONG address)
372{
373 globalmapMutex.enter();
374 Win32MemMap *map = memmaps;
375
376 if(map != NULL) {
377 while(map) {
378 if(map->pMapping && (ULONG)map->pMapping <= address &&
379 (ULONG)map->pMapping + map->mSize > address)
380 {
381 break;
382 }
383 map = map->next;
384 }
385 }
386 globalmapMutex.leave();
387 return map;
388}
389//******************************************************************************
390//******************************************************************************
391void Win32MemMap::deleteAll()
392{
393 while(memmaps) {
394 CloseHandle(memmaps->hMemMap);
395 }
396}
397//******************************************************************************
398//******************************************************************************
399Win32MemMap *Win32MemMap::memmaps = NULL;
Note: See TracBrowser for help on using the repository browser.