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

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

Memory mapped file (writes) changes

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