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