1 /**
2 * @namespace biewlib
3 * @file biewlib/sysdep/ia32/win32/mmfio.c
4 * @brief Memory Mapped Files Interface for Win32
5 * @version -
6 * @remark this source file is modified version of OS/2 one.
7 For detail see biewlib/sysdep/ia32/os2/mmfio.c
8 * @note Requires POSIX compatible development system
9 *
10 * @author Nickols_K
11 * @date 02/03/2001
12 * @bug Program can be destroyed if operates with 0 size of file.
13 **/
14 /* for cygwin - remove unnecessary includes */
15 #define _OLE_H
16 #define _OLE2_H
17 #ifdef __DISABLE_MMF
18 #include "biewlib/sysdep/ia16/dos/mmfio.c"
19 #else
20 #ifndef __DISABLE_LOWLEVEL_MMF
21 #include <string.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <windows.h>
25 #include "biewlib/pmalloc.h"
26 #include "biewlib/biewlib.h"
27
28 /*
29 Using standard file-mapping technique of Win32 (CreateFileMapping,
30 MapViewOfFile and so on) is not compatible with program logic of biew.
31 (IMHO it also is incompatible with logic of mremap function from advanced
32 UNIX standards).
33
34 Cause:
35 WinNT 4.0 SDK says:
36 SetEndOfFile.
37 ...
38 Remarks:
39 ...
40 If you called CreateFileMapping to create a file-mapping object for hFile,
41 you must first call UnmapViewOfFile to unmap all views and call CloseHandle
42 to close the file-mapping object before you can call SetEndOfFile.
43
44 But implementation of same logic with using VirtualAlloc - VirtualFree
45 and SetUnhandledExceptionFilter avoids problems described above.
46 */
47
48 /*
49 ** Constants
50 */
51
52 #define MMF_USEDENTRY 0x80000000 /* internal flag */
53 #define MMF_MAX 255 /* maximal number of concurently */
54 /* active MMF areas */
55
56 /*
57 ** Error codes
58 */
59
60 #define ERROR_MMF_TOO_MANY_MMF_OPEN -1
61 #define ERROR_MMF_ZERO_FILE -2
62 #define ERROR_MMF_INVALID_REGION -3
63
64
65 /* Internal structures and constants */
66
67 #define PAG_SIZE 4096
68 #if __WORDSIZE == 32
69 #define PAG_MASK 0xFFFFF000
70 typedef unsigned long ADRSIZE;
71 #else
72 /* Hack: MinGW64 (unlike linux64) assumes sizeof(long)==4 :( */
73 #define PAG_MASK 0xFFFFFFFFFFFFF000
74 typedef unsigned long long ADRSIZE;
75 #endif
76
77 typedef struct
78 {
79 tUInt32 ulFlags;
80 HANDLE fhandle;
81 void * pData;
82 ADRSIZE ulSize;
83 } MMFENTRY;
84
85 typedef MMFENTRY* PMMF;
86
87 /* Static data */
88
89 MMFENTRY mmfTable[MMF_MAX];
90
91 /* Local functions implementation */
92
Locate(void * addr)93 static PMMF __FASTCALL__ Locate(void *addr)
94 {
95 unsigned i;
96 for(i = 0; i < MMF_MAX; i++)
97 {
98 if(mmfTable[i].ulFlags & MMF_USEDENTRY)
99 {
100 if( (ADRSIZE)mmfTable[i].pData <= (ADRSIZE)addr
101 && ((ADRSIZE)mmfTable[i].pData+mmfTable[i].ulSize) >= (ADRSIZE)addr)
102 {
103 return &mmfTable[i];
104 }
105 }
106 }
107 return 0;
108 }
109
LocateFree(void)110 static PMMF __FASTCALL__ LocateFree(void)
111 {
112 unsigned i;
113 for(i = 0; i < MMF_MAX; i++)
114 {
115 if(!(mmfTable[i].ulFlags & MMF_USEDENTRY))
116 {
117 return &mmfTable[i];
118 }
119 }
120 return 0;
121 }
122
apiPageFaultHandler(LPEXCEPTION_POINTERS excpt)123 LONG CALLBACK apiPageFaultHandler(LPEXCEPTION_POINTERS excpt)
124 {
125 MEMORY_BASIC_INFORMATION mbi;
126 ADRSIZE ulTemp;
127 #if __WORDSIZE > 32
128 PEXCEPTION_RECORD64 per=excpt->ExceptionRecord;
129 #else
130 PEXCEPTION_RECORD per=excpt->ExceptionRecord;
131 #endif
132 if(per->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
133 {
134 PMMF pMMF = 0;
135 void * pPage = 0;
136 ULONG ulSize = PAG_SIZE;
137 if(!(pMMF = Locate((void *)per->ExceptionInformation[1])))
138 return EXCEPTION_CONTINUE_SEARCH;
139 pPage = (void *)((ADRSIZE)per->ExceptionInformation[1] & PAG_MASK);
140
141 /* Query affected page flags */
142 if(VirtualQuery(pPage, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) !=
143 sizeof(MEMORY_BASIC_INFORMATION))
144 return EXCEPTION_CONTINUE_SEARCH;
145
146 /*
147 ** There can be three cases:
148 **
149 ** 1. We trying to read page - always OK, commit it
150 ** 2. We trying to write committed page - OK if READ/WRITE mode
151 ** 3. We trying to write uncommitted page - OK if READ/WRITE mode
152 ** but we need to commit it.
153 */
154
155 /* If file was open for read-only access and system is requiring write access
156 we must produce page fault exception */
157 if(per->ExceptionInformation[0] != 0 &&
158 !(pMMF->ulFlags & FO_READWRITE))
159 return EXCEPTION_CONTINUE_SEARCH;
160
161 /* if page not committed, commit it and mark as readonly */
162
163 if(!(mbi.State & MEM_COMMIT))
164 {
165 /* We must temporary enable write access even for read-only pages
166 for reading it into memory */
167 if(VirtualAlloc(pPage, ulSize, MEM_COMMIT, PAGE_READWRITE) != pPage)
168 return EXCEPTION_CONTINUE_SEARCH;
169 ulTemp = (ADRSIZE)pPage - (ADRSIZE)pMMF->pData;
170 /* set position & read page from disk */
171 if(SetFilePointer((HANDLE)pMMF->fhandle,
172 ulTemp,
173 NULL,
174 FILE_BEGIN) == ulTemp)
175 /* Actually ignore errors here */
176 ReadFile((HANDLE)pMMF->fhandle,pPage,ulSize,(DWORD *)&ulTemp,NULL);
177 /* Mark it as read-only to be sure that page is clean. */
178 if(VirtualProtect(pPage, ulSize, PAGE_READONLY,(ULONG *)&ulTemp) == 0)
179 return EXCEPTION_CONTINUE_SEARCH;
180 }
181
182 /* if page already committed, and accessed for writing - mark them writable,
183 for writting it back to the file at close. */
184 if(per->ExceptionInformation[0] != 0)
185 VirtualProtect(pPage, PAG_SIZE, PAGE_READWRITE,(ULONG *)&ulTemp);
186 return EXCEPTION_CONTINUE_EXECUTION;
187 /* exception is fully working. Success exit. */
188 }
189 return EXCEPTION_CONTINUE_SEARCH;
190 }
191
192 extern LPTOP_LEVEL_EXCEPTION_FILTER PrevPageFaultHandler;
193
194 /* Main worker :) */
PageFaultHandler(LPEXCEPTION_POINTERS excpt)195 LONG CALLBACK PageFaultHandler(LPEXCEPTION_POINTERS excpt)
196 {
197 LONG retval;
198 retval = apiPageFaultHandler(excpt);
199 if(retval == EXCEPTION_CONTINUE_SEARCH && PrevPageFaultHandler)
200 return (*PrevPageFaultHandler)(excpt);
201 return retval;
202 }
203
DosUpdateMMF(PMMF pMMF,ADRSIZE length)204 static int __FASTCALL__ DosUpdateMMF(PMMF pMMF,ADRSIZE length)
205 {
206 char * pArea = 0;
207 ADRSIZE ulPos = 0;
208 MEMORY_BASIC_INFORMATION mbi;
209
210 /* locate all regions which needs update, and actually update them */
211
212 for(pArea = (PBYTE)pMMF->pData; ulPos < min(length,pMMF->ulSize); )
213 {
214 /* Query info about range */
215 if(VirtualQuery(pArea, &mbi, sizeof(MEMORY_BASIC_INFORMATION) !=
216 sizeof(MEMORY_BASIC_INFORMATION)))
217 return -1;
218
219 if(mbi.AllocationProtect & PAGE_READWRITE)
220 {
221 /* set pointer */
222 errno = 0;
223 __OsSeek(pMMF->fhandle,(ADRSIZE)pArea - (ADRSIZE)pMMF->pData,SEEKF_START);
224 if(!errno)
225 {
226 ADRSIZE rem = ulPos - pMMF->ulSize;
227 __OsWrite(pMMF->fhandle,pArea,rem >= PAG_SIZE ? PAG_SIZE : rem);
228 if(errno) return errno;
229 VirtualProtect(pArea,PAG_SIZE,PAGE_READONLY,(DWORD *) &rem); /* Mark it as clean */
230 }
231 }
232 ulPos += PAG_SIZE;
233 pArea += PAG_SIZE;
234 }
235
236 return 0;
237 }
238
DosReallocMMF(PMMF pMMF,ADRSIZE new_length)239 static int __FASTCALL__ DosReallocMMF(PMMF pMMF,ADRSIZE new_length)
240 {
241 void * newData = 0;
242 /*
243 Since Win32 does not have VirtualRealloc we must perform this stupid
244 operation. But it help us to avoid rereading information from disk.
245 Anew allocated memory is decommited and will swapped from disk later.
246 For flushing memory back to the disk a program must to use mmfFlush
247 or DosUpdateMMF directly.
248 */
249 newData = VirtualAlloc(NULL, new_length, MEM_RESERVE, PAGE_READWRITE);
250 if(newData)
251 {
252 VirtualFree(pMMF->pData, 0L, MEM_RELEASE);
253 pMMF->pData = newData;
254 pMMF->ulSize = new_length;
255 return 0;
256 }
257 return -1;
258 }
259
260 /* API Implementation */
261
__mmfOpen(const char * fname,int mode)262 mmfHandle __FASTCALL__ __mmfOpen(const char *fname,int mode)
263 {
264 ADRSIZE flength;
265 bhandle_t fhandle;
266 void * pData = 0;
267 PMMF pMMF = 0;
268
269 /* Locate free entry in table */
270
271 pMMF = LocateFree();
272
273 if(!pMMF)
274 {
275 errno = ERROR_MMF_TOO_MANY_MMF_OPEN;
276 return NULL;
277 }
278 /* Open file */
279
280 if((fhandle = __OsOpen(fname,mode)) == NULL_HANDLE) return NULL;
281
282 /* Query file size */
283 if((flength = GetFileSize((HANDLE)fhandle,NULL)) == 0xFFFFFFFFUL)
284 goto exit_func;
285 if(!flength) flength = 1;
286 /*
287 Allocate memory. It is safety here to make read/write access because all
288 pages are decommited (i.e. unaccessible). Actual access mode will be set
289 later in exception handler.
290 */
291 if(!(pData = VirtualAlloc(NULL, flength, MEM_RESERVE, PAGE_READWRITE)))
292 {
293 exit_func:
294 __OsClose(fhandle);
295 return NULL;
296 }
297
298 /* Everything seems ok, fill data structure and return pointer to memory */
299
300 pMMF->ulFlags = mode | MMF_USEDENTRY;
301 pMMF->fhandle = fhandle;
302 pMMF->pData = pData;
303 pMMF->ulSize = flength;
304 return pMMF;
305 }
306
__mmfFlush(mmfHandle mh)307 tBool __FASTCALL__ __mmfFlush(mmfHandle mh)
308 {
309 PMMF mrec = (PMMF)mh;
310 return DosUpdateMMF(mrec,mrec->ulSize) ? True : False;
311 }
312
__mmfSync(mmfHandle mh)313 mmfHandle __FASTCALL__ __mmfSync(mmfHandle mh)
314 {
315 PMMF mrec = (PMMF)mh;
316 long length;
317 length = __FileLength(mrec->fhandle);
318 DosUpdateMMF(mrec,min((ADRSIZE)length,mrec->ulSize));
319 if(length!=mrec->ulSize) {
320 if(DosReallocMMF(mrec,length))
321 {
322 VirtualFree(mrec->pData, 0L, MEM_RELEASE);
323 __OsClose(mrec->fhandle);
324 mrec = NULL;
325 }
326 }
327 return mrec;
328 }
329
__mmfProtect(mmfHandle mh,int flags)330 tBool __FASTCALL__ __mmfProtect(mmfHandle mh,int flags)
331 {
332 PMMF mrec = (PMMF)mh;
333 mrec->ulFlags = flags | MMF_USEDENTRY;
334 return True;
335 }
336
__mmfResize(mmfHandle mh,long size)337 tBool __FASTCALL__ __mmfResize(mmfHandle mh,long size)
338 {
339 PMMF mrec = (PMMF)mh;
340 ADRSIZE old_length;
341 tBool can_continue = False;
342 old_length = mrec->ulSize;
343 DosUpdateMMF(mrec,min((ADRSIZE)size,mrec->ulSize));
344 if(mrec->ulSize > (ADRSIZE)size) /* truncate */
345 {
346 if(!DosReallocMMF(mrec,size)) can_continue = True;
347 if(can_continue)
348 can_continue = __OsChSize(mrec->fhandle,size) != -1 ? True : False;
349 }
350 else /* expand */
351 {
352 if(__OsChSize(mrec->fhandle,size) != -1) can_continue = True;
353 if(can_continue)
354 can_continue = !DosReallocMMF(mrec,size) ? True : False;
355 }
356 if(can_continue) return True;
357 else /* Attempt to unroll transaction back */
358 __OsChSize(mrec->fhandle,old_length);
359 return False;
360 }
361
__mmfClose(mmfHandle mh)362 void __FASTCALL__ __mmfClose(mmfHandle mh)
363 {
364 PMMF mrec = (PMMF)mh;
365
366 /* No automatic update when area freed */
367
368 if((mrec->ulFlags & FO_READWRITE) || (mrec->ulFlags & FO_WRITEONLY))
369 DosUpdateMMF(mrec->pData,mrec->ulSize);
370
371 CloseHandle((HANDLE)mrec->fhandle);
372 VirtualFree(mrec->pData, 0L, MEM_RELEASE);
373
374 mrec->ulFlags = 0;
375 mrec->fhandle = NULL_HANDLE;
376 mrec->pData = 0;
377 mrec->ulSize = 0;
378 }
379
__mmfAddress(mmfHandle mh)380 void * __FASTCALL__ __mmfAddress(mmfHandle mh)
381 {
382 return ((PMMF)mh)->pData;
383 }
384
__mmfSize(mmfHandle mh)385 long __FASTCALL__ __mmfSize(mmfHandle mh)
386 {
387 return ((PMMF)mh)->ulSize;
388 }
389
__mmfIsWorkable(void)390 tBool __FASTCALL__ __mmfIsWorkable( void ) { return True; }
391 #else
392 /*
393 WARNING! This implementation of program logic is not fully compatible
394 with win32 MMF.
395 WinNT 4.0 SDK says:
396 SetEndOfFile.
397 ...
398 Remarks:
399 ...
400 If you called CreateFileMapping to create a file-mapping object for hFile,
401 you must first call UnmapViewOfFile to unmap all views and call CloseHandle
402 to close the file-mapping object before you can call SetEndOfFile.
403 */
404 #include <windows.h>
405 #include "biewlib/pmalloc.h"
406 #include "biewlib/biewlib.h"
407
408 #ifndef _MSC_VER
409 #error "You've defined __DISABLE_LOWLEVEL_MMF. You can comment out this message, but in this case resulting code will partially workable."
410 #endif
411
412 struct mmfRecord
413 {
414 void * addr;
415 long length;
416 bhandle_t fhandle;
417 int mode;
418 HANDLE fmapping;
419 };
420
mk_prot(int mode)421 static int __FASTCALL__ mk_prot(int mode)
422 {
423 int pflg;
424 pflg = PAGE_READONLY;
425 if((mode & FO_WRITEONLY) || (mode & FO_READWRITE)) pflg = PAGE_READWRITE;
426 pflg |= SEC_COMMIT | SEC_NOCACHE;
427 if(mode & SO_PRIVATE) pflg |= PAGE_WRITECOPY;
428 return pflg;
429 }
430
mk_access(int mode)431 static int __FASTCALL__ mk_access(int mode)
432 {
433 int pflg;
434 pflg = FILE_MAP_READ;
435 if((mode & FO_WRITEONLY) || (mode & FO_READWRITE)) pflg = FILE_MAP_WRITE;
436 if(mode & SO_PRIVATE) pflg |= FILE_MAP_COPY;
437 return pflg;
438 }
439
__mmfOpen(const char * fname,int mode)440 mmfHandle __FASTCALL__ __mmfOpen(const char *fname,int mode)
441 {
442 struct mmfRecord *mret;
443 __fileoff_t length;
444 bhandle_t fhandle;
445 fhandle = __OsOpen(fname,mode);
446 if(fhandle != NULL_HANDLE)
447 {
448 length = __FileLength(fhandle);
449 if(length <= PTRDIFF_MAX)
450 {
451 mret = PMalloc(sizeof(struct mmfRecord));
452 if(mret)
453 {
454 HANDLE fmapping;
455 void *addr;
456 fmapping = CreateFileMapping((HANDLE)fhandle,NULL,mk_prot(mode),0L,length,NULL);
457 if(fmapping)
458 {
459 addr = MapViewOfFile(fmapping,mk_access(mode),0L,0L,length);
460 if(addr)
461 {
462 mret->fhandle = fhandle;
463 mret->fmapping= fmapping;
464 mret->addr = addr;
465 mret->length = length;
466 mret->mode = mode;
467 return mret;
468 }
469 CloseHandle(fmapping);
470 }
471 }
472 PFree(mret);
473 }
474 __OsClose(fhandle);
475 }
476 return NULL;
477 }
478
__mmfFlush(mmfHandle mh)479 tBool __FASTCALL__ __mmfFlush(mmfHandle mh)
480 {
481 struct mmfRecord *mrec = (struct mmfRecord *)mh;
482 return FlushViewOfFile(mrec->addr,mrec->length) ? True : False;
483 }
484
__mmfSync(mmfHandle mh)485 mmfHandle __FASTCALL__ __mmfSync(mmfHandle mh)
486 {
487 /** @bug This implementation does not change size of file.
488 Sorry! It is not possible under win32 without changing logic of
489 program.
490 */
491 struct mmfRecord *mrec = (struct mmfRecord *)mh;
492 long length;
493 length = __FileLength(mrec->fhandle);
494 FlushViewOfFile(mrec->addr,min(mrec->length,length));
495 UnmapViewOfFile(mrec->addr); /* It restores size of truncated file */
496 CloseHandle(mrec->fmapping);
497 if(length < mrec->length) __OsChSize(mrec->fhandle,length);
498 mrec->length = __FileLength(mrec->fhandle);
499 mrec->fmapping = CreateFileMapping((HANDLE)mrec->fhandle,NULL,mk_prot(mrec->mode),0L,mrec->length,NULL);
500 if(mrec->fmapping)
501 {
502 if((mrec->addr = MapViewOfFile(mrec->fmapping,mk_access(mrec->mode),0L,0L,mrec->length)) != NULL)
503 return mh;
504 CloseHandle(mrec->fmapping);
505 }
506 __OsClose(mrec->fhandle);
507 PFree(mrec);
508 return NULL;
509 }
510
__mmfProtect(mmfHandle mh,int flags)511 tBool __FASTCALL__ __mmfProtect(mmfHandle mh,int flags)
512 {
513 DWORD oldProt;
514 struct mmfRecord *mrec = (struct mmfRecord *)mh;
515 mrec->mode = flags;
516 return VirtualProtect(mrec->addr,mrec->length,mk_prot(flags),&oldProt) ? True : False;
517 }
518
__mmfResize(mmfHandle mh,long size)519 tBool __FASTCALL__ __mmfResize(mmfHandle mh,long size)
520 {
521 /** @bug This implementation does not change size of file.
522 Sorry! It is not possible under win32 without changing logic of
523 program.
524 */
525 struct mmfRecord *mrec = (struct mmfRecord *)mh;
526 UnmapViewOfFile(mrec->addr);
527 CloseHandle(mrec->fmapping);
528 __OsChSize(mrec->fhandle,size);
529 mrec->length = __FileLength(mrec->fhandle);
530 mrec->fmapping = CreateFileMapping((HANDLE)mrec->fhandle,NULL,mk_prot(mrec->mode),0L,mrec->length,NULL);
531 if(mrec->fmapping)
532 {
533 if((mrec->addr = MapViewOfFile(mrec->fmapping,mk_access(mrec->mode),0L,0L,mrec->length)) != NULL)
534 return True;
535 CloseHandle(mrec->fmapping);
536 }
537 __OsClose(mrec->fhandle);
538 PFree(mrec);
539 return False;
540 }
541
__mmfClose(mmfHandle mh)542 void __FASTCALL__ __mmfClose(mmfHandle mh)
543 {
544 struct mmfRecord *mrec = (struct mmfRecord *)mh;
545 UnmapViewOfFile(mrec->addr);
546 CloseHandle(mrec->fmapping);
547 __OsClose(mrec->fhandle);
548 PFree(mrec);
549 }
550
__mmfAddress(mmfHandle mh)551 void * __FASTCALL__ __mmfAddress(mmfHandle mh)
552 {
553 return ((struct mmfRecord *)mh)->addr;
554 }
555
__mmfSize(mmfHandle mh)556 long __FASTCALL__ __mmfSize(mmfHandle mh)
557 {
558 return ((struct mmfRecord *)mh)->length;
559 }
560
__mmfIsWorkable(void)561 tBool __FASTCALL__ __mmfIsWorkable( void ) { return True; }
562 #endif
563 #endif
564