Use the same color conversion algorithm as the DIB code (and Windows),
[wine] / memory / virtual.c
1 /*
2  * Win32 virtual memory functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  */
6
7 #include "wine/port.h"
8
9 #include <assert.h>
10 #include <errno.h>
11 #ifdef HAVE_SYS_ERRNO_H
12 #include <sys/errno.h>
13 #endif
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/types.h>
20 #ifdef HAVE_SYS_MMAN_H
21 #include <sys/mman.h>
22 #endif
23 #include "winnls.h"
24 #include "winbase.h"
25 #include "wine/exception.h"
26 #include "wine/unicode.h"
27 #include "wine/library.h"
28 #include "winerror.h"
29 #include "file.h"
30 #include "global.h"
31 #include "wine/server.h"
32 #include "debugtools.h"
33
34 DEFAULT_DEBUG_CHANNEL(virtual);
35 DECLARE_DEBUG_CHANNEL(module);
36
37 #ifndef MS_SYNC
38 #define MS_SYNC 0
39 #endif
40
41 /* File view */
42 typedef struct _FV
43 {
44     struct _FV   *next;        /* Next view */
45     struct _FV   *prev;        /* Prev view */
46     void         *base;        /* Base address */
47     UINT          size;        /* Size in bytes */
48     UINT          flags;       /* Allocation flags */
49     HANDLE        mapping;     /* Handle to the file mapping */
50     HANDLERPROC   handlerProc; /* Fault handler */
51     LPVOID        handlerArg;  /* Fault handler argument */
52     BYTE          protect;     /* Protection for all pages at allocation time */
53     BYTE          prot[1];     /* Protection byte for each page */
54 } FILE_VIEW;
55
56 /* Per-view flags */
57 #define VFLAG_SYSTEM     0x01
58 #define VFLAG_VALLOC     0x02  /* allocated by VirtualAlloc */
59
60 /* Conversion from VPROT_* to Win32 flags */
61 static const BYTE VIRTUAL_Win32Flags[16] =
62 {
63     PAGE_NOACCESS,              /* 0 */
64     PAGE_READONLY,              /* READ */
65     PAGE_READWRITE,             /* WRITE */
66     PAGE_READWRITE,             /* READ | WRITE */
67     PAGE_EXECUTE,               /* EXEC */
68     PAGE_EXECUTE_READ,          /* READ | EXEC */
69     PAGE_EXECUTE_READWRITE,     /* WRITE | EXEC */
70     PAGE_EXECUTE_READWRITE,     /* READ | WRITE | EXEC */
71     PAGE_WRITECOPY,             /* WRITECOPY */
72     PAGE_WRITECOPY,             /* READ | WRITECOPY */
73     PAGE_WRITECOPY,             /* WRITE | WRITECOPY */
74     PAGE_WRITECOPY,             /* READ | WRITE | WRITECOPY */
75     PAGE_EXECUTE_WRITECOPY,     /* EXEC | WRITECOPY */
76     PAGE_EXECUTE_WRITECOPY,     /* READ | EXEC | WRITECOPY */
77     PAGE_EXECUTE_WRITECOPY,     /* WRITE | EXEC | WRITECOPY */
78     PAGE_EXECUTE_WRITECOPY      /* READ | WRITE | EXEC | WRITECOPY */
79 };
80
81
82 static FILE_VIEW *VIRTUAL_FirstView;
83 static CRITICAL_SECTION csVirtual = CRITICAL_SECTION_INIT("csVirtual");
84
85 #ifdef __i386__
86 /* These are always the same on an i386, and it will be faster this way */
87 # define page_mask  0xfff
88 # define page_shift 12
89 # define page_size  0x1000
90 #else
91 static UINT page_shift;
92 static UINT page_mask;
93 static UINT page_size;
94 #endif  /* __i386__ */
95 #define granularity_mask 0xffff  /* Allocation granularity (usually 64k) */
96
97 #define ROUND_ADDR(addr,mask) \
98    ((void *)((UINT_PTR)(addr) & ~(mask)))
99
100 #define ROUND_SIZE(addr,size) \
101    (((UINT)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
102
103 #define VIRTUAL_DEBUG_DUMP_VIEW(view) \
104    if (!TRACE_ON(virtual)); else VIRTUAL_DumpView(view)
105
106 static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size, DWORD offset_low,
107                             DWORD offset_high, int prot, int flags );
108
109 /* filter for page-fault exceptions */
110 static WINE_EXCEPTION_FILTER(page_fault)
111 {
112     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
113         return EXCEPTION_EXECUTE_HANDLER;
114     return EXCEPTION_CONTINUE_SEARCH;
115 }
116
117 /***********************************************************************
118  *           VIRTUAL_GetProtStr
119  */
120 static const char *VIRTUAL_GetProtStr( BYTE prot )
121 {
122     static char buffer[6];
123     buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-';
124     buffer[1] = (prot & VPROT_GUARD) ? 'g' : '-';
125     buffer[2] = (prot & VPROT_READ) ? 'r' : '-';
126     buffer[3] = (prot & VPROT_WRITE) ?
127                     ((prot & VPROT_WRITECOPY) ? 'w' : 'W') : '-';
128     buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-';
129     buffer[5] = 0;
130     return buffer;
131 }
132
133
134 /***********************************************************************
135  *           VIRTUAL_DumpView
136  */
137 static void VIRTUAL_DumpView( FILE_VIEW *view )
138 {
139     UINT i, count;
140     char *addr = view->base;
141     BYTE prot = view->prot[0];
142
143     DPRINTF( "View: %p - %p", addr, addr + view->size - 1 );
144     if (view->flags & VFLAG_SYSTEM)
145         DPRINTF( " (system)\n" );
146     else if (view->flags & VFLAG_VALLOC)
147         DPRINTF( " (valloc)\n" );
148     else if (view->mapping)
149         DPRINTF( " %d\n", view->mapping );
150     else
151         DPRINTF( " (anonymous)\n");
152
153     for (count = i = 1; i < view->size >> page_shift; i++, count++)
154     {
155         if (view->prot[i] == prot) continue;
156         DPRINTF( "      %p - %p %s\n",
157                  addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
158         addr += (count << page_shift);
159         prot = view->prot[i];
160         count = 0;
161     }
162     if (count)
163         DPRINTF( "      %p - %p %s\n",
164                  addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
165 }
166
167
168 /***********************************************************************
169  *           VIRTUAL_Dump
170  */
171 void VIRTUAL_Dump(void)
172 {
173     FILE_VIEW *view;
174     DPRINTF( "\nDump of all virtual memory views:\n\n" );
175     EnterCriticalSection(&csVirtual);
176     view = VIRTUAL_FirstView;
177     while (view)
178     {
179         VIRTUAL_DumpView( view );
180         view = view->next;
181     }
182     LeaveCriticalSection(&csVirtual);
183 }
184
185
186 /***********************************************************************
187  *           VIRTUAL_FindView
188  *
189  * Find the view containing a given address.
190  *
191  * RETURNS
192  *      View: Success
193  *      NULL: Failure
194  */
195 static FILE_VIEW *VIRTUAL_FindView( const void *addr ) /* [in] Address */
196 {
197     FILE_VIEW *view;
198
199     EnterCriticalSection(&csVirtual);
200     view = VIRTUAL_FirstView;
201     while (view)
202     {
203         if (view->base > addr)
204         {
205             view = NULL;
206             break;
207         }
208         if (view->base + view->size > addr) break;
209         view = view->next;
210     }
211     LeaveCriticalSection(&csVirtual);
212     return view;
213 }
214
215
216 /***********************************************************************
217  *           VIRTUAL_CreateView
218  *
219  * Create a new view and add it in the linked list.
220  */
221 static FILE_VIEW *VIRTUAL_CreateView( void *base, UINT size, UINT flags,
222                                       BYTE vprot, HANDLE mapping )
223 {
224     FILE_VIEW *view, *prev;
225
226     /* Create the view structure */
227
228     assert( !((unsigned int)base & page_mask) );
229     assert( !(size & page_mask) );
230     size >>= page_shift;
231     if (!(view = (FILE_VIEW *)malloc( sizeof(*view) + size - 1 ))) return NULL;
232     view->base    = base;
233     view->size    = size << page_shift;
234     view->flags   = flags;
235     view->mapping = mapping;
236     view->protect = vprot;
237     view->handlerProc = NULL;
238     memset( view->prot, vprot, size );
239
240     /* Duplicate the mapping handle */
241
242     if (view->mapping &&
243         !DuplicateHandle( GetCurrentProcess(), view->mapping,
244                           GetCurrentProcess(), &view->mapping,
245                           0, FALSE, DUPLICATE_SAME_ACCESS ))
246     {
247         free( view );
248         return NULL;
249     }
250
251     /* Insert it in the linked list */
252
253     EnterCriticalSection(&csVirtual);
254     if (!VIRTUAL_FirstView || (VIRTUAL_FirstView->base > base))
255     {
256         view->next = VIRTUAL_FirstView;
257         view->prev = NULL;
258         if (view->next) view->next->prev = view;
259         VIRTUAL_FirstView = view;
260     }
261     else
262     {
263         prev = VIRTUAL_FirstView;
264         while (prev->next && (prev->next->base < base)) prev = prev->next;
265         view->next = prev->next;
266         view->prev = prev;
267         if (view->next) view->next->prev = view;
268         prev->next  = view;
269     }
270     LeaveCriticalSection(&csVirtual);
271     VIRTUAL_DEBUG_DUMP_VIEW( view );
272     return view;
273 }
274
275
276 /***********************************************************************
277  *           VIRTUAL_DeleteView
278  * Deletes a view.
279  *
280  * RETURNS
281  *      None
282  */
283 static void VIRTUAL_DeleteView(
284             FILE_VIEW *view /* [in] View */
285 ) {
286     if (!(view->flags & VFLAG_SYSTEM))
287         munmap( (void *)view->base, view->size );
288     EnterCriticalSection(&csVirtual);
289     if (view->next) view->next->prev = view->prev;
290     if (view->prev) view->prev->next = view->next;
291     else VIRTUAL_FirstView = view->next;
292     LeaveCriticalSection(&csVirtual);
293     if (view->mapping) NtClose( view->mapping );
294     free( view );
295 }
296
297
298 /***********************************************************************
299  *           VIRTUAL_GetUnixProt
300  *
301  * Convert page protections to protection for mmap/mprotect.
302  */
303 static int VIRTUAL_GetUnixProt( BYTE vprot )
304 {
305     int prot = 0;
306     if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD))
307     {
308         if (vprot & VPROT_READ) prot |= PROT_READ;
309         if (vprot & VPROT_WRITE) prot |= PROT_WRITE;
310         if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE;
311         if (vprot & VPROT_EXEC) prot |= PROT_EXEC;
312     }
313     return prot;
314 }
315
316
317 /***********************************************************************
318  *           VIRTUAL_GetWin32Prot
319  *
320  * Convert page protections to Win32 flags.
321  *
322  * RETURNS
323  *      None
324  */
325 static void VIRTUAL_GetWin32Prot(
326             BYTE vprot,     /* [in] Page protection flags */
327             DWORD *protect, /* [out] Location to store Win32 protection flags */
328             DWORD *state    /* [out] Location to store mem state flag */
329 ) {
330     if (protect) {
331         *protect = VIRTUAL_Win32Flags[vprot & 0x0f];
332 /*      if (vprot & VPROT_GUARD) *protect |= PAGE_GUARD;*/
333         if (vprot & VPROT_NOCACHE) *protect |= PAGE_NOCACHE;
334
335         if (vprot & VPROT_GUARD) *protect = PAGE_NOACCESS;
336     }
337
338     if (state) *state = (vprot & VPROT_COMMITTED) ? MEM_COMMIT : MEM_RESERVE;
339 }
340
341
342 /***********************************************************************
343  *           VIRTUAL_GetProt
344  *
345  * Build page protections from Win32 flags.
346  *
347  * RETURNS
348  *      Value of page protection flags
349  */
350 static BYTE VIRTUAL_GetProt(
351             DWORD protect  /* [in] Win32 protection flags */
352 ) {
353     BYTE vprot;
354
355     switch(protect & 0xff)
356     {
357     case PAGE_READONLY:
358         vprot = VPROT_READ;
359         break;
360     case PAGE_READWRITE:
361         vprot = VPROT_READ | VPROT_WRITE;
362         break;
363     case PAGE_WRITECOPY:
364         /* MSDN CreateFileMapping() states that if PAGE_WRITECOPY is given,
365          * that the hFile must have been opened with GENERIC_READ and
366          * GENERIC_WRITE access.  This is WRONG as tests show that you
367          * only need GENERIC_READ access (at least for Win9x,
368          * FIXME: what about NT?).  Thus, we don't put VPROT_WRITE in
369          * PAGE_WRITECOPY and PAGE_EXECUTE_WRITECOPY.
370          */
371         vprot = VPROT_READ | VPROT_WRITECOPY;
372         break;
373     case PAGE_EXECUTE:
374         vprot = VPROT_EXEC;
375         break;
376     case PAGE_EXECUTE_READ:
377         vprot = VPROT_EXEC | VPROT_READ;
378         break;
379     case PAGE_EXECUTE_READWRITE:
380         vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITE;
381         break;
382     case PAGE_EXECUTE_WRITECOPY:
383         /* See comment for PAGE_WRITECOPY above */
384         vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITECOPY;
385         break;
386     case PAGE_NOACCESS:
387     default:
388         vprot = 0;
389         break;
390     }
391     if (protect & PAGE_GUARD) vprot |= VPROT_GUARD;
392     if (protect & PAGE_NOCACHE) vprot |= VPROT_NOCACHE;
393     return vprot;
394 }
395
396
397 /***********************************************************************
398  *           VIRTUAL_SetProt
399  *
400  * Change the protection of a range of pages.
401  *
402  * RETURNS
403  *      TRUE: Success
404  *      FALSE: Failure
405  */
406 static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
407                              void *base,      /* [in] Starting address */
408                              UINT size,       /* [in] Size in bytes */
409                              BYTE vprot )     /* [in] Protections to use */
410 {
411     TRACE("%p-%p %s\n",
412           base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) );
413
414     if (mprotect( base, size, VIRTUAL_GetUnixProt(vprot) ))
415         return FALSE;  /* FIXME: last error */
416
417     memset( view->prot + (((char *)base - (char *)view->base) >> page_shift),
418             vprot, size >> page_shift );
419     VIRTUAL_DEBUG_DUMP_VIEW( view );
420     return TRUE;
421 }
422
423
424 /***********************************************************************
425  *           anon_mmap_aligned
426  *
427  * Create an anonymous mapping aligned to the allocation granularity.
428  */
429 static void *anon_mmap_aligned( void *base, unsigned int size, int prot, int flags )
430 {
431     void *ptr;
432     unsigned int view_size = size + (base ? 0 : granularity_mask + 1);
433
434     if ((ptr = wine_anon_mmap( base, view_size, prot, flags )) == (void *)-1)
435     {
436         /* KB: Q125713, 25-SEP-1995, "Common File Mapping Problems and
437          * Platform Differences": 
438          * Windows NT: ERROR_INVALID_PARAMETER
439          * Windows 95: ERROR_INVALID_ADDRESS.
440          */
441         if (errno == ENOMEM) SetLastError( ERROR_OUTOFMEMORY );
442         else
443         {
444             if (GetVersion() & 0x80000000)  /* win95 */
445                 SetLastError( ERROR_INVALID_ADDRESS );
446             else
447                 SetLastError( ERROR_INVALID_PARAMETER );
448         }
449         return ptr;
450     }
451
452     if (!base)
453     {
454         /* Release the extra memory while keeping the range
455          * starting on the granularity boundary. */
456         if ((unsigned int)ptr & granularity_mask)
457         {
458             unsigned int extra = granularity_mask + 1 - ((unsigned int)ptr & granularity_mask);
459             munmap( ptr, extra );
460             ptr = (char *)ptr + extra;
461             view_size -= extra;
462         }
463         if (view_size > size)
464             munmap( (char *)ptr + size, view_size - size );
465     }
466     else if (ptr != base)
467     {
468         /* We couldn't get the address we wanted */
469         munmap( ptr, view_size );
470         SetLastError( ERROR_INVALID_ADDRESS );
471         ptr = (void *)-1;
472     }
473     return ptr;
474 }
475
476
477 /***********************************************************************
478  *           map_image
479  *
480  * Map an executable (PE format) image into memory.
481  */
482 static LPVOID map_image( HANDLE hmapping, int fd, char *base, DWORD total_size,
483                          DWORD header_size, HANDLE shared_file, DWORD shared_size )
484 {
485     IMAGE_DOS_HEADER *dos;
486     IMAGE_NT_HEADERS *nt;
487     IMAGE_SECTION_HEADER *sec;
488     int i, pos;
489     DWORD err = GetLastError();
490     FILE_VIEW *view = NULL;
491     char *ptr;
492     int shared_fd = -1;
493
494     SetLastError( ERROR_BAD_EXE_FORMAT );  /* generic error */
495
496     /* zero-map the whole range */
497
498     if ((ptr = wine_anon_mmap( base, total_size,
499                              PROT_READ | PROT_WRITE | PROT_EXEC, 0 )) == (char *)-1)
500     {
501         ptr = wine_anon_mmap( NULL, total_size,
502                             PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
503         if (ptr == (char *)-1)
504         {
505             ERR_(module)("Not enough memory for module (%ld bytes)\n", total_size);
506             goto error;
507         }
508     }
509     TRACE_(module)( "mapped PE file at %p-%p\n", ptr, ptr + total_size );
510
511     if (!(view = VIRTUAL_CreateView( ptr, total_size, 0,
512                                      VPROT_COMMITTED|VPROT_READ|VPROT_WRITE|VPROT_WRITECOPY,
513                                      hmapping )))
514     {
515         munmap( ptr, total_size );
516         SetLastError( ERROR_OUTOFMEMORY );
517         goto error;
518     }
519
520     /* map the header */
521
522     if (VIRTUAL_mmap( fd, ptr, header_size, 0, 0, PROT_READ | PROT_WRITE,
523                       MAP_PRIVATE | MAP_FIXED ) == (char *)-1) goto error;
524     dos = (IMAGE_DOS_HEADER *)ptr;
525     nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
526     if ((char *)(nt + 1) > ptr + header_size) goto error;
527
528     sec = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader);
529     if ((char *)(sec + nt->FileHeader.NumberOfSections) > ptr + header_size) goto error;
530
531     /* check the architecture */
532
533     if (nt->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
534     {
535         MESSAGE("Trying to load PE image for unsupported architecture (");
536         switch (nt->FileHeader.Machine)
537         {
538         case IMAGE_FILE_MACHINE_UNKNOWN: MESSAGE("Unknown"); break;
539         case IMAGE_FILE_MACHINE_I860:    MESSAGE("I860"); break;
540         case IMAGE_FILE_MACHINE_R3000:   MESSAGE("R3000"); break;
541         case IMAGE_FILE_MACHINE_R4000:   MESSAGE("R4000"); break;
542         case IMAGE_FILE_MACHINE_R10000:  MESSAGE("R10000"); break;
543         case IMAGE_FILE_MACHINE_ALPHA:   MESSAGE("Alpha"); break;
544         case IMAGE_FILE_MACHINE_POWERPC: MESSAGE("PowerPC"); break;
545         default: MESSAGE("Unknown-%04x", nt->FileHeader.Machine); break;
546         }
547         MESSAGE(")\n");
548         goto error;
549     }
550     
551     /* retrieve the shared sections file */
552
553     if (shared_size)
554     {
555         if ((shared_fd = FILE_GetUnixHandle( shared_file, GENERIC_READ )) == -1) goto error;
556         CloseHandle( shared_file );  /* we no longer need it */
557         shared_file = 0;
558     }
559
560     /* map all the sections */
561
562     for (i = pos = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
563     {
564         DWORD size;
565
566         /* a few sanity checks */
567         size = sec->VirtualAddress + ROUND_SIZE( sec->VirtualAddress, sec->Misc.VirtualSize );
568         if (sec->VirtualAddress > total_size || size > total_size || size < sec->VirtualAddress)
569         {
570             ERR_(module)( "Section %.8s too large (%lx+%lx/%lx)\n",
571                           sec->Name, sec->VirtualAddress, sec->Misc.VirtualSize, total_size );
572             goto error;
573         }
574
575         if ((sec->Characteristics & IMAGE_SCN_MEM_SHARED) &&
576             (sec->Characteristics & IMAGE_SCN_MEM_WRITE))
577         {
578             size = ROUND_SIZE( 0, sec->Misc.VirtualSize );
579             TRACE_(module)( "mapping shared section %.8s at %p off %lx (%x) size %lx (%lx) flags %lx\n",
580                           sec->Name, ptr + sec->VirtualAddress,
581                           sec->PointerToRawData, pos, sec->SizeOfRawData,
582                           size, sec->Characteristics );
583             if (VIRTUAL_mmap( shared_fd, ptr + sec->VirtualAddress, size,
584                               pos, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
585                               MAP_SHARED|MAP_FIXED ) == (void *)-1)
586             {
587                 ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
588                 goto error;
589             }
590             pos += size;
591             continue;
592         }
593
594         if (sec->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) continue;
595         if (!sec->PointerToRawData || !sec->SizeOfRawData) continue;
596
597         TRACE_(module)( "mapping section %.8s at %p off %lx size %lx flags %lx\n",
598                         sec->Name, ptr + sec->VirtualAddress,
599                         sec->PointerToRawData, sec->SizeOfRawData,
600                         sec->Characteristics );
601
602         /* Note: if the section is not aligned properly VIRTUAL_mmap will magically
603          *       fall back to read(), so we don't need to check anything here.
604          */
605         if (VIRTUAL_mmap( fd, ptr + sec->VirtualAddress, sec->SizeOfRawData,
606                           sec->PointerToRawData, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
607                           MAP_PRIVATE | MAP_FIXED ) == (void *)-1)
608         {
609             ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
610             goto error;
611         }
612
613         if ((sec->SizeOfRawData < sec->Misc.VirtualSize) && (sec->SizeOfRawData & page_mask))
614         {
615             DWORD end = ROUND_SIZE( 0, sec->SizeOfRawData );
616             if (end > sec->Misc.VirtualSize) end = sec->Misc.VirtualSize;
617             TRACE_(module)("clearing %p - %p\n",
618                            ptr + sec->VirtualAddress + sec->SizeOfRawData,
619                            ptr + sec->VirtualAddress + end );
620             memset( ptr + sec->VirtualAddress + sec->SizeOfRawData, 0,
621                     end - sec->SizeOfRawData );
622         }
623     }
624
625     SetLastError( err );  /* restore last error */
626     close( fd );
627     if (shared_fd != -1) close( shared_fd );
628     return ptr;
629
630  error:
631     if (view) VIRTUAL_DeleteView( view );
632     close( fd );
633     if (shared_fd != -1) close( shared_fd );
634     if (shared_file) CloseHandle( shared_file );
635     return NULL;
636 }
637
638
639 /***********************************************************************
640  *           VIRTUAL_Init
641  */
642 #ifndef page_mask
643 DECL_GLOBAL_CONSTRUCTOR(VIRTUAL_Init)
644 {
645     page_size = getpagesize();
646     page_mask = page_size - 1;
647     /* Make sure we have a power of 2 */
648     assert( !(page_size & page_mask) );
649     page_shift = 0;
650     while ((1 << page_shift) != page_size) page_shift++;
651 }
652 #endif  /* page_mask */
653
654
655 /***********************************************************************
656  *           VIRTUAL_SetFaultHandler
657  */
658 BOOL VIRTUAL_SetFaultHandler( LPCVOID addr, HANDLERPROC proc, LPVOID arg )
659 {
660     FILE_VIEW *view;
661
662     if (!(view = VIRTUAL_FindView( addr ))) return FALSE;
663     view->handlerProc = proc;
664     view->handlerArg  = arg;
665     return TRUE;
666 }
667
668 /***********************************************************************
669  *           VIRTUAL_HandleFault
670  */
671 DWORD VIRTUAL_HandleFault( LPCVOID addr )
672 {
673     FILE_VIEW *view = VIRTUAL_FindView( addr );
674     DWORD ret = EXCEPTION_ACCESS_VIOLATION;
675
676     if (view)
677     {
678         if (view->handlerProc)
679         {
680             if (view->handlerProc(view->handlerArg, addr)) ret = 0;  /* handled */
681         }
682         else
683         {
684             BYTE vprot = view->prot[((char *)addr - (char *)view->base) >> page_shift];
685             void *page = (void *)((UINT_PTR)addr & ~page_mask);
686             char *stack = (char *)NtCurrentTeb()->stack_base + SIGNAL_STACK_SIZE + page_mask + 1;
687             if (vprot & VPROT_GUARD)
688             {
689                 VIRTUAL_SetProt( view, page, page_mask + 1, vprot & ~VPROT_GUARD );
690                 ret = STATUS_GUARD_PAGE_VIOLATION;
691             }
692             /* is it inside the stack guard pages? */
693             if (((char *)addr >= stack) && ((char *)addr < stack + 2*(page_mask+1)))
694                 ret = STATUS_STACK_OVERFLOW;
695         }
696     }
697     return ret;
698 }
699
700
701
702 /***********************************************************************
703  *           unaligned_mmap
704  *
705  * Linux kernels before 2.4.x can support non page-aligned offsets, as
706  * long as the offset is aligned to the filesystem block size. This is
707  * a big performance gain so we want to take advantage of it.
708  *
709  * However, when we use 64-bit file support this doesn't work because
710  * glibc rejects unaligned offsets. Also glibc 2.1.3 mmap64 is broken
711  * in that it rounds unaligned offsets down to a page boundary. For
712  * these reasons we do a direct system call here.
713  */
714 static void *unaligned_mmap( void *addr, size_t length, unsigned int prot,
715                              unsigned int flags, int fd, unsigned int offset_low,
716                              unsigned int offset_high )
717 {
718 #if defined(linux) && defined(__i386__) && defined(__GNUC__)
719     if (!offset_high && (offset_low & page_mask))
720     {
721         int ret;
722         __asm__ __volatile__("push %%ebx\n\t"
723                              "movl %2,%%ebx\n\t"
724                              "int $0x80\n\t"
725                              "popl %%ebx"
726                              : "=a" (ret)
727                              : "0" (90), /* SYS_mmap */
728                                "g" (&addr) );
729         if (ret < 0 && ret > -4096)
730         {
731             errno = -ret;
732             ret = -1;
733         }
734         return (void *)ret;
735     }
736 #endif
737     return mmap( addr, length, prot, flags, fd, ((off_t)offset_high << 32) | offset_low );
738 }
739
740
741 /***********************************************************************
742  *           VIRTUAL_mmap
743  *
744  * Wrapper for mmap() that handles anonymous mappings portably,
745  * and falls back to read if mmap of a file fails.
746  */
747 static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size,
748                             DWORD offset_low, DWORD offset_high, int prot, int flags )
749 {
750     int pos;
751     LPVOID ret;
752     off_t offset;
753
754     if (fd == -1) return wine_anon_mmap( start, size, prot, flags );
755
756     if ((ret = unaligned_mmap( start, size, prot, flags, fd,
757                                offset_low, offset_high )) != (LPVOID)-1) return ret;
758
759     /* mmap() failed; if this is because the file offset is not    */
760     /* page-aligned (EINVAL), or because the underlying filesystem */
761     /* does not support mmap() (ENOEXEC,ENODEV), we do it by hand. */
762
763     if ((errno != ENOEXEC) && (errno != EINVAL) && (errno != ENODEV)) return ret;
764     if (prot & PROT_WRITE)
765     {
766         /* We cannot fake shared write mappings */
767 #ifdef MAP_SHARED
768         if (flags & MAP_SHARED) return ret;
769 #endif
770 #ifdef MAP_PRIVATE
771         if (!(flags & MAP_PRIVATE)) return ret;
772 #endif
773     }
774
775     /* Reserve the memory with an anonymous mmap */
776     ret = wine_anon_mmap( start, size, PROT_READ | PROT_WRITE, flags );
777     if (ret == (LPVOID)-1) return ret;
778     /* Now read in the file */
779     offset = ((off_t)offset_high << 32) | offset_low;
780     if ((pos = lseek( fd, offset, SEEK_SET )) == -1)
781     {
782         munmap( ret, size );
783         return (LPVOID)-1;
784     }
785     read( fd, ret, size );
786     lseek( fd, pos, SEEK_SET );  /* Restore the file pointer */
787     mprotect( ret, size, prot );  /* Set the right protection */
788     return ret;
789 }
790
791
792 /***********************************************************************
793  *             VirtualAlloc   (KERNEL32.@)
794  * Reserves or commits a region of pages in virtual address space
795  *
796  * RETURNS
797  *      Base address of allocated region of pages
798  *      NULL: Failure
799  */
800 LPVOID WINAPI VirtualAlloc(
801               LPVOID addr,  /* [in] Address of region to reserve or commit */
802               DWORD size,   /* [in] Size of region */
803               DWORD type,   /* [in] Type of allocation */
804               DWORD protect)/* [in] Type of access protection */
805 {
806     FILE_VIEW *view;
807     char *ptr, *base;
808     BYTE vprot;
809
810     TRACE("%p %08lx %lx %08lx\n", addr, size, type, protect );
811
812     /* Round parameters to a page boundary */
813
814     if (size > 0x7fc00000)  /* 2Gb - 4Mb */
815     {
816         SetLastError( ERROR_OUTOFMEMORY );
817         return NULL;
818     }
819     if (addr)
820     {
821         if (type & MEM_RESERVE) /* Round down to 64k boundary */
822             base = ROUND_ADDR( addr, granularity_mask );
823         else
824             base = ROUND_ADDR( addr, page_mask );
825         size = (((UINT_PTR)addr + size + page_mask) & ~page_mask) - (UINT_PTR)base;
826         if ((base <= (char *)granularity_mask) || (base + size < base))
827         {
828             /* disallow low 64k and wrap-around */
829             SetLastError( ERROR_INVALID_PARAMETER );
830             return NULL;
831         }
832     }
833     else
834     {
835         base = 0;
836         size = (size + page_mask) & ~page_mask;
837     }
838
839     if (type & MEM_TOP_DOWN) {
840         /* FIXME: MEM_TOP_DOWN allocates the largest possible address.
841          *        Is there _ANY_ way to do it with UNIX mmap()?
842          */
843         WARN("MEM_TOP_DOWN ignored\n");
844         type &= ~MEM_TOP_DOWN;
845     }
846     /* Compute the alloc type flags */
847
848     if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_SYSTEM)) ||
849         (type & ~(MEM_COMMIT | MEM_RESERVE | MEM_SYSTEM)))
850     {
851         ERR("called with wrong alloc type flags (%08lx) !\n", type);
852         SetLastError( ERROR_INVALID_PARAMETER );
853         return NULL;
854     }
855     if (type & (MEM_COMMIT | MEM_SYSTEM))
856         vprot = VIRTUAL_GetProt( protect ) | VPROT_COMMITTED;
857     else vprot = 0;
858
859     /* Reserve the memory */
860
861     if ((type & MEM_RESERVE) || !base)
862     {
863         if (type & MEM_SYSTEM)
864         {
865             if (!(view = VIRTUAL_CreateView( base, size, VFLAG_VALLOC | VFLAG_SYSTEM, vprot, 0 )))
866             {
867                 SetLastError( ERROR_OUTOFMEMORY );
868                 return NULL;
869             }
870             return (LPVOID)base;
871         }
872         ptr = anon_mmap_aligned( base, size, VIRTUAL_GetUnixProt( vprot ), 0 );
873         if (ptr == (void *)-1) return NULL;
874
875         if (!(view = VIRTUAL_CreateView( ptr, size, VFLAG_VALLOC, vprot, 0 )))
876         {
877             munmap( ptr, size );
878             SetLastError( ERROR_OUTOFMEMORY );
879             return NULL;
880         }
881         return ptr;
882     }
883
884     /* Commit the pages */
885
886     if (!(view = VIRTUAL_FindView( base )) ||
887         (base + size > (char *)view->base + view->size))
888     {
889         SetLastError( ERROR_INVALID_ADDRESS );
890         return NULL;
891     }
892
893     if (!VIRTUAL_SetProt( view, base, size, vprot )) return NULL;
894     return (LPVOID)base;
895 }
896
897
898 /***********************************************************************
899  *             VirtualAllocEx   (KERNEL32.@)
900  *
901  * Seems to be just as VirtualAlloc, but with process handle.
902  */
903 LPVOID WINAPI VirtualAllocEx(
904               HANDLE hProcess, /* [in] Handle of process to do mem operation */
905               LPVOID addr,  /* [in] Address of region to reserve or commit */
906               DWORD size,   /* [in] Size of region */
907               DWORD type,   /* [in] Type of allocation */
908               DWORD protect /* [in] Type of access protection */
909 ) {
910     if (MapProcessHandle( hProcess ) == GetCurrentProcessId())
911         return VirtualAlloc( addr, size, type, protect );
912     ERR("Unsupported on other process\n");
913     return NULL;
914 }
915
916
917 /***********************************************************************
918  *             VirtualFree   (KERNEL32.@)
919  * Release or decommits a region of pages in virtual address space.
920  * 
921  * RETURNS
922  *      TRUE: Success
923  *      FALSE: Failure
924  */
925 BOOL WINAPI VirtualFree(
926               LPVOID addr, /* [in] Address of region of committed pages */
927               DWORD size,  /* [in] Size of region */
928               DWORD type   /* [in] Type of operation */
929 ) {
930     FILE_VIEW *view;
931     char *base;
932
933     TRACE("%p %08lx %lx\n", addr, size, type );
934
935     /* Fix the parameters */
936
937     size = ROUND_SIZE( addr, size );
938     base = ROUND_ADDR( addr, page_mask );
939
940     if (!(view = VIRTUAL_FindView( base )) ||
941         (base + size > (char *)view->base + view->size) ||
942         !(view->flags & VFLAG_VALLOC))
943     {
944         SetLastError( ERROR_INVALID_PARAMETER );
945         return FALSE;
946     }
947
948     /* Compute the protection flags */
949
950     if ((type != MEM_DECOMMIT) && (type != MEM_RELEASE))
951     {
952         ERR("called with wrong free type flags (%08lx) !\n", type);
953         SetLastError( ERROR_INVALID_PARAMETER );
954         return FALSE;
955     }
956
957     /* Free the pages */
958
959     if (type == MEM_RELEASE)
960     {
961         if (size || (base != view->base))
962         {
963             SetLastError( ERROR_INVALID_PARAMETER );
964             return FALSE;
965         }
966         VIRTUAL_DeleteView( view );
967         return TRUE;
968     }
969
970     /* Decommit the pages by remapping zero-pages instead */
971
972     if (wine_anon_mmap( (LPVOID)base, size, VIRTUAL_GetUnixProt(0), MAP_FIXED ) != (LPVOID)base)
973         ERR( "Could not remap pages, expect trouble\n" );
974     return VIRTUAL_SetProt( view, base, size, 0 );
975 }
976
977
978 /***********************************************************************
979  *             VirtualLock   (KERNEL32.@)
980  * Locks the specified region of virtual address space
981  * 
982  * NOTE
983  *      Always returns TRUE
984  *
985  * RETURNS
986  *      TRUE: Success
987  *      FALSE: Failure
988  */
989 BOOL WINAPI VirtualLock(
990               LPVOID addr, /* [in] Address of first byte of range to lock */
991               DWORD size   /* [in] Number of bytes in range to lock */
992 ) {
993     return TRUE;
994 }
995
996
997 /***********************************************************************
998  *             VirtualUnlock   (KERNEL32.@)
999  * Unlocks a range of pages in the virtual address space
1000  *
1001  * NOTE
1002  *      Always returns TRUE
1003  *
1004  * RETURNS
1005  *      TRUE: Success
1006  *      FALSE: Failure
1007  */
1008 BOOL WINAPI VirtualUnlock(
1009               LPVOID addr, /* [in] Address of first byte of range */
1010               DWORD size   /* [in] Number of bytes in range */
1011 ) {
1012     return TRUE;
1013 }
1014
1015
1016 /***********************************************************************
1017  *             VirtualProtect   (KERNEL32.@)
1018  * Changes the access protection on a region of committed pages
1019  *
1020  * RETURNS
1021  *      TRUE: Success
1022  *      FALSE: Failure
1023  */
1024 BOOL WINAPI VirtualProtect(
1025               LPVOID addr,     /* [in] Address of region of committed pages */
1026               DWORD size,      /* [in] Size of region */
1027               DWORD new_prot,  /* [in] Desired access protection */
1028               LPDWORD old_prot /* [out] Address of variable to get old protection */
1029 ) {
1030     FILE_VIEW *view;
1031     char *base;
1032     UINT i;
1033     BYTE vprot, *p;
1034     DWORD prot;
1035
1036     TRACE("%p %08lx %08lx\n", addr, size, new_prot );
1037
1038     /* Fix the parameters */
1039
1040     size = ROUND_SIZE( addr, size );
1041     base = ROUND_ADDR( addr, page_mask );
1042
1043     if (!(view = VIRTUAL_FindView( base )) ||
1044         (base + size > (char *)view->base + view->size))
1045     {
1046         SetLastError( ERROR_INVALID_PARAMETER );
1047         return FALSE;
1048     }
1049
1050     /* Make sure all the pages are committed */
1051
1052     p = view->prot + ((base - (char *)view->base) >> page_shift);
1053     VIRTUAL_GetWin32Prot( *p, &prot, NULL );
1054     for (i = size >> page_shift; i; i--, p++)
1055     {
1056         if (!(*p & VPROT_COMMITTED))
1057         {
1058             SetLastError( ERROR_INVALID_PARAMETER );
1059             return FALSE;
1060         }
1061     }
1062
1063     if (old_prot) *old_prot = prot;
1064     vprot = VIRTUAL_GetProt( new_prot ) | VPROT_COMMITTED;
1065     return VIRTUAL_SetProt( view, base, size, vprot );
1066 }
1067
1068
1069 /***********************************************************************
1070  *             VirtualProtectEx   (KERNEL32.@)
1071  * Changes the access protection on a region of committed pages in the
1072  * virtual address space of a specified process
1073  *
1074  * RETURNS
1075  *      TRUE: Success
1076  *      FALSE: Failure
1077  */
1078 BOOL WINAPI VirtualProtectEx(
1079               HANDLE handle, /* [in]  Handle of process */
1080               LPVOID addr,     /* [in]  Address of region of committed pages */
1081               DWORD size,      /* [in]  Size of region */
1082               DWORD new_prot,  /* [in]  Desired access protection */
1083               LPDWORD old_prot /* [out] Address of variable to get old protection */ )
1084 {
1085     if (MapProcessHandle( handle ) == GetCurrentProcessId())
1086         return VirtualProtect( addr, size, new_prot, old_prot );
1087     ERR("Unsupported on other process\n");
1088     return FALSE;
1089 }
1090
1091
1092 /***********************************************************************
1093  *             VirtualQuery   (KERNEL32.@)
1094  * Provides info about a range of pages in virtual address space
1095  *
1096  * RETURNS
1097  *      Number of bytes returned in information buffer
1098  *      or 0 if addr is >= 0xc0000000 (kernel space).
1099  */
1100 DWORD WINAPI VirtualQuery(
1101              LPCVOID addr,                    /* [in]  Address of region */
1102              LPMEMORY_BASIC_INFORMATION info, /* [out] Address of info buffer */
1103              DWORD len                        /* [in]  Size of buffer */
1104 ) {
1105     FILE_VIEW *view;
1106     char *base, *alloc_base = 0;
1107     UINT size = 0;
1108
1109     if (addr >= (void*)0xc0000000) return 0;
1110
1111     base = ROUND_ADDR( addr, page_mask );
1112
1113     /* Find the view containing the address */
1114
1115     EnterCriticalSection(&csVirtual);
1116     view = VIRTUAL_FirstView;
1117     for (;;)
1118     {
1119         if (!view)
1120         {
1121             size = (char *)0xffff0000 - alloc_base;
1122             break;
1123         }
1124         if ((char *)view->base > base)
1125         {
1126             size = (char *)view->base - alloc_base;
1127             view = NULL;
1128             break;
1129         }
1130         if ((char *)view->base + view->size > base)
1131         {
1132             alloc_base = view->base;
1133             size = view->size;
1134             break;
1135         }
1136         alloc_base = (char *)view->base + view->size;
1137         view = view->next;
1138     }
1139     LeaveCriticalSection(&csVirtual);
1140
1141     /* Fill the info structure */
1142
1143     if (!view)
1144     {
1145         info->State             = MEM_FREE;
1146         info->Protect           = 0;
1147         info->AllocationProtect = 0;
1148         info->Type              = 0;
1149     }
1150     else
1151     {
1152         BYTE vprot = view->prot[(base - alloc_base) >> page_shift];
1153         VIRTUAL_GetWin32Prot( vprot, &info->Protect, &info->State );
1154         for (size = base - alloc_base; size < view->size; size += page_mask+1)
1155             if (view->prot[size >> page_shift] != vprot) break;
1156         info->AllocationProtect = view->protect;
1157         info->Type              = MEM_PRIVATE;  /* FIXME */
1158     }
1159
1160     info->BaseAddress    = (LPVOID)base;
1161     info->AllocationBase = (LPVOID)alloc_base;
1162     info->RegionSize     = size - (base - alloc_base);
1163     return sizeof(*info);
1164 }
1165
1166
1167 /***********************************************************************
1168  *             VirtualQueryEx   (KERNEL32.@)
1169  * Provides info about a range of pages in virtual address space of a
1170  * specified process
1171  *
1172  * RETURNS
1173  *      Number of bytes returned in information buffer
1174  */
1175 DWORD WINAPI VirtualQueryEx(
1176              HANDLE handle,                 /* [in] Handle of process */
1177              LPCVOID addr,                    /* [in] Address of region */
1178              LPMEMORY_BASIC_INFORMATION info, /* [out] Address of info buffer */
1179              DWORD len                        /* [in] Size of buffer */ )
1180 {
1181     if (MapProcessHandle( handle ) == GetCurrentProcessId())
1182         return VirtualQuery( addr, info, len );
1183     ERR("Unsupported on other process\n");
1184     return 0;
1185 }
1186
1187
1188 /***********************************************************************
1189  *             IsBadReadPtr   (KERNEL32.@)
1190  *
1191  * RETURNS
1192  *      FALSE: Process has read access to entire block
1193  *      TRUE: Otherwise
1194  */
1195 BOOL WINAPI IsBadReadPtr(
1196               LPCVOID ptr, /* [in] Address of memory block */
1197               UINT size )  /* [in] Size of block */
1198 {
1199     if (!size) return FALSE;  /* handle 0 size case w/o reference */
1200     __TRY
1201     {
1202         volatile const char *p = ptr;
1203         char dummy;
1204         UINT count = size;
1205
1206         while (count > page_size)
1207         {
1208             dummy = *p;
1209             p += page_size;
1210             count -= page_size;
1211         }
1212         dummy = p[0];
1213         dummy = p[count - 1];
1214     }
1215     __EXCEPT(page_fault) { return TRUE; }
1216     __ENDTRY
1217     return FALSE;
1218 }
1219
1220
1221 /***********************************************************************
1222  *             IsBadWritePtr   (KERNEL32.@)
1223  *
1224  * RETURNS
1225  *      FALSE: Process has write access to entire block
1226  *      TRUE: Otherwise
1227  */
1228 BOOL WINAPI IsBadWritePtr(
1229               LPVOID ptr, /* [in] Address of memory block */
1230               UINT size ) /* [in] Size of block in bytes */
1231 {
1232     if (!size) return FALSE;  /* handle 0 size case w/o reference */
1233     __TRY
1234     {
1235         volatile char *p = ptr;
1236         UINT count = size;
1237
1238         while (count > page_size)
1239         {
1240             *p |= 0;
1241             p += page_size;
1242             count -= page_size;
1243         }
1244         p[0] |= 0;
1245         p[count - 1] |= 0;
1246     }
1247     __EXCEPT(page_fault) { return TRUE; }
1248     __ENDTRY
1249     return FALSE;
1250 }
1251
1252
1253 /***********************************************************************
1254  *             IsBadHugeReadPtr   (KERNEL32.@)
1255  * RETURNS
1256  *      FALSE: Process has read access to entire block
1257  *      TRUE: Otherwise
1258  */
1259 BOOL WINAPI IsBadHugeReadPtr(
1260               LPCVOID ptr, /* [in] Address of memory block */
1261               UINT size  /* [in] Size of block */
1262 ) {
1263     return IsBadReadPtr( ptr, size );
1264 }
1265
1266
1267 /***********************************************************************
1268  *             IsBadHugeWritePtr   (KERNEL32.@)
1269  * RETURNS
1270  *      FALSE: Process has write access to entire block
1271  *      TRUE: Otherwise
1272  */
1273 BOOL WINAPI IsBadHugeWritePtr(
1274               LPVOID ptr, /* [in] Address of memory block */
1275               UINT size /* [in] Size of block */
1276 ) {
1277     return IsBadWritePtr( ptr, size );
1278 }
1279
1280
1281 /***********************************************************************
1282  *             IsBadCodePtr   (KERNEL32.@)
1283  *
1284  * RETURNS
1285  *      FALSE: Process has read access to specified memory
1286  *      TRUE: Otherwise
1287  */
1288 BOOL WINAPI IsBadCodePtr( FARPROC ptr ) /* [in] Address of function */
1289 {
1290     return IsBadReadPtr( ptr, 1 );
1291 }
1292
1293
1294 /***********************************************************************
1295  *             IsBadStringPtrA   (KERNEL32.@)
1296  *
1297  * RETURNS
1298  *      FALSE: Read access to all bytes in string
1299  *      TRUE: Else
1300  */
1301 BOOL WINAPI IsBadStringPtrA(
1302               LPCSTR str, /* [in] Address of string */
1303               UINT max )  /* [in] Maximum size of string */
1304 {
1305     __TRY
1306     {
1307         volatile const char *p = str;
1308         while (p != str + max) if (!*p++) break;
1309     }
1310     __EXCEPT(page_fault) { return TRUE; }
1311     __ENDTRY
1312     return FALSE;
1313 }
1314
1315
1316 /***********************************************************************
1317  *             IsBadStringPtrW   (KERNEL32.@)
1318  * See IsBadStringPtrA
1319  */
1320 BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT max )
1321 {
1322     __TRY
1323     {
1324         volatile const WCHAR *p = str;
1325         while (p != str + max) if (!*p++) break;
1326     }
1327     __EXCEPT(page_fault) { return TRUE; }
1328     __ENDTRY
1329     return FALSE;
1330 }
1331
1332
1333 /***********************************************************************
1334  *             CreateFileMappingA   (KERNEL32.@)
1335  * Creates a named or unnamed file-mapping object for the specified file
1336  *
1337  * RETURNS
1338  *      Handle: Success
1339  *      0: Mapping object does not exist
1340  *      NULL: Failure
1341  */
1342 HANDLE WINAPI CreateFileMappingA(
1343                 HANDLE hFile,   /* [in] Handle of file to map */
1344                 SECURITY_ATTRIBUTES *sa, /* [in] Optional security attributes*/
1345                 DWORD protect,   /* [in] Protection for mapping object */
1346                 DWORD size_high, /* [in] High-order 32 bits of object size */
1347                 DWORD size_low,  /* [in] Low-order 32 bits of object size */
1348                 LPCSTR name      /* [in] Name of file-mapping object */ )
1349 {
1350     HANDLE ret;
1351     BYTE vprot;
1352     DWORD len = name ? MultiByteToWideChar( CP_ACP, 0, name, strlen(name), NULL, 0 ) : 0;
1353
1354     /* Check parameters */
1355
1356     TRACE("(%x,%p,%08lx,%08lx%08lx,%s)\n",
1357           hFile, sa, protect, size_high, size_low, debugstr_a(name) );
1358
1359     if (len > MAX_PATH)
1360     {
1361         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1362         return 0;
1363     }
1364     vprot = VIRTUAL_GetProt( protect );
1365     if (protect & SEC_RESERVE)
1366     {
1367         if (hFile != INVALID_HANDLE_VALUE)
1368         {
1369             SetLastError( ERROR_INVALID_PARAMETER );
1370             return 0;
1371         }
1372     }
1373     else vprot |= VPROT_COMMITTED;
1374     if (protect & SEC_NOCACHE) vprot |= VPROT_NOCACHE;
1375     if (protect & SEC_IMAGE) vprot |= VPROT_IMAGE;
1376
1377     /* Create the server object */
1378
1379     if (hFile == INVALID_HANDLE_VALUE) hFile = 0;
1380     SERVER_START_VAR_REQ( create_mapping, len * sizeof(WCHAR) )
1381     {
1382         req->file_handle = hFile;
1383         req->size_high   = size_high;
1384         req->size_low    = size_low;
1385         req->protect     = vprot;
1386         req->inherit     = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
1387         if (len) MultiByteToWideChar( CP_ACP, 0, name, strlen(name), server_data_ptr(req), len );
1388         SetLastError(0);
1389         SERVER_CALL_ERR();
1390         ret = req->handle;
1391     }
1392     SERVER_END_VAR_REQ;
1393     return ret;
1394 }
1395
1396
1397 /***********************************************************************
1398  *             CreateFileMappingW   (KERNEL32.@)
1399  * See CreateFileMappingA
1400  */
1401 HANDLE WINAPI CreateFileMappingW( HANDLE hFile, LPSECURITY_ATTRIBUTES sa, 
1402                                   DWORD protect, DWORD size_high,  
1403                                   DWORD size_low, LPCWSTR name )
1404 {
1405     HANDLE ret;
1406     BYTE vprot;
1407     DWORD len = name ? strlenW(name) : 0;
1408
1409     /* Check parameters */
1410
1411     TRACE("(%x,%p,%08lx,%08lx%08lx,%s)\n",
1412           hFile, sa, protect, size_high, size_low, debugstr_w(name) );
1413
1414     if (len > MAX_PATH)
1415     {
1416         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1417         return 0;
1418     }
1419
1420     vprot = VIRTUAL_GetProt( protect );
1421     if (protect & SEC_RESERVE)
1422     {
1423         if (hFile != INVALID_HANDLE_VALUE)
1424         {
1425             SetLastError( ERROR_INVALID_PARAMETER );
1426             return 0;
1427         }
1428     }
1429     else vprot |= VPROT_COMMITTED;
1430     if (protect & SEC_NOCACHE) vprot |= VPROT_NOCACHE;
1431     if (protect & SEC_IMAGE) vprot |= VPROT_IMAGE;
1432
1433     /* Create the server object */
1434
1435     if (hFile == INVALID_HANDLE_VALUE) hFile = 0;
1436     SERVER_START_VAR_REQ( create_mapping, len * sizeof(WCHAR) )
1437     {
1438         req->file_handle = hFile;
1439         req->size_high   = size_high;
1440         req->size_low    = size_low;
1441         req->protect     = vprot;
1442         req->inherit     = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
1443         memcpy( server_data_ptr(req), name, len * sizeof(WCHAR) );
1444         SetLastError(0);
1445         SERVER_CALL_ERR();
1446         ret = req->handle;
1447     }
1448     SERVER_END_VAR_REQ;
1449     return ret;
1450 }
1451
1452
1453 /***********************************************************************
1454  *             OpenFileMappingA   (KERNEL32.@)
1455  * Opens a named file-mapping object.
1456  *
1457  * RETURNS
1458  *      Handle: Success
1459  *      NULL: Failure
1460  */
1461 HANDLE WINAPI OpenFileMappingA(
1462                 DWORD access,   /* [in] Access mode */
1463                 BOOL inherit, /* [in] Inherit flag */
1464                 LPCSTR name )   /* [in] Name of file-mapping object */
1465 {
1466     HANDLE ret;
1467     DWORD len = name ? MultiByteToWideChar( CP_ACP, 0, name, strlen(name), NULL, 0 ) : 0;
1468     if (len > MAX_PATH)
1469     {
1470         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1471         return 0;
1472     }
1473     SERVER_START_VAR_REQ( open_mapping, len * sizeof(WCHAR) )
1474     {
1475         req->access  = access;
1476         req->inherit = inherit;
1477         if (len) MultiByteToWideChar( CP_ACP, 0, name, strlen(name), server_data_ptr(req), len );
1478         SERVER_CALL_ERR();
1479         ret = req->handle;
1480     }
1481     SERVER_END_VAR_REQ;
1482     return ret;
1483 }
1484
1485
1486 /***********************************************************************
1487  *             OpenFileMappingW   (KERNEL32.@)
1488  * See OpenFileMappingA
1489  */
1490 HANDLE WINAPI OpenFileMappingW( DWORD access, BOOL inherit, LPCWSTR name)
1491 {
1492     HANDLE ret;
1493     DWORD len = name ? strlenW(name) : 0;
1494     if (len > MAX_PATH)
1495     {
1496         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1497         return 0;
1498     }
1499     SERVER_START_VAR_REQ( open_mapping, len * sizeof(WCHAR) )
1500     {
1501         req->access  = access;
1502         req->inherit = inherit;
1503         memcpy( server_data_ptr(req), name, len * sizeof(WCHAR) );
1504         SERVER_CALL_ERR();
1505         ret = req->handle;
1506     }
1507     SERVER_END_VAR_REQ;
1508     return ret;
1509 }
1510
1511
1512 /***********************************************************************
1513  *             MapViewOfFile   (KERNEL32.@)
1514  * Maps a view of a file into the address space
1515  *
1516  * RETURNS
1517  *      Starting address of mapped view
1518  *      NULL: Failure
1519  */
1520 LPVOID WINAPI MapViewOfFile(
1521               HANDLE mapping,  /* [in] File-mapping object to map */
1522               DWORD access,      /* [in] Access mode */
1523               DWORD offset_high, /* [in] High-order 32 bits of file offset */
1524               DWORD offset_low,  /* [in] Low-order 32 bits of file offset */
1525               DWORD count        /* [in] Number of bytes to map */
1526 ) {
1527     return MapViewOfFileEx( mapping, access, offset_high,
1528                             offset_low, count, NULL );
1529 }
1530
1531
1532 /***********************************************************************
1533  *             MapViewOfFileEx   (KERNEL32.@)
1534  * Maps a view of a file into the address space
1535  *
1536  * RETURNS
1537  *      Starting address of mapped view
1538  *      NULL: Failure
1539  */
1540 LPVOID WINAPI MapViewOfFileEx(
1541               HANDLE handle,   /* [in] File-mapping object to map */
1542               DWORD access,      /* [in] Access mode */
1543               DWORD offset_high, /* [in] High-order 32 bits of file offset */
1544               DWORD offset_low,  /* [in] Low-order 32 bits of file offset */
1545               DWORD count,       /* [in] Number of bytes to map */
1546               LPVOID addr        /* [in] Suggested starting address for mapped view */
1547 ) {
1548     FILE_VIEW *view;
1549     UINT size = 0;
1550     int flags = MAP_PRIVATE;
1551     int unix_handle = -1;
1552     int prot, res;
1553     void *base, *ptr = (void *)-1, *ret;
1554     DWORD size_low, size_high, header_size, shared_size;
1555     HANDLE shared_file;
1556
1557     /* Check parameters */
1558
1559     if ((offset_low & granularity_mask) ||
1560         (addr && ((UINT_PTR)addr & granularity_mask)))
1561     {
1562         SetLastError( ERROR_INVALID_PARAMETER );
1563         return NULL;
1564     }
1565
1566     SERVER_START_REQ( get_mapping_info )
1567     {
1568         req->handle = handle;
1569         res = SERVER_CALL_ERR();
1570         prot        = req->protect;
1571         base        = req->base;
1572         size_low    = req->size_low;
1573         size_high   = req->size_high;
1574         header_size = req->header_size;
1575         shared_file = req->shared_file;
1576         shared_size = req->shared_size;
1577     }
1578     SERVER_END_REQ;
1579     if (res) goto error;
1580
1581     if ((unix_handle = FILE_GetUnixHandle( handle, 0 )) == -1) goto error;
1582
1583     if (prot & VPROT_IMAGE)
1584         return map_image( handle, unix_handle, base, size_low, header_size,
1585                           shared_file, shared_size );
1586
1587
1588     if (size_high)
1589         ERR("Sizes larger than 4Gb not supported\n");
1590
1591     if ((offset_low >= size_low) ||
1592         (count > size_low - offset_low))
1593     {
1594         SetLastError( ERROR_INVALID_PARAMETER );
1595         goto error;
1596     }
1597     if (count) size = ROUND_SIZE( offset_low, count );
1598     else size = size_low - offset_low;
1599
1600     switch(access)
1601     {
1602     case FILE_MAP_ALL_ACCESS:
1603     case FILE_MAP_WRITE:
1604     case FILE_MAP_WRITE | FILE_MAP_READ:
1605         if (!(prot & VPROT_WRITE))
1606         {
1607             SetLastError( ERROR_INVALID_PARAMETER );
1608             goto error;
1609         }
1610         flags = MAP_SHARED;
1611         /* fall through */
1612     case FILE_MAP_READ:
1613     case FILE_MAP_COPY:
1614     case FILE_MAP_COPY | FILE_MAP_READ:
1615         if (prot & VPROT_READ) break;
1616         /* fall through */
1617     default:
1618         SetLastError( ERROR_INVALID_PARAMETER );
1619         goto error;
1620     }
1621
1622     /* FIXME: If a mapping is created with SEC_RESERVE and a process,
1623      * which has a view of this mapping commits some pages, they will
1624      * appear commited in all other processes, which have the same
1625      * view created. Since we don`t support this yet, we create the
1626      * whole mapping commited.
1627      */
1628     prot |= VPROT_COMMITTED;
1629
1630     /* Reserve a properly aligned area */
1631
1632     if ((ptr = anon_mmap_aligned( addr, size, PROT_NONE, 0 )) == (void *)-1) goto error;
1633
1634     /* Map the file */
1635
1636     TRACE("handle=%x size=%x offset=%lx\n", handle, size, offset_low );
1637
1638     ret = VIRTUAL_mmap( unix_handle, ptr, size, offset_low, offset_high,
1639                         VIRTUAL_GetUnixProt( prot ), flags | MAP_FIXED );
1640     if (ret != ptr)
1641     {
1642         ERR( "VIRTUAL_mmap %p %x %lx%08lx failed\n", ptr, size, offset_high, offset_low );
1643         goto error;
1644     }
1645
1646     if (!(view = VIRTUAL_CreateView( ptr, size, 0, prot, handle )))
1647     {
1648         SetLastError( ERROR_OUTOFMEMORY );
1649         goto error;
1650     }
1651     if (unix_handle != -1) close( unix_handle );
1652     return ptr;
1653
1654 error:
1655     if (unix_handle != -1) close( unix_handle );
1656     if (ptr != (void *)-1) munmap( ptr, size );
1657     return NULL;
1658 }
1659
1660
1661 /***********************************************************************
1662  *             FlushViewOfFile   (KERNEL32.@)
1663  * Writes to the disk a byte range within a mapped view of a file
1664  *
1665  * RETURNS
1666  *      TRUE: Success
1667  *      FALSE: Failure
1668  */
1669 BOOL WINAPI FlushViewOfFile(
1670               LPCVOID base, /* [in] Start address of byte range to flush */
1671               DWORD cbFlush /* [in] Number of bytes in range */
1672 ) {
1673     FILE_VIEW *view;
1674     void *addr = ROUND_ADDR( base, page_mask );
1675
1676     TRACE("FlushViewOfFile at %p for %ld bytes\n",
1677                      base, cbFlush );
1678
1679     if (!(view = VIRTUAL_FindView( addr )))
1680     {
1681         SetLastError( ERROR_INVALID_PARAMETER );
1682         return FALSE;
1683     }
1684     if (!cbFlush) cbFlush = view->size;
1685     if (!msync( addr, cbFlush, MS_SYNC )) return TRUE;
1686     SetLastError( ERROR_INVALID_PARAMETER );
1687     return FALSE;
1688 }
1689
1690
1691 /***********************************************************************
1692  *             UnmapViewOfFile   (KERNEL32.@)
1693  * Unmaps a mapped view of a file.
1694  *
1695  * NOTES
1696  *      Should addr be an LPCVOID?
1697  *
1698  * RETURNS
1699  *      TRUE: Success
1700  *      FALSE: Failure
1701  */
1702 BOOL WINAPI UnmapViewOfFile(
1703               LPVOID addr /* [in] Address where mapped view begins */
1704 ) {
1705     FILE_VIEW *view;
1706     void *base = ROUND_ADDR( addr, page_mask );
1707     if (!(view = VIRTUAL_FindView( base )) || (base != view->base))
1708     {
1709         SetLastError( ERROR_INVALID_PARAMETER );
1710         return FALSE;
1711     }
1712     VIRTUAL_DeleteView( view );
1713     return TRUE;
1714 }