Ignore:
Timestamp:
Mar 27, 2003, 3:13:11 PM (22 years ago)
Author:
sandervl
Message:

memory map updates

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kernel32/mmap.cpp

    r9911 r9946  
    1 /* $Id: mmap.cpp,v 1.63 2003-03-06 10:44:34 sandervl Exp $ */
     1/* $Id: mmap.cpp,v 1.64 2003-03-27 14:13:10 sandervl Exp $ */
    22
    33/*
    44 * Win32 Memory mapped file & view classes
    55 *
    6  * Copyright 1999 Sander van Leeuwen (sandervl@xs4all.nl)
     6 * Copyright 1999-2003 Sander van Leeuwen (sandervl@xs4all.nl)
    77 *
    88 * NOTE: Memory mapping DOES NOT work when kernel-mode code causes
     
    3636#include <winimagepeldr.h>
    3737#include <custombuild.h>
     38#include "asmutil.h"
    3839
    3940#define DBG_LOCALLOG    DBG_mmap
    4041#include "dbglocal.h"
     42
     43
    4144
    4245//Global DLL Data
     
    4548CRITICAL_SECTION_OS2       globalmapcritsect = {0};
    4649#pragma data_seg()
    47 Win32MemMapView *Win32MemMapView::mapviews = NULL;
    4850
    4951
     
    7274//******************************************************************************
    7375Win32MemMap::Win32MemMap(HFILE hfile, ULONG size, ULONG fdwProtect, LPSTR lpszName)
    74                : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0), image(0)
     76               : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0),
     77                 image(0), pWriteBitmap(NULL)
    7578{
    7679    DosEnterCriticalSection(&globalmapcritsect);
     
    8386    mSize      = size;
    8487    mProtFlags = fdwProtect;
     88
    8589    mProcessId  = GetCurrentProcessId();
    8690
     
    96100//******************************************************************************
    97101Win32MemMap::Win32MemMap(Win32PeLdrImage *pImage, ULONG baseAddress, ULONG size)
    98                : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0), image(0)
     102               : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0),
     103                 image(0), pWriteBitmap(NULL)
    99104{
    100105    DosEnterCriticalSection(&globalmapcritsect);
     
    171176    Win32MemMapView::deleteViews(this); //delete all views of our memory mapped file
    172177
    173     dprintf(("Win32MemMap dtor: deleting view %x %x", pMapping, mSize));
     178    dprintf(("Win32MemMap dtor: deleting map %x %x", pMapping, mSize));
    174179
    175180    mapMutex.enter();
     
    190195        hMemFile = -1;
    191196    }
     197    if(pWriteBitmap) free(pWriteBitmap);
     198
    192199    mapMutex.leave();
    193200
     
    212219}
    213220//******************************************************************************
    214 // Win32MemMap::setProtFlags
    215 //
    216 // Change the protection flags of this memory map if required
    217 // This is currently only used when creating a mapping for a file which already
    218 // has an existing mapping.
    219 //
    220 //
    221 // Parameters:
    222 //
    223 //   DWORD dwNewProtect         - new protection flags
    224 //
    225 // Returns:
    226 //   TRUE                       - success
    227 //   FALSE                      - failure
    228 //
    229 // NOTE:
    230 //   We're ignoring the SEC_* flags for now
    231 //
    232 //******************************************************************************
    233 BOOL Win32MemMap::setProtFlags(DWORD dwNewProtect)
    234 {
    235     if(!(dwNewProtect & (PAGE_READWRITE|PAGE_WRITECOPY))) return TRUE; //no need for changes
    236 
    237     if(!(mProtFlags & PAGE_READWRITE))
    238     {//ok, current mapping is readonly; need to change it to readwrite
    239         mProtFlags &= ~PAGE_READONLY;
    240         mProtFlags |= PAGE_READWRITE;
    241 
    242         //that's all we need to do for now; memory mappings are readwrite by
    243         //default (see mapViewOfFile)
    244     }
    245     return TRUE;
    246 }
    247 //******************************************************************************
    248 //If memory map has no more views left, then we can safely delete it when
    249 //it's handle is closed
    250 //******************************************************************************
    251 void Win32MemMap::Release()
     221//******************************************************************************
     222int Win32MemMap::Release()
    252223{
    253224    dprintf(("Win32MemMap::Release %s (%d)", lpszMapName, referenced-1));
    254225    --referenced;
    255     if(nrMappings == 0 && referenced == 0) {
     226    if(referenced == 0) {
    256227        delete this;
    257     }
     228        return 0;
     229    }
     230    return referenced;
    258231}
    259232//******************************************************************************
     
    327300 LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset);
    328301 DWORD pageAddr         = (DWORD)lpPageFaultAddr & ~0xFFF;
    329  DWORD oldProt, newProt, nrBytesRead, size;
     302 DWORD oldProt, newProt, nrBytesRead;
    330303 int i;
    331304
     
    333306
    334307  if(image) {
    335     return image->commitPage(pageAddr, fWriteAccess);
    336   }
    337   newProt  = mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY);
     308      return image->commitPage(pageAddr, fWriteAccess);
     309  }
    338310
    339311  dprintf(("Win32MemMap::commitPage %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
    340   if(hMemFile != -1)
     312
     313  //align at page boundary
     314  offset &= ~0xFFF;
     315
     316  //If it's a write access violation and the view is readonly, then fail
     317  if(fWriteAccess) {
     318      Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr);
     319      if(view) {
     320          if(!(view->getAccessFlags() & MEMMAP_ACCESS_WRITE)) {
     321              dprintf(("Write access for a readonly view!!"));
     322              return FALSE;
     323          }
     324      }
     325      else {
     326          DebugInt3(); //can't happen
     327          return FALSE;
     328      }
     329  }
     330
     331  int faultsize = nrpages*PAGE_SIZE;
     332
     333  if(fWriteAccess)
     334  {//write access needs special care, so do that on a per page basis
     335      dprintf(("Write access -> handle only one page"));
     336      faultsize = PAGE_SIZE;
     337  }
     338
     339  offset = pageAddr - (ULONG)pMapping;
     340  if(offset + faultsize > mSize) {
     341      faultsize = mSize - offset;
     342  }
     343
     344  while(faultsize)
    341345  {
    342     int faultsize = nrpages*PAGE_SIZE;
    343 
    344     offset = pageAddr - (ULONG)pMapping;
    345     if(offset + faultsize > mSize) {
    346         faultsize = mSize - offset;
    347     }
    348 
    349     while(faultsize) {
    350346        if(VirtualQuery((LPSTR)pageAddr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
    351347            dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
     
    362358                goto fail;
    363359            }
    364             offset = pageAddr - (ULONG)pMapping;
    365             size   = memInfo.RegionSize;
    366             if(offset + size > mSize) {
    367                 dprintf(("Adjusting size from %d to %d", size, mSize - offset));
    368                 size = mSize - offset;
    369             }
    370             if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
    371                 dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
    372                 goto fail;
    373             }
    374             if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
    375                 dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
    376                 goto fail;
    377             }
    378             if(nrBytesRead != size) {
    379                 dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
    380                 goto fail;
    381             }
    382             if(newProt != PAGE_READWRITE) {
    383                 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, newProt, &oldProt) == FALSE) {
     360
     361            //Part of the memory map has been committed, so now we can change
     362            //the protection flags of the aliases (DosSetMem fails for reserved pages)
     363            updateViewPages(offset, memInfo.RegionSize, (fWriteAccess) ? PAGEVIEW_VIEW : PAGEVIEW_READONLY);
     364
     365            if(hMemFile != -1)
     366            {//now read the page(s) from disk
     367                DWORD size;
     368
     369                offset = pageAddr - (ULONG)pMapping;
     370                size   = memInfo.RegionSize;
     371                if(offset + size > mSize) {
     372                    dprintf(("Adjusting size from %d to %d", size, mSize - offset));
     373                    size = mSize - offset;
     374                }
     375                if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
     376                    dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
     377                    goto fail;
     378                }
     379                if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
     380                    dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
     381                    goto fail;
     382                }
     383                if(nrBytesRead != size) {
     384                    dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
     385                    goto fail;
     386                }
     387            }           
     388            //We set the protection flags to PAGE_READONLY, unless this pagefault
     389            //was due to a write access
     390            //This way we can track dirty pages which need to be flushed to
     391            //disk when FlushViewOfFile is called or the map is closed.
     392            if(!fWriteAccess)
     393            {
     394                if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READONLY, &oldProt) == FALSE) {
     395                    dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
    384396                    goto fail;
    385397                }
    386398            }
     399            else
     400            {//make these pages as dirty
     401                ULONG startPage  = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
     402                ULONG nrPages    = memInfo.RegionSize >> PAGE_SHIFT;
     403
     404                if(memInfo.RegionSize & 0xFFF)
     405                    nrPages++;
     406
     407                dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
     408                markDirtyPages(startPage, nrPages);
     409            }
     410        }
     411        else
     412        if(fWriteAccess)
     413        {
     414            //mark these pages as dirty
     415            ULONG startPage  = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
     416            ULONG nrPages    = memInfo.RegionSize >> PAGE_SHIFT;
     417
     418            if(memInfo.RegionSize & 0xFFF)
     419                nrPages++;
     420
     421            dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
     422            markDirtyPages(startPage, nrPages);
     423
     424            //and turn on a write access
     425            if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READWRITE, &oldProt) == FALSE) {
     426                dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
     427                goto fail;
     428            }
     429            //Now change all the aliased pages according to their view protection flags
     430            updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_VIEW);
    387431        }
    388432        faultsize -= memInfo.RegionSize;
    389433        pageAddr  += memInfo.RegionSize;
    390     }
    391   }
    392   else {
    393     ULONG sizeleft = nrpages*PAGE_SIZE;
    394     while(sizeleft)
    395     {
    396         if(VirtualQuery((LPSTR)pageAddr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
    397             dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed", pageAddr, sizeleft));
    398             goto fail;
    399         }
    400         memInfo.RegionSize = min(memInfo.RegionSize, sizeleft);
    401 
    402         if(!(memInfo.State & MEM_COMMIT))
    403             {//if it's already committed, then the app tried to write to it
    404             if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, newProt) == FALSE)
    405                 goto fail;
    406         }
    407         memInfo.RegionSize = (memInfo.RegionSize+PAGE_SIZE-1) & ~0xfff;
    408         pageAddr += memInfo.RegionSize;
    409         sizeleft -= memInfo.RegionSize;
    410     }
    411434  }
    412435
     
    420443// Win32MemMap::commitGuardPage
    421444//
    422 // Handle a guard page exception for a copy-on-write view (one page only)
     445// Handle a guard page exception
    423446//
    424447// Parameters:
     
    436459BOOL Win32MemMap::commitGuardPage(ULONG ulFaultAddr, ULONG ulOffset, BOOL fWriteAccess)
    437460{
    438     return FALSE;
    439 }
    440 //******************************************************************************
    441 // Win32MemMap::invalidatePages
    442 //
    443 // Invalidate map pages. (called by WriteFile)
     461   MEMORY_BASIC_INFORMATION memInfo;
     462   BOOL   ret;
     463   DWORD  pageAddr = ulFaultAddr & ~0xFFF;
     464   DWORD  dwNewProt, dwOldProt;
     465
     466   dprintf(("Win32MemMap::commitGuardPage %x (faultaddr %x)", pageAddr, ulFaultAddr));
     467
     468   //align at page boundary
     469   ulOffset &= ~0xFFF;
     470
     471   return TRUE;
     472fail:
     473   return FALSE;
     474}
     475//******************************************************************************
     476// Win32MemMap::updateViewPages
     477//
     478// Update the page flags of all views
    444479//
    445480// Parameters:
    446481//
    447482//   ULONG offset               - offset in memory map
    448 //   ULONG size                 - invalid range size
     483//   ULONG size                 - range size
     484//   PAGEVIEW flags             - page flags
     485//       PAGEVIEW_READONLY      -> set page flags to readonly
     486//       PAGEVIEW_VIEW          -> set page flags to view default
    449487//
    450488// Returns:
     
    453491//
    454492//******************************************************************************
    455 BOOL Win32MemMap::invalidatePages(ULONG offset, ULONG size)
    456 {
    457     return FALSE;
    458 }
    459 //******************************************************************************
    460 // Win32MemMap::unmapViewOfFile
    461 //
    462 // Unmap the view identified by addr
     493BOOL Win32MemMap::updateViewPages(ULONG offset, ULONG size, PAGEVIEW flags)
     494{
     495    Win32MemMapView **views = (Win32MemMapView **)alloca(sizeof(Win32MemMapView*)*nrMappings);
     496    if(views)
     497    {
     498        if(Win32MemMapView::findViews(this, nrMappings, views) == nrMappings)
     499        {
     500            for(int i=0;i<nrMappings;i++)
     501            {
     502                views[i]->changePageFlags(offset, size, flags);
     503            }           
     504        }
     505        else DebugInt3(); //oh, oh
     506    }
     507    return TRUE;
     508}
     509//******************************************************************************
     510// Win32MemMap::invalidatePages
     511//
     512// Invalidate map pages. (called by WriteFile)
    463513//
    464514// Parameters:
    465515//
    466 //   LPVOID addr                - view address; doesn't need to be the address
    467 //                                returned by MapViewOfFile(Ex) (as MSDN clearly says);
    468 //                                can be any address within the view range
     516//   ULONG offset               - offset in memory map
     517//   ULONG size                 - invalid range size
    469518//
    470519// Returns:
     
    473522//
    474523//******************************************************************************
    475 BOOL Win32MemMap::unmapViewOfFile(LPVOID addr)
    476 {
    477     Win32MemMapView *view;
    478 
    479     dprintf(("Win32MemMap::unmapViewOfFile %x (nrmaps=%d)", addr, nrMappings));
    480     mapMutex.enter();
    481 
    482     if(nrMappings == 0)
    483         goto fail;
    484 
    485     view = Win32MemMapView::findView((ULONG)addr);
    486     if(view == NULL)
    487         goto fail;
    488 
    489     delete view;
    490 
    491     if(--nrMappings == 0) {
    492         VirtualFree(pMapping, 0, MEM_RELEASE);
    493         pMapping = NULL;
    494     }
    495     mapMutex.leave();
    496 
    497     //if there are no more mappings left and the memory map's handle has been
    498     //closed, then delete the object
    499     if(nrMappings == 0 && referenced == 0) {
    500         delete this;
     524BOOL Win32MemMap::invalidatePages(ULONG offset, ULONG size)
     525{
     526    ULONG diff = offset & 0xFFF;
     527    BOOL ret;
     528
     529    offset &= ~0xFFF;
     530    size   += diff;
     531
     532    dprintf(("Win32MemMap::invalidatePages %x %x", offset, size));
     533    ret = VirtualFree((LPSTR)pMapping + offset, size, MEM_DECOMMIT);
     534    if(ret == FALSE) {
     535        dprintf(("ERROR: Win32MemMap::invalidatePages: VirtualFree failed!!"));
    501536    }
    502537    return TRUE;
    503 fail:
    504     mapMutex.leave();
    505     return FALSE;
    506 }
    507 //******************************************************************************
    508 //******************************************************************************
    509 LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
    510 {
    511  DWORD processId = GetCurrentProcessId();
    512 
    513     mapMutex.enter();
    514     ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
     538}
     539//******************************************************************************
     540// Win32MemMap::allocateMap
     541//
     542// Allocate memory for the map if not yet already done.
     543//
     544// Returns:
     545//   FALSE                      - success
     546//   TRUE                       - failure
     547//
     548//******************************************************************************
     549BOOL Win32MemMap::allocateMap()
     550{
    515551    ULONG fAlloc   = 0;
    516     Win32MemMapView *mapview;
    517 
    518     //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY))
    519     // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though.
    520     if(fdwAccess & ~FILE_MAP_ALL_ACCESS)
    521         goto parmfail;
    522     if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
    523         goto parmfail;
    524     if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
    525         goto parmfail;
    526 
    527     //@@@PH
    528     if (fdwAccess != FILE_MAP_ALL_ACCESS)
    529         if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
    530         goto parmfail;
    531 
    532     if(offset+size > mSize && (!(fdwAccess & FILE_MAP_WRITE) || hMemFile == -1))
    533         goto parmfail;
    534 
    535     //SvL: TODO: Doesn't work for multiple views
    536     if(offset+size > mSize) {
    537         mSize = offset+size;
    538     }
    539 
    540 //TODO: If committed, read file into memory
    541 #if 0
    542     if(mProtFlags & SEC_COMMIT)
    543         fAlloc |= MEM_COMMIT;
    544     else
    545     if(mProtFlags & SEC_RESERVE)
    546         fAlloc |= MEM_RESERVE;
    547 #else
     552
    548553    fAlloc = MEM_RESERVE;
    549 #endif
    550554
    551555    //Memory has already been allocated for executable image maps (only used internally)
     
    567571        }
    568572        if(pMapping == NULL) {
    569             dprintf(("Win32MemMap::mapFileView: VirtualAlloc %x %x %x failed!", mSize, fAlloc, memFlags));
     573            dprintf(("Win32MemMap::mapViewOfFile: VirtualAlloc %x %x failed!", mSize, fAlloc));
    570574            goto fail;
    571575        }
     
    574578            VirtualAlloc(pMapping, mSize, MEM_COMMIT, PAGE_READWRITE);
    575579        }
     580
     581        DWORD nrPages = mSize >> PAGE_SHIFT;
     582        if(mSize & 0xFFF)
     583           nrPages++;
     584
    576585        if(hMemFile != -1 && (mProtFlags & SEC_COMMIT)) {
    577             DWORD nrPages = mSize >> PAGE_SHIFT;
    578             if(mSize & 0xFFF)
    579                 nrPages++;
    580 
    581             commitPage(0, FALSE, nrPages);
    582         }
    583     }
     586            commitPage((ULONG)pMapping, 0, FALSE, nrPages);
     587        }
     588        //Allocate bitmap for all pages to keep track of write access (file maps only)
     589        //Necessary for FlushViewOfFile.
     590        if(hMemFile != -1) {
     591            int sizebitmap = nrPages/8 + 1;
     592
     593            pWriteBitmap = (char *)_smalloc(sizebitmap);
     594            if(pWriteBitmap == NULL) {
     595                DebugInt3();
     596                goto fail;
     597            }
     598            memset(pWriteBitmap, 0, sizebitmap);
     599        }
     600    }
     601    return TRUE;
     602
     603fail:
     604    return FALSE;
     605}
     606//******************************************************************************
     607// Win32MemMap::mapViewOfFile
     608//
     609// Map the view identified by addr
     610//
     611// Parameters:
     612//
     613//   ULONG size                 - size of view
     614//   ULONG offset               - offset in memory map
     615//   ULONG fdwAccess            - access flags
     616//                                FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_COPY
     617//                                FILE_MAP_ALL_ACCESS
     618//                               
     619//
     620// Returns:
     621//   <>NULL                     - success, view address
     622//   NULL                       - failure
     623//
     624//******************************************************************************
     625LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
     626{
     627    DWORD processId = GetCurrentProcessId();
     628
     629    mapMutex.enter();
     630    ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
     631    Win32MemMapView *mapview;
     632
     633    //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY))
     634    // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though.
     635    if(fdwAccess & ~FILE_MAP_ALL_ACCESS)
     636        goto parmfail;
     637    if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
     638        goto parmfail;
     639    if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY)))
     640        goto parmfail;
     641   
     642    if (fdwAccess != FILE_MAP_ALL_ACCESS)
     643        if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
     644             goto parmfail;
     645
     646    if(offset+size > mSize && (!(fdwAccess & FILE_MAP_WRITE) || hMemFile == -1))
     647        goto parmfail;
     648
     649    //SvL: TODO: Doesn't work for multiple views
     650    if(offset+size > mSize) {
     651        mSize = offset+size;
     652    }
     653
     654    if(allocateMap() == FALSE) {
     655        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
     656        goto fail;
     657    }
     658
    584659    mapview = new Win32MemMapView(this, offset, (size == 0) ? (mSize - offset) : size, fdwAccess);
    585660    if(mapview == NULL) {
     
    587662    }
    588663    if(mapview->everythingOk() == FALSE) {
    589         dprintf(("Win32MemMap::mapFileView: !mapview->everythingOk"));
     664        dprintf(("Win32MemMap::mapViewOfFile: !mapview->everythingOk"));
    590665        delete mapview;
    591666        goto fail;
    592667    }
    593     nrMappings++;
    594668    mapMutex.leave();
     669    SetLastError(ERROR_SUCCESS);
    595670    return mapview->getViewAddr();
    596671
    597672parmfail:
    598     dprintf(("Win32MemMap::mapFileView: ERROR_INVALID_PARAMETER"));
    599     SetLastError(ERROR_INVALID_PARAMETER);
     673    dprintf(("Win32MemMap::mapViewOfFile: invalid parameter (ERROR_ACCESS_DENIED)"));
     674    //NT4 SP6 returns ERROR_ACCESS_DENIED for most invalid parameters
     675    SetLastError(ERROR_ACCESS_DENIED);
    600676fail:
    601677    mapMutex.leave();
     
    603679}
    604680//******************************************************************************
     681// Win32MemMap::unmapViewOfFile
     682//
     683// Unmap the view identified by addr
     684//
     685// Parameters:
     686//
     687//   LPVOID addr                - view address; doesn't need to be the address
     688//                                returned by MapViewOfFile(Ex) (as MSDN clearly says);
     689//                                can be any address within the view range
     690//
     691// Returns:
     692//   TRUE                       - success
     693//   FALSE                      - failure
     694//
     695//******************************************************************************
     696BOOL Win32MemMap::unmapViewOfFile(LPVOID addr)
     697{
     698    Win32MemMapView *view;
     699
     700    dprintf(("Win32MemMap::unmapViewOfFile %x (nrmaps=%d)", addr, nrMappings));
     701    mapMutex.enter();
     702
     703    if(nrMappings == 0)
     704        goto fail;
     705
     706    view = Win32MemMapView::findView((ULONG)addr);
     707    if(view == NULL)
     708        goto fail;
     709
     710    delete view;
     711
     712    if(nrMappings == 0) {
     713        VirtualFree(pMapping, 0, MEM_RELEASE);
     714        pMapping = NULL;
     715    }
     716    mapMutex.leave();
     717
     718    SetLastError(ERROR_SUCCESS);
     719    return TRUE;
     720fail:
     721    mapMutex.leave();
     722    SetLastError(ERROR_INVALID_ADDRESS);
     723    return FALSE;
     724}
     725//******************************************************************************
    605726//We determine whether a page has been modified by checking it's protection flags
    606727//If the write flag is set, this means commitPage had to enable this due to a pagefault
     
    612733BOOL Win32MemMap::flushView(ULONG viewaddr, ULONG offset, ULONG cbFlush)
    613734{
    614  LPVOID lpvBase = (LPVOID)((ULONG)pMapping+offset);
    615  MEMORY_BASIC_INFORMATION memInfo;
    616  ULONG nrBytesWritten, size;
    617  int   i;
     735  ULONG nrBytesWritten, size, accessflags, oldProt;
     736  Win32MemMapView *view;
     737  int i;
     738
     739  dprintf(("Win32MemMap::flushView: %x %x", (ULONG)pMapping+offset, cbFlush));
    618740
    619741  if(image) //no flushing for image maps
    620     return TRUE;
    621 
    622   dprintf(("Win32MemMap::flushView: %x %x", lpvBase, cbFlush));
    623   if(nrMappings == 0)
    624     goto parmfail;
     742      return TRUE;
     743
     744  if(hMemFile == -1)
     745      goto success; //TODO: Return an error here?
     746
     747  if(offset > mSize)
     748      goto parmfail;
     749
     750  if(viewaddr != MMAP_FLUSHVIEW_ALL)
     751  {
     752      view = Win32MemMapView::findView(viewaddr);
     753      if(nrMappings == 0 || view == NULL) {
     754          DebugInt3(); //should never happen
     755          goto parmfail;
     756      }
     757      accessflags = view->getAccessFlags();
     758  }
     759  else {
     760      //force a flush to disk; only those pages marked dirty are flushed anyway
     761      accessflags = FILE_MAP_WRITE;
     762  }
     763  //If the view is readonly or copy on write, then the flush is ignored
     764  if(!(accessflags & MEMMAP_ACCESS_WRITE) || (accessflags & MEMMAP_ACCESS_COPYONWRITE))
     765  {
     766      dprintf(("Readonly or Copy-On-Write memory map -> ignore flush"));
     767      //this is not a failure; NT4 SP6 returns success
     768      goto success;
     769  }
    625770
    626771  if(cbFlush == 0)
    627     cbFlush = mSize;
    628 
    629   if(lpvBase < pMapping || (ULONG)lpvBase+cbFlush > (ULONG)pMapping+mSize)
    630     goto parmfail;
    631 
    632   if(mProtFlags & PAGE_READONLY)
    633     goto parmfail;
    634 
    635   if(hMemFile == -1)
    636     goto success; //TODO: Return an error here?
    637 
    638   while(cbFlush) {
    639     if(VirtualQuery((LPSTR)lpvBase, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
    640         dprintf(("Win32MemMap::flushView: VirtualQuery (%x,%x) failed for %x", lpvBase, cbFlush, (ULONG)lpvBase+i*PAGE_SIZE));
    641         goto fail;
    642     }
    643     //If a page (or range of pages) is reserved or write protected, we
    644         //won't bother flushing it to disk
    645     if(memInfo.State & MEM_COMMIT &&
    646            memInfo.AllocationProtect & (PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))
    647         {//committed and allowed for writing?
    648         offset = (ULONG)lpvBase - (ULONG)pMapping;
    649         size   = memInfo.RegionSize;
    650         if(size > cbFlush) {
    651             size = cbFlush;
    652         }
    653         dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
    654 
    655         if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
    656             dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
    657             goto fail;
    658         }
    659         if(WriteFile(hMemFile, (LPSTR)lpvBase, size, &nrBytesWritten, NULL) == FALSE) {
    660             dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)lpvBase));
    661             goto fail;
    662         }
    663         if(nrBytesWritten != size) {
    664             dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)lpvBase));
    665             goto fail;
    666         }
    667     }
    668     lpvBase = (LPVOID)((ULONG)lpvBase + memInfo.RegionSize);
    669 
    670     if(cbFlush < memInfo.RegionSize)
    671         break;
    672 
    673     cbFlush -= memInfo.RegionSize;
     772      cbFlush = mSize;
     773
     774  if(offset + cbFlush > mSize) {
     775      cbFlush -= (offset + cbFlush - mSize);
     776  }
     777
     778  //Check the write page bitmap for dirty pages and write them to disk
     779  while(cbFlush)
     780  {
     781      int startPage = offset >> PAGE_SHIFT;
     782      size = PAGE_SIZE;
     783
     784      if(isDirtyPage(startPage))
     785      {
     786          if(size > cbFlush) {
     787              size = cbFlush;
     788          }
     789          dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
     790
     791          if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
     792              dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
     793              goto fail;
     794          }
     795          if(WriteFile(hMemFile, (LPSTR)((ULONG)pMapping + offset), size, &nrBytesWritten, NULL) == FALSE) {
     796              dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)pMapping + offset));
     797              goto fail;
     798          }
     799          if(nrBytesWritten != size) {
     800              dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)pMapping + offset));
     801              goto fail;
     802          }
     803          clearDirtyPages(startPage, 1);
     804
     805          //We've just flushed the page to disk, so we need to track future writes
     806          //again; Set page to readonly (first memory map, then alias(es))
     807          if(VirtualProtect((LPVOID)((ULONG)pMapping + offset), size, PAGE_READONLY, &oldProt) == FALSE) {
     808              dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", (ULONG)pMapping + offset, size, GetLastError()));
     809              goto fail;
     810          }
     811          updateViewPages(offset, size, PAGEVIEW_READONLY);
     812      }
     813
     814      if(cbFlush < size)
     815          break;
     816
     817      cbFlush -= size;
     818      offset  += size;
    674819  }
    675820success:
     821  SetLastError(ERROR_SUCCESS);
    676822  return TRUE;
     823
    677824parmfail:
    678825  SetLastError(ERROR_INVALID_PARAMETER);
     
    724871  if(map) map->AddRef();
    725872  DosLeaveCriticalSection(&globalmapcritsect);
    726   if(!map) dprintf(("Win32MemMap::findMapByFile: couldn't find map with file handle %x", hFile));
     873  if(!map) dprintf2(("Win32MemMap::findMapByFile: couldn't find map with file handle %x", hFile));
    727874  return map;
    728875}
     
    752899void Win32MemMap::deleteAll()
    753900{
    754  Win32MemMap *map = memmaps, *nextmap;
     901 Win32MemMap *map, *nextmap;
    755902 DWORD processId = GetCurrentProcessId();
    756903
    757904  //delete all maps created by this process
    758905  DosEnterCriticalSection(&globalmapcritsect);
     906
     907startdeleteviews:
     908  map = memmaps;
    759909  while(map) {
    760     nextmap = map->next;
    761     if(map->getProcessId() == processId) {
    762         //Delete map directly for executable images (only used internally)
    763         if(map->getImage()) {
    764             delete map;
    765         }
    766         else {
    767             delete map;
    768         }
    769     }
    770     else {
    771         //delete all views created by this process for this map
    772         Win32MemMapView::deleteViews(map);
    773     }
    774     map = nextmap;
     910      map->AddRef(); //make sure it doesn't get deleted
     911
     912      //delete all views created by this process for this map
     913      Win32MemMapView::deleteViews(map);
     914
     915      nextmap = map->next;
     916
     917      //map->Release can delete multiple objects (duplicate memory map), so make
     918      //sure our nextmap pointer remains valid by increasing the refcount
     919      if(nextmap) nextmap->AddRef();
     920      map->Release();
     921
     922      if(nextmap && nextmap->Release() == 0) {
     923          //oops, nextmap was just deleted and is no longer valid
     924          //can't continue from here, so let's start again
     925          dprintf(("oops, nextmap is invalid -> start again (1)"));
     926          goto startdeleteviews;
     927      }
     928
     929      map = nextmap;
     930  }
     931startdelete:
     932  map = memmaps;
     933  while(map) {
     934      nextmap = map->next;
     935      if(map->getProcessId() == processId)
     936      {
     937          //delete map can delete multiple objects (duplicate memory map), so make
     938          //sure our nextmap pointer remains valid by increasing the refcount
     939          if(nextmap) nextmap->AddRef();
     940          delete map;
     941
     942          if(nextmap && nextmap->Release() == 0) {
     943              //oops, nextmap was just deleted and is no longer valid
     944              //can't continue from here, so let's start again
     945              dprintf(("oops, nextmap is invalid -> start again (2)"));
     946              goto startdelete;
     947          }
     948      }
     949      map = nextmap;
    775950  }
    776951  DosLeaveCriticalSection(&globalmapcritsect);
    777952}
    778953//******************************************************************************
    779 //******************************************************************************
    780 Win32MemMapView::Win32MemMapView(Win32MemMap *map, ULONG offset, ULONG size,
    781                                  ULONG fdwAccess)
    782 {
    783  LPVOID           viewaddr = (LPVOID)((ULONG)map->getMappingAddr()+offset);
    784  ULONG            accessAttr = 0;
    785  Win32MemMapView *tmpview  = mapviews;
    786 
    787     errorState      = 0;
    788     mParentMap      = map;
    789     mSize           = size;
    790     mOffset         = offset;
    791     mProcessId      = GetCurrentProcessId();
    792     pShareViewAddr  = NULL;
    793 
    794     switch(fdwAccess) {
    795     case FILE_MAP_READ:
    796         accessAttr  = PAG_READ;
    797         mfAccess    = MEMMAP_ACCESS_READ;
    798         break;
    799     case FILE_MAP_ALL_ACCESS:
    800     case FILE_MAP_WRITE:
    801     case FILE_MAP_WRITE|FILE_MAP_READ:
    802     case FILE_MAP_COPY:
    803         accessAttr  = (PAG_READ|PAG_WRITE);
    804         mfAccess    = MEMMAP_ACCESS_READ | MEMMAP_ACCESS_WRITE;
    805         break;
    806     }
    807     //Named file mappings from other processes are always shared;
    808     //map into our address space
    809     if(map->getMemName() != NULL && map->getProcessId() != mProcessId)
    810     {
    811         //shared memory map, so map it into our address space
    812         if(OSLibDosGetNamedSharedMem((LPVOID *)&viewaddr, map->getMemName()) != OSLIB_NOERROR) {
    813             dprintf(("new OSLibDosGetNamedSharedMem FAILED"));
    814             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
    815             errorState = 1;
    816             return;
    817         }
    818         pShareViewAddr = viewaddr;
    819     }
    820 
    821     //view == memory mapping for executable images (only used internally)
    822     if(map->getImage()) {
    823         pMapView = map->getMappingAddr();
    824     }
    825     else {
    826         if(OSLibDosAliasMem(viewaddr, size, &pMapView, accessAttr) != OSLIB_NOERROR) {
    827             dprintf(("new OSLibDosAliasMem FAILED"));
    828             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
    829             errorState = 1;
    830             return;
    831         }
    832     }
    833     dprintf(("Win32MemMapView::Win32MemMapView: created %x (alias for %x), size %d", pMapView, viewaddr, size));
    834 
    835     DosEnterCriticalSection(&globalmapcritsect);
    836     if(tmpview == NULL || tmpview->getViewAddr() > pMapView) {
    837         next     = mapviews;
    838         mapviews = this;
    839     }
    840     else {
    841         while(tmpview->next) {
    842             if(tmpview->next->getViewAddr() > pMapView) {
    843                 break;
    844             }
    845             tmpview = tmpview->next;
    846         }
    847         next          = tmpview->next;
    848         tmpview->next = this;
    849     }
    850     DosLeaveCriticalSection(&globalmapcritsect);
    851 }
    852 //******************************************************************************
    853 //******************************************************************************
    854 Win32MemMapView::~Win32MemMapView()
    855 {
    856     if(errorState != 0)
    857         return;
    858 
    859     dprintf(("Win32MemMapView dtor: deleting view %x %x", mOffset, mSize));
    860 
    861     if(mfAccess & MEMMAP_ACCESS_WRITE)
    862         mParentMap->flushView(MMAP_FLUSHVIEW_ALL, mOffset, mSize);
    863 
    864     //Don't free memory for executable image map views (only used internally)
    865     if(!mParentMap->getImage())
    866         OSLibDosFreeMem(pMapView);
    867 
    868     if(pShareViewAddr) {
    869         OSLibDosFreeMem(pShareViewAddr);
    870     }
    871 
    872     DosEnterCriticalSection(&globalmapcritsect);
    873     Win32MemMapView *view = mapviews;
    874 
    875     if(view == this) {
    876         mapviews = next;
    877     }
    878     else {
    879         while(view->next) {
    880             if(view->next == this)
    881                 break;
    882             view = view->next;
    883         }
    884         if(view->next) {
    885                 view->next = next;
    886         }
    887         else    dprintf(("Win32MemMapView::~Win32MemMapView: map not found!! (%x)", this));
    888     }
    889     DosLeaveCriticalSection(&globalmapcritsect);
    890 }
    891 //******************************************************************************
    892 //******************************************************************************
    893 void Win32MemMapView::deleteViews(Win32MemMap *map)
    894 {
    895   DosEnterCriticalSection(&globalmapcritsect);
    896   Win32MemMapView *view = mapviews, *nextview;
    897 
    898   if(view != NULL) {
    899     while(view) {
    900         nextview = view->next;
    901         if(view->getParentMap() == map)
    902         {
    903             DosLeaveCriticalSection(&globalmapcritsect);
    904             delete view;
    905             DosEnterCriticalSection(&globalmapcritsect);
    906         }
    907         view = nextview;
    908     }
    909   }
    910   DosLeaveCriticalSection(&globalmapcritsect);
    911 }
    912 //******************************************************************************
    913 //******************************************************************************
    914 // Win32MemMap::findMapByView
    915 //
    916 // Find the map of the view that contains the specified starting address
    917 // and has the specified access type
     954// Win32MemMapView::markDirtyPages
     955//
     956// Mark pages as dirty (changed) in the write page bitmap
    918957//
    919958// Parameters:
    920959//
    921 //   ULONG address              - view address
    922 //   ULONG *offset              - address of ULONG that receives the offset
    923 //                                in the returned memory map
    924 //   ULONG accessType           - access type:
    925 //                                MEMMAP_ACCESS_READ
    926 //                                MEMMAP_ACCESS_WRITE
    927 //                                MEMMAP_ACCESS_EXECUTE
    928 //
    929 // Returns:
    930 //   <> NULL                    - success, address of parent map object
    931 //   NULL                       - failure
    932 //
    933 //******************************************************************************
    934 //******************************************************************************
    935 Win32MemMap *Win32MemMapView::findMapByView(ULONG address,
    936                                             ULONG *offset,
    937                                             ULONG accessType)
    938 {
    939   Win32MemMap *map = NULL;
    940   ULONG ulOffset;
    941 
    942   if(mapviews == NULL) return NULL;
    943 
    944   DosEnterCriticalSection(&globalmapcritsect);
    945   Win32MemMapView *view = mapviews;
    946   ULONG ulViewAddr;
    947 
    948   if(!offset)  offset = &ulOffset;
    949 
    950   *offset = 0;
    951 
    952   if(view != NULL)
    953   {
    954     do
    955     {
    956       ulViewAddr = (ULONG)view->getViewAddr();
    957 
    958       // if ulViewAddr is > address, we've exhausted
    959       // the sorted list already and can abort search.
    960       if(ulViewAddr <= address)
    961       {
    962         if(ulViewAddr + view->getSize() > address &&
    963            view->getAccessFlags() >= accessType)
    964         {
    965           *offset = view->getOffset() + (address - ulViewAddr);
    966           goto success;
    967         }
    968 
    969         // Not found yet, continue search with next map
    970         view = view->next;
    971       }
    972       else
    973       {
    974           // list is exhausted, abort loop
    975           view = NULL;
    976       }
    977     }
    978     while(view);
    979 
    980     //failure if we get here
    981     view = NULL;
    982   }
    983 success:
    984 #ifdef DEBUG
    985   if(view && !view->getParentMap()->isImageMap())
    986       dprintf(("findMapByView %x %x -> %x off %x",
    987                address,
    988                accessType,
    989                view->getViewAddr(),
    990                *offset));
    991 #endif
    992 
    993   if(view) {
    994       map = view->getParentMap();
    995       if(map) map->AddRef();
    996   }
    997 
    998   DosLeaveCriticalSection(&globalmapcritsect);
    999 
    1000   return map;
    1001 }
    1002 //******************************************************************************
    1003 // Win32MemMap::findView
    1004 //
    1005 // Find the view that contains the specified starting address
     960//   int startpage              - start page
     961//   int nrpages                - number of pages
     962//
     963//
     964//******************************************************************************
     965void Win32MemMap::markDirtyPages(int startpage, int nrpages)
     966{
     967    if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
     968
     969    for(int i=startpage;i<startpage+nrpages;i++) {
     970        set_bit(i, pWriteBitmap);
     971    }
     972}
     973//******************************************************************************
     974// Win32MemMapView::clearDirtyPages
     975//
     976// Mark pages as clean in the write page bitmap
    1006977//
    1007978// Parameters:
    1008979//
    1009 //   LPVOID address             - view address
    1010 //
    1011 // Returns:
    1012 //   <> NULL                    - success, address view object
    1013 //   NULL                       - failure
    1014 //
    1015 //******************************************************************************
    1016 Win32MemMapView *Win32MemMapView::findView(ULONG address)
    1017 {
    1018   ULONG ulViewAddr;
    1019 
    1020   DosEnterCriticalSection(&globalmapcritsect);
    1021   Win32MemMapView *view = mapviews;
    1022 
    1023   if(view != NULL) {
    1024     while(view) {
    1025         ulViewAddr = (ULONG)view->getViewAddr();
    1026         if(ulViewAddr <= address && ulViewAddr + view->getSize() > address)
    1027         {
    1028             break;
    1029         }
    1030         view = view->next;
    1031     }
    1032   }
    1033   DosLeaveCriticalSection(&globalmapcritsect);
    1034   return view;
    1035 }
    1036 //******************************************************************************
    1037 //******************************************************************************
     980//   int startpage              - start page
     981//   int nrpages                - number of pages
     982//
     983//
     984//******************************************************************************
     985void Win32MemMap::clearDirtyPages(int startpage, int nrpages)
     986{
     987    if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
     988
     989    for(int i=startpage;i<startpage+nrpages;i++) {
     990        clear_bit(i, pWriteBitmap);
     991    }
     992}
     993//******************************************************************************
     994//******************************************************************************
Note: See TracChangeset for help on using the changeset viewer.