Sweden-Number/memory/virtual.c
Alexandre Julliard 62a8b43301 Hacked server-side device support
Added user APCs support.
Many changes to the xterm console to make use of the server (not
finished yet).
Moved some other small stuff to the server.
1999-01-19 17:48:23 +00:00

1512 lines
44 KiB
C

/*
* Win32 virtual memory functions
*
* Copyright 1997 Alexandre Julliard
*/
#include <assert.h>
#include <errno.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include "winbase.h"
#include "winerror.h"
#include "file.h"
#include "heap.h"
#include "process.h"
#include "xmalloc.h"
#include "global.h"
#include "server.h"
#include "debug.h"
#ifndef MS_SYNC
#define MS_SYNC 0
#endif
/* File mapping */
typedef struct
{
K32OBJ header;
K32OBJ *file;
} FILE_MAPPING;
/* File view */
typedef struct _FV
{
struct _FV *next; /* Next view */
struct _FV *prev; /* Prev view */
UINT32 base; /* Base address */
UINT32 size; /* Size in bytes */
UINT32 flags; /* Allocation flags */
UINT32 offset; /* Offset from start of mapped file */
HANDLE32 mapping; /* Handle to the file mapping */
HANDLERPROC handlerProc; /* Fault handler */
LPVOID handlerArg; /* Fault handler argument */
BYTE protect; /* Protection for all pages at allocation time */
BYTE prot[1]; /* Protection byte for each page */
} FILE_VIEW;
/* Per-view flags */
#define VFLAG_SYSTEM 0x01
/* Conversion from VPROT_* to Win32 flags */
static const BYTE VIRTUAL_Win32Flags[16] =
{
PAGE_NOACCESS, /* 0 */
PAGE_READONLY, /* READ */
PAGE_READWRITE, /* WRITE */
PAGE_READWRITE, /* READ | WRITE */
PAGE_EXECUTE, /* EXEC */
PAGE_EXECUTE_READ, /* READ | EXEC */
PAGE_EXECUTE_READWRITE, /* WRITE | EXEC */
PAGE_EXECUTE_READWRITE, /* READ | WRITE | EXEC */
PAGE_WRITECOPY, /* WRITECOPY */
PAGE_WRITECOPY, /* READ | WRITECOPY */
PAGE_WRITECOPY, /* WRITE | WRITECOPY */
PAGE_WRITECOPY, /* READ | WRITE | WRITECOPY */
PAGE_EXECUTE_WRITECOPY, /* EXEC | WRITECOPY */
PAGE_EXECUTE_WRITECOPY, /* READ | EXEC | WRITECOPY */
PAGE_EXECUTE_WRITECOPY, /* WRITE | EXEC | WRITECOPY */
PAGE_EXECUTE_WRITECOPY /* READ | WRITE | EXEC | WRITECOPY */
};
static FILE_VIEW *VIRTUAL_FirstView;
#ifdef __i386__
/* These are always the same on an i386, and it will be faster this way */
# define page_mask 0xfff
# define page_shift 12
# define granularity_mask 0xffff
#else
static UINT32 page_shift;
static UINT32 page_mask;
static UINT32 granularity_mask; /* Allocation granularity (usually 64k) */
#endif /* __i386__ */
#define ROUND_ADDR(addr) \
((UINT32)(addr) & ~page_mask)
#define ROUND_SIZE(addr,size) \
(((UINT32)(size) + ((UINT32)(addr) & page_mask) + page_mask) & ~page_mask)
static void VIRTUAL_DestroyMapping( K32OBJ *obj );
const K32OBJ_OPS MEM_MAPPED_FILE_Ops =
{
VIRTUAL_DestroyMapping /* destroy */
};
#define VIRTUAL_DEBUG_DUMP_VIEW(view) \
if (!TRACE_ON(virtual)); else VIRTUAL_DumpView(view)
/***********************************************************************
* VIRTUAL_GetProtStr
*/
static const char *VIRTUAL_GetProtStr( BYTE prot )
{
static char buffer[6];
buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-';
buffer[1] = (prot & VPROT_GUARD) ? 'g' : '-';
buffer[2] = (prot & VPROT_READ) ? 'r' : '-';
buffer[3] = (prot & VPROT_WRITE) ?
((prot & VPROT_WRITECOPY) ? 'w' : 'W') : '-';
buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-';
buffer[5] = 0;
return buffer;
}
/***********************************************************************
* VIRTUAL_DumpView
*/
static void VIRTUAL_DumpView( FILE_VIEW *view )
{
UINT32 i, count;
UINT32 addr = view->base;
BYTE prot = view->prot[0];
DUMP( "View: %08x - %08x%s",
view->base, view->base + view->size - 1,
(view->flags & VFLAG_SYSTEM) ? " (system)" : "" );
if (view->mapping)
DUMP( " %d @ %08x\n", view->mapping, view->offset );
else
DUMP( " (anonymous)\n");
for (count = i = 1; i < view->size >> page_shift; i++, count++)
{
if (view->prot[i] == prot) continue;
DUMP( " %08x - %08x %s\n",
addr, addr + (count << page_shift) - 1,
VIRTUAL_GetProtStr(prot) );
addr += (count << page_shift);
prot = view->prot[i];
count = 0;
}
if (count)
DUMP( " %08x - %08x %s\n",
addr, addr + (count << page_shift) - 1,
VIRTUAL_GetProtStr(prot) );
}
/***********************************************************************
* VIRTUAL_Dump
*/
void VIRTUAL_Dump(void)
{
FILE_VIEW *view = VIRTUAL_FirstView;
DUMP( "\nDump of all virtual memory views:\n\n" );
while (view)
{
VIRTUAL_DumpView( view );
view = view->next;
}
}
/***********************************************************************
* VIRTUAL_FindView
*
* Find the view containing a given address.
*
* RETURNS
* View: Success
* NULL: Failure
*/
static FILE_VIEW *VIRTUAL_FindView(
UINT32 addr /* [in] Address */
) {
FILE_VIEW *view = VIRTUAL_FirstView;
while (view)
{
if (view->base > addr) return NULL;
if (view->base + view->size > addr) return view;
view = view->next;
}
return NULL;
}
/***********************************************************************
* VIRTUAL_CreateView
*
* Create a new view and add it in the linked list.
*/
static FILE_VIEW *VIRTUAL_CreateView( UINT32 base, UINT32 size, UINT32 offset,
UINT32 flags, BYTE vprot,
HANDLE32 mapping )
{
FILE_VIEW *view, *prev;
/* Create the view structure */
assert( !(base & page_mask) );
assert( !(size & page_mask) );
size >>= page_shift;
if (!(view = (FILE_VIEW *)malloc( sizeof(*view) + size - 1 ))) return NULL;
view->base = base;
view->size = size << page_shift;
view->flags = flags;
view->offset = offset;
view->mapping = mapping;
view->protect = vprot;
memset( view->prot, vprot, size );
/* Duplicate the mapping handle */
if ((view->mapping != -1) &&
!DuplicateHandle( GetCurrentProcess(), view->mapping,
GetCurrentProcess(), &view->mapping,
0, FALSE, DUPLICATE_SAME_ACCESS ))
{
free( view );
return NULL;
}
/* Insert it in the linked list */
if (!VIRTUAL_FirstView || (VIRTUAL_FirstView->base > base))
{
view->next = VIRTUAL_FirstView;
view->prev = NULL;
if (view->next) view->next->prev = view;
VIRTUAL_FirstView = view;
}
else
{
prev = VIRTUAL_FirstView;
while (prev->next && (prev->next->base < base)) prev = prev->next;
view->next = prev->next;
view->prev = prev;
if (view->next) view->next->prev = view;
prev->next = view;
}
VIRTUAL_DEBUG_DUMP_VIEW( view );
return view;
}
/***********************************************************************
* VIRTUAL_DeleteView
* Deletes a view.
*
* RETURNS
* None
*/
static void VIRTUAL_DeleteView(
FILE_VIEW *view /* [in] View */
) {
FILE_munmap( (void *)view->base, 0, view->size );
if (view->next) view->next->prev = view->prev;
if (view->prev) view->prev->next = view->next;
else VIRTUAL_FirstView = view->next;
if (view->mapping) CloseHandle( view->mapping );
free( view );
}
/***********************************************************************
* VIRTUAL_GetUnixProt
*
* Convert page protections to protection for mmap/mprotect.
*/
static int VIRTUAL_GetUnixProt( BYTE vprot )
{
int prot = 0;
if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD))
{
if (vprot & VPROT_READ) prot |= PROT_READ;
if (vprot & VPROT_WRITE) prot |= PROT_WRITE;
if (vprot & VPROT_EXEC) prot |= PROT_EXEC;
}
return prot;
}
/***********************************************************************
* VIRTUAL_GetWin32Prot
*
* Convert page protections to Win32 flags.
*
* RETURNS
* None
*/
static void VIRTUAL_GetWin32Prot(
BYTE vprot, /* [in] Page protection flags */
DWORD *protect, /* [out] Location to store Win32 protection flags */
DWORD *state /* [out] Location to store mem state flag */
) {
if (protect) {
*protect = VIRTUAL_Win32Flags[vprot & 0x0f];
/* if (vprot & VPROT_GUARD) *protect |= PAGE_GUARD;*/
if (vprot & VPROT_NOCACHE) *protect |= PAGE_NOCACHE;
if (vprot & VPROT_GUARD) *protect = PAGE_NOACCESS;
}
if (state) *state = (vprot & VPROT_COMMITTED) ? MEM_COMMIT : MEM_RESERVE;
}
/***********************************************************************
* VIRTUAL_GetProt
*
* Build page protections from Win32 flags.
*
* RETURNS
* Value of page protection flags
*/
static BYTE VIRTUAL_GetProt(
DWORD protect /* [in] Win32 protection flags */
) {
BYTE vprot;
switch(protect & 0xff)
{
case PAGE_READONLY:
vprot = VPROT_READ;
break;
case PAGE_READWRITE:
vprot = VPROT_READ | VPROT_WRITE;
break;
case PAGE_WRITECOPY:
vprot = VPROT_READ | VPROT_WRITE | VPROT_WRITECOPY;
break;
case PAGE_EXECUTE:
vprot = VPROT_EXEC;
break;
case PAGE_EXECUTE_READ:
vprot = VPROT_EXEC | VPROT_READ;
break;
case PAGE_EXECUTE_READWRITE:
vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITE | VPROT_WRITECOPY;
break;
case PAGE_EXECUTE_WRITECOPY:
vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITE;
break;
case PAGE_NOACCESS:
default:
vprot = 0;
break;
}
if (protect & PAGE_GUARD) vprot |= VPROT_GUARD;
if (protect & PAGE_NOCACHE) vprot |= VPROT_NOCACHE;
return vprot;
}
/***********************************************************************
* VIRTUAL_SetProt
*
* Change the protection of a range of pages.
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
static BOOL32 VIRTUAL_SetProt(
FILE_VIEW *view, /* [in] Pointer to view */
UINT32 base, /* [in] Starting address */
UINT32 size, /* [in] Size in bytes */
BYTE vprot /* [in] Protections to use */
) {
TRACE(virtual, "%08x-%08x %s\n",
base, base + size - 1, VIRTUAL_GetProtStr( vprot ) );
if (mprotect( (void *)base, size, VIRTUAL_GetUnixProt(vprot) ))
return FALSE; /* FIXME: last error */
memset( view->prot + ((base - view->base) >> page_shift),
vprot, size >> page_shift );
VIRTUAL_DEBUG_DUMP_VIEW( view );
return TRUE;
}
/***********************************************************************
* VIRTUAL_CheckFlags
*
* Check that all pages in a range have the given flags.
*
* RETURNS
* TRUE: They do
* FALSE: They do not
*/
static BOOL32 VIRTUAL_CheckFlags(
UINT32 base, /* [in] Starting address */
UINT32 size, /* [in] Size in bytes */
BYTE flags /* [in] Flags to check for */
) {
FILE_VIEW *view;
UINT32 page;
if (!size) return TRUE;
if (!(view = VIRTUAL_FindView( base ))) return FALSE;
if (view->base + view->size < base + size) return FALSE;
page = (base - view->base) >> page_shift;
size = ROUND_SIZE( base, size ) >> page_shift;
while (size--) if ((view->prot[page++] & flags) != flags) return FALSE;
return TRUE;
}
/***********************************************************************
* VIRTUAL_Init
*/
BOOL32 VIRTUAL_Init(void)
{
#ifndef __i386__
DWORD page_size;
# ifdef HAVE_GETPAGESIZE
page_size = getpagesize();
# else
# ifdef __svr4__
page_size = sysconf(_SC_PAGESIZE);
# else
# error Cannot get the page size on this platform
# endif
# endif
page_mask = page_size - 1;
granularity_mask = 0xffff; /* hard-coded for now */
/* Make sure we have a power of 2 */
assert( !(page_size & page_mask) );
page_shift = 0;
while ((1 << page_shift) != page_size) page_shift++;
#endif /* !__i386__ */
#ifdef linux
{
/* Do not use stdio here since it may temporarily change the size
* of some segments (ie libc6 adds 0x1000 per open FILE)
*/
int fd = open ("/proc/self/maps", O_RDONLY);
if (fd >= 0)
{
char buffer[512]; /* line might be rather long in 2.1 */
for (;;)
{
int start, end, offset;
char r, w, x, p;
BYTE vprot = VPROT_COMMITTED;
char * ptr = buffer;
int count = sizeof(buffer);
while (1 == read(fd, ptr, 1) && *ptr != '\n' && --count > 0)
ptr++;
if (*ptr != '\n') break;
*ptr = '\0';
sscanf( buffer, "%x-%x %c%c%c%c %x",
&start, &end, &r, &w, &x, &p, &offset );
if (r == 'r') vprot |= VPROT_READ;
if (w == 'w') vprot |= VPROT_WRITE;
if (x == 'x') vprot |= VPROT_EXEC;
if (p == 'p') vprot |= VPROT_WRITECOPY;
VIRTUAL_CreateView( start, end - start, 0,
VFLAG_SYSTEM, vprot, -1 );
}
close (fd);
}
}
#endif /* linux */
return TRUE;
}
/***********************************************************************
* VIRTUAL_GetPageSize
*/
DWORD VIRTUAL_GetPageSize(void)
{
return 1 << page_shift;
}
/***********************************************************************
* VIRTUAL_GetGranularity
*/
DWORD VIRTUAL_GetGranularity(void)
{
return granularity_mask + 1;
}
/***********************************************************************
* VIRTUAL_SetFaultHandler
*/
BOOL32 VIRTUAL_SetFaultHandler( LPVOID addr, HANDLERPROC proc, LPVOID arg )
{
FILE_VIEW *view;
if (!(view = VIRTUAL_FindView((UINT32)addr))) return FALSE;
view->handlerProc = proc;
view->handlerArg = arg;
return TRUE;
}
/***********************************************************************
* VIRTUAL_HandleFault
*/
BOOL32 VIRTUAL_HandleFault(LPVOID addr)
{
FILE_VIEW *view = VIRTUAL_FindView((UINT32)addr);
if (view && view->handlerProc)
return view->handlerProc(view->handlerArg, addr);
return FALSE;
}
/***********************************************************************
* VirtualAlloc (KERNEL32.548)
* Reserves or commits a region of pages in virtual address space
*
* RETURNS
* Base address of allocated region of pages
* NULL: Failure
*/
LPVOID WINAPI VirtualAlloc(
LPVOID addr, /* [in] Address of region to reserve or commit */
DWORD size, /* [in] Size of region */
DWORD type, /* [in] Type of allocation */
DWORD protect /* [in] Type of access protection */
) {
FILE_VIEW *view;
UINT32 base, ptr, view_size;
BYTE vprot;
TRACE(virtual, "%08x %08lx %lx %08lx\n",
(UINT32)addr, size, type, protect );
/* Round parameters to a page boundary */
if (size > 0x7fc00000) /* 2Gb - 4Mb */
{
SetLastError( ERROR_OUTOFMEMORY );
return NULL;
}
if (addr)
{
if (type & MEM_RESERVE) /* Round down to 64k boundary */
base = ((UINT32)addr + granularity_mask) & ~granularity_mask;
else
base = ROUND_ADDR( addr );
size = (((UINT32)addr + size + page_mask) & ~page_mask) - base;
if (base + size < base) /* Disallow wrap-around */
{
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
}
else
{
base = 0;
size = (size + page_mask) & ~page_mask;
}
if (type & MEM_TOP_DOWN) {
/* FIXME: MEM_TOP_DOWN allocates the largest possible address.
* Is there _ANY_ way to do it with UNIX mmap()?
*/
WARN(virtual,"MEM_TOP_DOWN ignored\n");
type &= ~MEM_TOP_DOWN;
}
/* Compute the protection flags */
if (!(type & (MEM_COMMIT | MEM_RESERVE)) ||
(type & ~(MEM_COMMIT | MEM_RESERVE)))
{
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
if (type & MEM_COMMIT)
vprot = VIRTUAL_GetProt( protect ) | VPROT_COMMITTED;
else vprot = 0;
/* Reserve the memory */
if ((type & MEM_RESERVE) || !base)
{
view_size = size + (base ? 0 : granularity_mask + 1);
ptr = (UINT32)FILE_dommap( -1, (LPVOID)base, 0, view_size, 0, 0,
VIRTUAL_GetUnixProt( vprot ), MAP_PRIVATE );
if (ptr == (UINT32)-1)
{
SetLastError( ERROR_OUTOFMEMORY );
return NULL;
}
if (!base)
{
/* Release the extra memory while keeping the range */
/* starting on a 64k boundary. */
if (ptr & granularity_mask)
{
UINT32 extra = granularity_mask + 1 - (ptr & granularity_mask);
FILE_munmap( (void *)ptr, 0, extra );
ptr += extra;
view_size -= extra;
}
if (view_size > size)
FILE_munmap( (void *)(ptr + size), 0, view_size - size );
}
else if (ptr != base)
{
/* We couldn't get the address we wanted */
FILE_munmap( (void *)ptr, 0, view_size );
SetLastError( ERROR_INVALID_ADDRESS );
return NULL;
}
if (!(view = VIRTUAL_CreateView( ptr, size, 0, 0, vprot, -1 )))
{
FILE_munmap( (void *)ptr, 0, size );
SetLastError( ERROR_OUTOFMEMORY );
return NULL;
}
VIRTUAL_DEBUG_DUMP_VIEW( view );
return (LPVOID)ptr;
}
/* Commit the pages */
if (!(view = VIRTUAL_FindView( base )) ||
(base + size > view->base + view->size))
{
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
if (!VIRTUAL_SetProt( view, base, size, vprot )) return NULL;
return (LPVOID)base;
}
/***********************************************************************
* VirtualFree (KERNEL32.550)
* Release or decommits a region of pages in virtual address space.
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI VirtualFree(
LPVOID addr, /* [in] Address of region of committed pages */
DWORD size, /* [in] Size of region */
DWORD type /* [in] Type of operation */
) {
FILE_VIEW *view;
UINT32 base;
TRACE(virtual, "%08x %08lx %lx\n",
(UINT32)addr, size, type );
/* Fix the parameters */
size = ROUND_SIZE( addr, size );
base = ROUND_ADDR( addr );
if (!(view = VIRTUAL_FindView( base )) ||
(base + size > view->base + view->size))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
/* Compute the protection flags */
if ((type != MEM_DECOMMIT) && (type != MEM_RELEASE))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
/* Free the pages */
if (type == MEM_RELEASE)
{
if (size || (base != view->base))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
VIRTUAL_DeleteView( view );
return TRUE;
}
/* Decommit the pages */
return VIRTUAL_SetProt( view, base, size, 0 );
}
/***********************************************************************
* VirtualLock (KERNEL32.551)
* Locks the specified region of virtual address space
*
* NOTE
* Always returns TRUE
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI VirtualLock(
LPVOID addr, /* [in] Address of first byte of range to lock */
DWORD size /* [in] Number of bytes in range to lock */
) {
return TRUE;
}
/***********************************************************************
* VirtualUnlock (KERNEL32.556)
* Unlocks a range of pages in the virtual address space
*
* NOTE
* Always returns TRUE
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI VirtualUnlock(
LPVOID addr, /* [in] Address of first byte of range */
DWORD size /* [in] Number of bytes in range */
) {
return TRUE;
}
/***********************************************************************
* VirtualProtect (KERNEL32.552)
* Changes the access protection on a region of committed pages
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI VirtualProtect(
LPVOID addr, /* [in] Address of region of committed pages */
DWORD size, /* [in] Size of region */
DWORD new_prot, /* [in] Desired access protection */
LPDWORD old_prot /* [out] Address of variable to get old protection */
) {
FILE_VIEW *view;
UINT32 base, i;
BYTE vprot, *p;
TRACE(virtual, "%08x %08lx %08lx\n",
(UINT32)addr, size, new_prot );
/* Fix the parameters */
size = ROUND_SIZE( addr, size );
base = ROUND_ADDR( addr );
if (!(view = VIRTUAL_FindView( base )) ||
(base + size > view->base + view->size))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
/* Make sure all the pages are committed */
p = view->prot + ((base - view->base) >> page_shift);
for (i = size >> page_shift; i; i--, p++)
{
if (!(*p & VPROT_COMMITTED))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
VIRTUAL_GetWin32Prot( view->prot[0], old_prot, NULL );
vprot = VIRTUAL_GetProt( new_prot ) | VPROT_COMMITTED;
return VIRTUAL_SetProt( view, base, size, vprot );
}
/***********************************************************************
* VirtualProtectEx (KERNEL32.553)
* Changes the access protection on a region of committed pages in the
* virtual address space of a specified process
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI VirtualProtectEx(
HANDLE32 handle, /* [in] Handle of process */
LPVOID addr, /* [in] Address of region of committed pages */
DWORD size, /* [in] Size of region */
DWORD new_prot, /* [in] Desired access protection */
LPDWORD old_prot /* [out] Address of variable to get old protection */ )
{
if (PROCESS_IsCurrent( handle ))
return VirtualProtect( addr, size, new_prot, old_prot );
ERR(virtual,"Unsupported on other process\n");
return FALSE;
}
/***********************************************************************
* VirtualQuery (KERNEL32.554)
* Provides info about a range of pages in virtual address space
*
* RETURNS
* Number of bytes returned in information buffer
*/
DWORD WINAPI VirtualQuery(
LPCVOID addr, /* [in] Address of region */
LPMEMORY_BASIC_INFORMATION info, /* [out] Address of info buffer */
DWORD len /* [in] Size of buffer */
) {
FILE_VIEW *view = VIRTUAL_FirstView;
UINT32 base = ROUND_ADDR( addr );
UINT32 alloc_base = 0;
UINT32 size = 0;
/* Find the view containing the address */
for (;;)
{
if (!view)
{
size = 0xffff0000 - alloc_base;
break;
}
if (view->base > base)
{
size = view->base - alloc_base;
view = NULL;
break;
}
if (view->base + view->size > base)
{
alloc_base = view->base;
size = view->size;
break;
}
alloc_base = view->base + view->size;
view = view->next;
}
/* Fill the info structure */
if (!view)
{
info->State = MEM_FREE;
info->Protect = 0;
info->AllocationProtect = 0;
info->Type = 0;
}
else
{
BYTE vprot = view->prot[(base - alloc_base) >> page_shift];
VIRTUAL_GetWin32Prot( vprot, &info->Protect, &info->State );
for (size = base - alloc_base; size < view->size; size += page_mask+1)
if (view->prot[size >> page_shift] != vprot) break;
info->AllocationProtect = view->protect;
info->Type = MEM_PRIVATE; /* FIXME */
}
info->BaseAddress = (LPVOID)base;
info->AllocationBase = (LPVOID)alloc_base;
info->RegionSize = size - (base - alloc_base);
return sizeof(*info);
}
/***********************************************************************
* VirtualQueryEx (KERNEL32.555)
* Provides info about a range of pages in virtual address space of a
* specified process
*
* RETURNS
* Number of bytes returned in information buffer
*/
DWORD WINAPI VirtualQueryEx(
HANDLE32 handle, /* [in] Handle of process */
LPCVOID addr, /* [in] Address of region */
LPMEMORY_BASIC_INFORMATION info, /* [out] Address of info buffer */
DWORD len /* [in] Size of buffer */ )
{
if (PROCESS_IsCurrent( handle ))
return VirtualQuery( addr, info, len );
ERR(virtual,"Unsupported on other process\n");
return 0;
}
/***********************************************************************
* IsBadReadPtr32 (KERNEL32.354)
*
* RETURNS
* FALSE: Process has read access to entire block
* TRUE: Otherwise
*/
BOOL32 WINAPI IsBadReadPtr32(
LPCVOID ptr, /* Address of memory block */
UINT32 size /* Size of block */
) {
return !VIRTUAL_CheckFlags( (UINT32)ptr, size,
VPROT_READ | VPROT_COMMITTED );
}
/***********************************************************************
* IsBadWritePtr32 (KERNEL32.357)
*
* RETURNS
* FALSE: Process has write access to entire block
* TRUE: Otherwise
*/
BOOL32 WINAPI IsBadWritePtr32(
LPVOID ptr, /* [in] Address of memory block */
UINT32 size /* [in] Size of block in bytes */
) {
return !VIRTUAL_CheckFlags( (UINT32)ptr, size,
VPROT_WRITE | VPROT_COMMITTED );
}
/***********************************************************************
* IsBadHugeReadPtr32 (KERNEL32.352)
* RETURNS
* FALSE: Process has read access to entire block
* TRUE: Otherwise
*/
BOOL32 WINAPI IsBadHugeReadPtr32(
LPCVOID ptr, /* [in] Address of memory block */
UINT32 size /* [in] Size of block */
) {
return IsBadReadPtr32( ptr, size );
}
/***********************************************************************
* IsBadHugeWritePtr32 (KERNEL32.353)
* RETURNS
* FALSE: Process has write access to entire block
* TRUE: Otherwise
*/
BOOL32 WINAPI IsBadHugeWritePtr32(
LPVOID ptr, /* [in] Address of memory block */
UINT32 size /* [in] Size of block */
) {
return IsBadWritePtr32( ptr, size );
}
/***********************************************************************
* IsBadCodePtr32 (KERNEL32.351)
*
* RETURNS
* FALSE: Process has read access to specified memory
* TRUE: Otherwise
*/
BOOL32 WINAPI IsBadCodePtr32(
FARPROC32 ptr /* [in] Address of function */
) {
return !VIRTUAL_CheckFlags( (UINT32)ptr, 1, VPROT_EXEC | VPROT_COMMITTED );
}
/***********************************************************************
* IsBadStringPtr32A (KERNEL32.355)
*
* RETURNS
* FALSE: Read access to all bytes in string
* TRUE: Else
*/
BOOL32 WINAPI IsBadStringPtr32A(
LPCSTR str, /* [in] Address of string */
UINT32 max /* [in] Maximum size of string */
) {
FILE_VIEW *view;
UINT32 page, count;
if (!max) return FALSE;
if (!(view = VIRTUAL_FindView( (UINT32)str ))) return TRUE;
page = ((UINT32)str - view->base) >> page_shift;
count = page_mask + 1 - ((UINT32)str & page_mask);
while (max)
{
if ((view->prot[page] & (VPROT_READ | VPROT_COMMITTED)) !=
(VPROT_READ | VPROT_COMMITTED))
return TRUE;
if (count > max) count = max;
max -= count;
while (count--) if (!*str++) return FALSE;
if (++page >= view->size >> page_shift) return TRUE;
count = page_mask + 1;
}
return FALSE;
}
/***********************************************************************
* IsBadStringPtr32W (KERNEL32.356)
* See IsBadStringPtr32A
*/
BOOL32 WINAPI IsBadStringPtr32W( LPCWSTR str, UINT32 max )
{
FILE_VIEW *view;
UINT32 page, count;
if (!max) return FALSE;
if (!(view = VIRTUAL_FindView( (UINT32)str ))) return TRUE;
page = ((UINT32)str - view->base) >> page_shift;
count = (page_mask + 1 - ((UINT32)str & page_mask)) / sizeof(WCHAR);
while (max)
{
if ((view->prot[page] & (VPROT_READ | VPROT_COMMITTED)) !=
(VPROT_READ | VPROT_COMMITTED))
return TRUE;
if (count > max) count = max;
max -= count;
while (count--) if (!*str++) return FALSE;
if (++page >= view->size >> page_shift) return TRUE;
count = (page_mask + 1) / sizeof(WCHAR);
}
return FALSE;
}
/***********************************************************************
* CreateFileMapping32A (KERNEL32.46)
* Creates a named or unnamed file-mapping object for the specified file
*
* RETURNS
* Handle: Success
* 0: Mapping object does not exist
* NULL: Failure
*/
HANDLE32 WINAPI CreateFileMapping32A(
HFILE32 hFile, /* [in] Handle of file to map */
SECURITY_ATTRIBUTES *sa, /* [in] Optional security attributes*/
DWORD protect, /* [in] Protection for mapping object */
DWORD size_high, /* [in] High-order 32 bits of object size */
DWORD size_low, /* [in] Low-order 32 bits of object size */
LPCSTR name /* [in] Name of file-mapping object */ )
{
FILE_MAPPING *mapping = NULL;
K32OBJ *file;
struct create_mapping_request req;
struct create_mapping_reply reply = { -1 };
HANDLE32 handle;
BYTE vprot;
BOOL32 inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
/* First search for an object with the same name */
K32OBJ *obj = K32OBJ_FindName( name );
if (obj)
{
if (obj->type == K32OBJ_MEM_MAPPED_FILE)
{
req.handle = -1;
CLIENT_SendRequest( REQ_CREATE_MAPPING, -1, 2,
&req, sizeof(req),
name, name ? strlen(name) + 1 : 0 );
CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL );
if (reply.handle == -1) return 0;
if (GetLastError() != ERROR_ALREADY_EXISTS)
return 0; /* not supposed to happen */
handle = HANDLE_Alloc( PROCESS_Current(), obj,
FILE_MAP_ALL_ACCESS /*FIXME*/, inherit, reply.handle );
}
else
{
SetLastError( ERROR_DUP_NAME );
handle = 0;
}
K32OBJ_DecCount( obj );
return handle;
}
/* Check parameters */
TRACE(virtual,"(%x,%p,%08lx,%08lx%08lx,%s)\n",
hFile, sa, protect, size_high, size_low, debugstr_a(name) );
vprot = VIRTUAL_GetProt( protect );
if (protect & SEC_RESERVE)
{
if (hFile != INVALID_HANDLE_VALUE32)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
}
else vprot |= VPROT_COMMITTED;
if (protect & SEC_NOCACHE) vprot |= VPROT_NOCACHE;
/* Compute the size and extend the file if necessary */
if (hFile == INVALID_HANDLE_VALUE32)
{
if (!size_high && !size_low)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
file = NULL;
if (name)
{
CHAR buf[260];
GetTempPath32A(260,buf);
GetTempFileName32A(buf,"wine",0,buf);
hFile = CreateFile32A(
buf,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,/* so we can reuse the tmpfn */
NULL,
OPEN_ALWAYS,
0,
0
);
/* FIXME: bad hack to avoid lots of leftover tempfiles */
DeleteFile32A(buf);
if (hFile == INVALID_HANDLE_VALUE32)
FIXME(virtual,"could not create temp. file for anon shared mapping: reason was 0x%08lx\n",GetLastError());
}
}
if (hFile != INVALID_HANDLE_VALUE32) /* We have a file */
{
BY_HANDLE_FILE_INFORMATION info;
DWORD access = GENERIC_READ;
if (((protect & 0xff) == PAGE_READWRITE) ||
((protect & 0xff) == PAGE_WRITECOPY) ||
((protect & 0xff) == PAGE_EXECUTE_READWRITE) ||
((protect & 0xff) == PAGE_EXECUTE_WRITECOPY))
access |= GENERIC_WRITE;
if (!(file = HANDLE_GetObjPtr( PROCESS_Current(), hFile, K32OBJ_FILE,
access, &req.handle ))) goto error;
if (!GetFileInformationByHandle( hFile, &info )) goto error;
if (!size_high && !size_low)
{
size_high = info.nFileSizeHigh;
size_low = info.nFileSizeLow;
}
else if ((size_high > info.nFileSizeHigh) ||
((size_high == info.nFileSizeHigh) &&
(size_low > info.nFileSizeLow)))
{
/* We have to grow the file */
if (SetFilePointer( hFile, size_low, &size_high,
FILE_BEGIN ) == 0xffffffff) goto error;
if (!SetEndOfFile( hFile )) goto error;
}
}
else req.handle = -1;
/* Create the server object */
req.size_high = size_high;
req.size_low = ROUND_SIZE( 0, size_low );
req.protect = vprot;
CLIENT_SendRequest( REQ_CREATE_MAPPING, -1, 2,
&req, sizeof(req),
name, name ? strlen(name) + 1 : 0 );
if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ))
goto error;
/* Allocate the mapping object */
if (!(mapping = HeapAlloc( SystemHeap, 0, sizeof(*mapping) ))) goto error;
mapping->header.type = K32OBJ_MEM_MAPPED_FILE;
mapping->header.refcount = 1;
mapping->file = file;
if (!K32OBJ_AddName( &mapping->header, name )) handle = 0;
else handle = HANDLE_Alloc( PROCESS_Current(), &mapping->header,
FILE_MAP_ALL_ACCESS /*FIXME*/, inherit, reply.handle );
K32OBJ_DecCount( &mapping->header );
SetLastError(0); /* Last error value is relevant. (see the start of fun) */
return handle;
error:
if (reply.handle != -1) CLIENT_CloseHandle( reply.handle );
if (file) K32OBJ_DecCount( file );
if (mapping) HeapFree( SystemHeap, 0, mapping );
return 0;
}
/***********************************************************************
* CreateFileMapping32W (KERNEL32.47)
* See CreateFileMapping32A
*/
HANDLE32 WINAPI CreateFileMapping32W( HFILE32 hFile, LPSECURITY_ATTRIBUTES attr,
DWORD protect, DWORD size_high,
DWORD size_low, LPCWSTR name )
{
LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
HANDLE32 ret = CreateFileMapping32A( hFile, attr, protect,
size_high, size_low, nameA );
HeapFree( GetProcessHeap(), 0, nameA );
return ret;
}
/***********************************************************************
* OpenFileMapping32A (KERNEL32.397)
* Opens a named file-mapping object.
*
* RETURNS
* Handle: Success
* NULL: Failure
*/
HANDLE32 WINAPI OpenFileMapping32A(
DWORD access, /* [in] Access mode */
BOOL32 inherit, /* [in] Inherit flag */
LPCSTR name ) /* [in] Name of file-mapping object */
{
HANDLE32 handle = 0;
K32OBJ *obj;
struct open_named_obj_request req;
struct open_named_obj_reply reply;
int len = name ? strlen(name) + 1 : 0;
req.type = OPEN_MAPPING;
req.access = access;
req.inherit = inherit;
CLIENT_SendRequest( REQ_OPEN_NAMED_OBJ, -1, 2, &req, sizeof(req), name, len );
CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL );
if (reply.handle != -1)
{
SYSTEM_LOCK();
if ((obj = K32OBJ_FindNameType( name, K32OBJ_MEM_MAPPED_FILE )))
{
handle = HANDLE_Alloc( PROCESS_Current(), obj, access, inherit, reply.handle );
K32OBJ_DecCount( obj );
if (handle == INVALID_HANDLE_VALUE32)
handle = 0; /* must return 0 on failure, not -1 */
}
else CLIENT_CloseHandle( reply.handle );
SYSTEM_UNLOCK();
}
return handle;
}
/***********************************************************************
* OpenFileMapping32W (KERNEL32.398)
* See OpenFileMapping32A
*/
HANDLE32 WINAPI OpenFileMapping32W( DWORD access, BOOL32 inherit, LPCWSTR name)
{
LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
HANDLE32 ret = OpenFileMapping32A( access, inherit, nameA );
HeapFree( GetProcessHeap(), 0, nameA );
return ret;
}
/***********************************************************************
* VIRTUAL_DestroyMapping
*
* Destroy a FILE_MAPPING object.
*/
static void VIRTUAL_DestroyMapping( K32OBJ *ptr )
{
FILE_MAPPING *mapping = (FILE_MAPPING *)ptr;
assert( ptr->type == K32OBJ_MEM_MAPPED_FILE );
if (mapping->file) K32OBJ_DecCount( mapping->file );
ptr->type = K32OBJ_UNKNOWN;
HeapFree( SystemHeap, 0, mapping );
}
/***********************************************************************
* MapViewOfFile (KERNEL32.385)
* Maps a view of a file into the address space
*
* RETURNS
* Starting address of mapped view
* NULL: Failure
*/
LPVOID WINAPI MapViewOfFile(
HANDLE32 mapping, /* [in] File-mapping object to map */
DWORD access, /* [in] Access mode */
DWORD offset_high, /* [in] High-order 32 bits of file offset */
DWORD offset_low, /* [in] Low-order 32 bits of file offset */
DWORD count /* [in] Number of bytes to map */
) {
return MapViewOfFileEx( mapping, access, offset_high,
offset_low, count, NULL );
}
/***********************************************************************
* MapViewOfFileEx (KERNEL32.386)
* Maps a view of a file into the address space
*
* RETURNS
* Starting address of mapped view
* NULL: Failure
*/
LPVOID WINAPI MapViewOfFileEx(
HANDLE32 handle, /* [in] File-mapping object to map */
DWORD access, /* [in] Access mode */
DWORD offset_high, /* [in] High-order 32 bits of file offset */
DWORD offset_low, /* [in] Low-order 32 bits of file offset */
DWORD count, /* [in] Number of bytes to map */
LPVOID addr /* [in] Suggested starting address for mapped view */
) {
FILE_VIEW *view;
UINT32 ptr = (UINT32)-1, size = 0;
int flags = MAP_PRIVATE;
int unix_handle = -1;
struct get_mapping_info_request req;
struct get_mapping_info_reply info;
/* Check parameters */
if ((offset_low & granularity_mask) ||
(addr && ((UINT32)addr & granularity_mask)))
{
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
req.handle = HANDLE_GetServerHandle( PROCESS_Current(), handle,
K32OBJ_MEM_MAPPED_FILE, 0 /* FIXME */ );
CLIENT_SendRequest( REQ_GET_MAPPING_INFO, -1, 1, &req, sizeof(req) );
if (CLIENT_WaitSimpleReply( &info, sizeof(info), &unix_handle ))
goto error;
if (info.size_high || offset_high)
ERR(virtual, "Offsets larger than 4Gb not supported\n");
if ((offset_low >= info.size_low) ||
(count > info.size_low - offset_low))
{
SetLastError( ERROR_INVALID_PARAMETER );
goto error;
}
if (count) size = ROUND_SIZE( offset_low, count );
else size = info.size_low - offset_low;
switch(access)
{
case FILE_MAP_ALL_ACCESS:
case FILE_MAP_WRITE:
case FILE_MAP_WRITE | FILE_MAP_READ:
if (!(info.protect & VPROT_WRITE))
{
SetLastError( ERROR_INVALID_PARAMETER );
goto error;
}
flags = MAP_SHARED;
/* fall through */
case FILE_MAP_READ:
case FILE_MAP_COPY:
case FILE_MAP_COPY | FILE_MAP_READ:
if (info.protect & VPROT_READ) break;
/* fall through */
default:
SetLastError( ERROR_INVALID_PARAMETER );
goto error;
}
/* Map the file */
TRACE(virtual, "handle=%x size=%x offset=%lx\n",
handle, size, offset_low );
ptr = (UINT32)FILE_dommap( unix_handle,
addr, 0, size, 0, offset_low,
VIRTUAL_GetUnixProt( info.protect ),
flags );
if (ptr == (UINT32)-1) {
/* KB: Q125713, 25-SEP-1995, "Common File Mapping Problems and
* Platform Differences":
* Windows NT: ERROR_INVALID_PARAMETER
* Windows 95: ERROR_INVALID_ADDRESS.
* FIXME: So should we add a module dependend check here? -MM
*/
if (errno==ENOMEM)
SetLastError( ERROR_OUTOFMEMORY );
else
SetLastError( ERROR_INVALID_PARAMETER );
goto error;
}
if (!(view = VIRTUAL_CreateView( ptr, size, offset_low, 0,
info.protect, handle )))
{
SetLastError( ERROR_OUTOFMEMORY );
goto error;
}
if (unix_handle != -1) close( unix_handle );
return (LPVOID)ptr;
error:
if (unix_handle != -1) close( unix_handle );
if (ptr != (UINT32)-1) FILE_munmap( (void *)ptr, 0, size );
return NULL;
}
/***********************************************************************
* FlushViewOfFile (KERNEL32.262)
* Writes to the disk a byte range within a mapped view of a file
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI FlushViewOfFile(
LPCVOID base, /* [in] Start address of byte range to flush */
DWORD cbFlush /* [in] Number of bytes in range */
) {
FILE_VIEW *view;
UINT32 addr = ROUND_ADDR( base );
TRACE(virtual, "FlushViewOfFile at %p for %ld bytes\n",
base, cbFlush );
if (!(view = VIRTUAL_FindView( addr )))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (!cbFlush) cbFlush = view->size;
if (!msync( (void *)addr, cbFlush, MS_SYNC )) return TRUE;
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
/***********************************************************************
* UnmapViewOfFile (KERNEL32.540)
* Unmaps a mapped view of a file.
*
* NOTES
* Should addr be an LPCVOID?
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL32 WINAPI UnmapViewOfFile(
LPVOID addr /* [in] Address where mapped view begins */
) {
FILE_VIEW *view;
UINT32 base = ROUND_ADDR( addr );
if (!(view = VIRTUAL_FindView( base )) || (base != view->base))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
VIRTUAL_DeleteView( view );
return TRUE;
}
/***********************************************************************
* VIRTUAL_MapFileW
*
* Helper function to map a file to memory:
* name - file name
* [RETURN] ptr - pointer to mapped file
*/
LPVOID VIRTUAL_MapFileW( LPCWSTR name )
{
HANDLE32 hFile, hMapping;
LPVOID ptr = NULL;
hFile = CreateFile32W( name, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
if (hFile != INVALID_HANDLE_VALUE32)
{
hMapping = CreateFileMapping32A( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
CloseHandle( hFile );
if (hMapping)
{
ptr = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
CloseHandle( hMapping );
}
}
return ptr;
}