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