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