mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3263 lines
64 KiB
3263 lines
64 KiB
/***********************************************************************
|
|
* Microsoft (R) 32-Bit Incremental Linker
|
|
*
|
|
* Copyright (C) Microsoft Corp 1992-95. All rights reserved.
|
|
*
|
|
* File: bufio.cpp
|
|
*
|
|
* File Comments:
|
|
*
|
|
* Functions which are common to the COFF Linker/Librarian/Dumper.
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "link.h"
|
|
|
|
#include <share.h>
|
|
|
|
#define MAX_WRITEABLE_FILES 16
|
|
static PFI pfiCloseOnBadExit[MAX_WRITEABLE_FILES];
|
|
|
|
// value in buffer map table signifying something
|
|
// has previously been to this range
|
|
#define pbufPreviousWrite ((PBUF) 0x1)
|
|
|
|
// default size of writeable files
|
|
#define cbDefaultFileSize (4096L * 1024L) // 4Meg
|
|
|
|
/* buffered i/o routines */
|
|
STATIC INT BufferedOpen(PFI, INT, INT, BOOL);
|
|
STATIC INT BufferedCloseHard(PFI);
|
|
STATIC LONG BufferedSeek(PFI, LONG, INT);
|
|
STATIC DWORD BufferedWrite(PFI, const void *, DWORD);
|
|
STATIC DWORD BufferedRead(PFI, PVOID, DWORD);
|
|
STATIC INT BufferedChSize (PFI, LONG);
|
|
|
|
/* mapped i/o routines */
|
|
INT MappedOpen(PFI, LONG, BOOL, DWORD *);
|
|
INT MappedCloseHard(PFI);
|
|
LONG MappedSeek(PFI, LONG, INT, DWORD *);
|
|
STATIC DWORD MappedWrite(PFI, const void *, DWORD);
|
|
STATIC DWORD MappedRead(PFI, PVOID, DWORD);
|
|
BOOL ExtendMapView(PFI pfi, DWORD ibNeeded, DWORD *);
|
|
BOOL FMapPfi(PFI pfi, BOOL, DWORD *pdwrc);
|
|
STATIC INT MappedChSize(PFI, LONG);
|
|
|
|
/* logical file descriptor manipulators */
|
|
STATIC PFI LookupCachedFiles(const char *, INT, INT, BOOL *);
|
|
STATIC PFI PfiClosedCached(const char *);
|
|
STATIC PFI PfiNew(VOID);
|
|
STATIC PFI PfiAlloc(VOID);
|
|
STATIC VOID TransitionPFI(PFI, PPFI, PPFI, PPFI, PPFI, BOOL);
|
|
STATIC VOID GrowMapTable(PFI);
|
|
STATIC VOID CloseSoft(PFI);
|
|
|
|
/* buffer manipulators */
|
|
STATIC BOOL FDeactivateBuf(PBUF);
|
|
STATIC BOOL FActivateBuf(PFI, PBUF);
|
|
STATIC PBUF PbufNew(VOID);
|
|
STATIC PBUF PbufAlloc(PFI, LONG);
|
|
STATIC PBUF PbufLRU(VOID);
|
|
STATIC VOID MoveToHeadLRU(PBUF);
|
|
STATIC VOID SlidePbufCur(PFI);
|
|
STATIC VOID FlushBuffer(PBUF);
|
|
STATIC VOID ReadpbufCur(PFI);
|
|
|
|
/* debug routines */
|
|
#if DBG
|
|
STATIC BOOL FBufListCheck(VOID);
|
|
STATIC BOOL FNoDupBuf(PBUF);
|
|
STATIC BOOL FPfiInList(PFI, PFI);
|
|
STATIC BOOL FPfiCheck(VOID);
|
|
#endif // DBG
|
|
|
|
/* buffer containers */
|
|
static PBUF pbufLRUHead; // LRU buffer chain head (MRU)
|
|
static PBUF pbufLRUTail; // LRU buffer chain tail (LRU)
|
|
static PBUF pbufFree; // free file buffers
|
|
static PBUF pbufActive; // active file buffers
|
|
|
|
/* statistics */
|
|
static DWORD crereads; // number of buffer re-reads
|
|
static DWORD cfiTot; // total number of logical file descriptors
|
|
static DWORD cfiCacheClosed; // current size of closed/cached pool
|
|
static DWORD cfiCacheClosedMax; // maximum size of closed/cached pool
|
|
|
|
/* logical file descriptor containers */
|
|
static PFI pfiFree; // free logical file handles
|
|
static PFI pfiOpen; // active logical file handles
|
|
static PFI pfiClosedHead; // head of list of closed file handles
|
|
static PFI pfiClosedTail; // tail of list of closed file handles
|
|
PFI *rgpfi; // map of cached file handles
|
|
|
|
/* error handlers */
|
|
static BOOL fMappedIO;
|
|
|
|
/* State transitions for logical file descriptors. */
|
|
/* There are 3 states, open, closed/cached, and free. */
|
|
/* The following macros are atomic state transitions. It is likely */
|
|
/* cheaper to change states with inline linked list code, however */
|
|
/* it becomes difficult to track file handles and the probability of */
|
|
/* loosing them increases. */
|
|
#define TR_Free_To_Open(pfi) { \
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(" - pfi = 0x%p free to open (%s)\n", \
|
|
(pfi), (pfi)->szFileName)); \
|
|
TransitionPFI((pfi), &pfiFree, NULL, &pfiOpen, NULL, 0); }
|
|
|
|
#define TR_Open_To_Free(pfi) { \
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(" - pfi = 0x%p open to free (%s)\n", \
|
|
(pfi), (pfi)->szFileName)); \
|
|
TransitionPFI((pfi), &pfiOpen, NULL, &pfiFree, NULL, 0); }
|
|
|
|
#define TR_Open_To_Cache(pfi) { \
|
|
cfiCacheClosed++; \
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(" - pfi = 0x%p open to cached (%s)\n", \
|
|
(pfi), (pfi)->szFileName)); \
|
|
TransitionPFI((pfi), &pfiOpen, NULL, &pfiClosedHead, &pfiClosedTail, 1); }
|
|
|
|
#define TR_Cache_To_Open(pfi) { \
|
|
cfiCacheClosed--; \
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(" - pfi = 0x%p cached to open (%s)\n", \
|
|
(pfi), (pfi)->szFileName)); \
|
|
TransitionPFI((pfi), &pfiClosedHead, &pfiClosedTail, &pfiOpen, NULL, 0); }
|
|
|
|
#define TR_Cache_To_Free(pfi) { \
|
|
cfiCacheClosed--; \
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(" - pfi = 0x%p cached to open (%s)\n", \
|
|
(pfi), (pfi)->szFileName)); \
|
|
TransitionPFI((pfi), &pfiClosedHead, &pfiClosedTail, &pfiFree, NULL, 0); }
|
|
|
|
|
|
VOID
|
|
FileInit(
|
|
DWORD cfiNT,
|
|
DWORD cfiCacheableNT,
|
|
DWORD cfiTNT,
|
|
DWORD cfiCacheableTNT)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the buffered i/o package.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HINSTANCE hLib;
|
|
DWORD cfi;
|
|
DWORD cfiCacheable;
|
|
DWORD ifi;
|
|
int i;
|
|
|
|
// Check for TNT. Don't use mapped I/O under TNT.
|
|
|
|
hLib = GetModuleHandle("kernel32.dll");
|
|
|
|
fMappedIO = (hLib == 0) || (GetProcAddress(hLib, "IsTNT") == 0);
|
|
|
|
DBEXEC_REL(DB_NO_FILE_MAP, fMappedIO = FALSE);
|
|
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(fMappedIO ?
|
|
"Using NT I/O parameters (file mapping)\n" :
|
|
"Using TNT I/O parameters (no mapping, fewer handles)\n"));
|
|
|
|
if (fMappedIO) {
|
|
cfi = cfiNT;
|
|
cfiCacheable = cfiCacheableNT;
|
|
} else {
|
|
cfi = cfiTNT;
|
|
cfiCacheable = cfiCacheableTNT;
|
|
}
|
|
|
|
// Allocate logical file handle map
|
|
|
|
rgpfi = (PFI *) PvAllocZ(cfi * sizeof(PFI));
|
|
|
|
// Allocate buffered file handles
|
|
|
|
for (ifi = 0; ifi < cfi; ifi++) {
|
|
PFI pfi;
|
|
|
|
pfi = PfiNew();
|
|
pfi->ifi = ifi;
|
|
|
|
if (pfiFree == NULL) {
|
|
pfiFree = pfi;
|
|
} else {
|
|
pfi->pfiNext = pfiFree;
|
|
pfiFree = pfi;
|
|
}
|
|
|
|
rgpfi[ifi] = pfi;
|
|
}
|
|
|
|
// Set the maximum cache size
|
|
|
|
cfiCacheClosedMax = cfiCacheable;
|
|
|
|
// Initialize the CloseOnBadExit array
|
|
|
|
for (i = 1; i < MAX_WRITEABLE_FILES; i++) {
|
|
pfiCloseOnBadExit[i] = NULL;
|
|
}
|
|
|
|
#if DBG
|
|
assert(FPfiCheck());
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
BufferedIOInit (
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize for buffered i/o.
|
|
UNDONE: we should just alloc buffers as needed
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
static BOOL fBufIoInit = FALSE;
|
|
DWORD ibuf;
|
|
|
|
if (fBufIoInit)
|
|
return;
|
|
else
|
|
fBufIoInit = TRUE;
|
|
|
|
// Allocate buffers and add them to the free list
|
|
|
|
for (ibuf = 0; ibuf < cbufTot; ibuf++) {
|
|
PBUF pbuf;
|
|
|
|
pbuf = PbufNew();
|
|
|
|
if (pbufFree == NULL) {
|
|
pbufFree = pbuf;
|
|
} else {
|
|
pbuf->pbufNext = pbufFree;
|
|
pbufFree = pbuf;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
STATIC PFI
|
|
PfiNew(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new logical file handle.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfi;
|
|
|
|
pfi = (PFI) PvAllocZ(sizeof(FI));
|
|
|
|
cfiTot++;
|
|
|
|
return(pfi);
|
|
}
|
|
|
|
STATIC VOID
|
|
GrowMapTable(
|
|
PFI pfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Grow a the file buffer map table and set *pl to the new size. This
|
|
routine assumes pfi->cbMap % cbIOBuf == 0.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG cbufNew;
|
|
LONG cbufOld;
|
|
|
|
// get the old buffer table size
|
|
cbufOld = pfi->cbMap / cbIOBuf;
|
|
|
|
// double the size of the old table
|
|
cbufNew = cbufOld << 1;
|
|
|
|
assert(cbufNew > cbufOld);
|
|
|
|
// grow the table
|
|
pfi->rgpbuf = (PPBUF) PvRealloc(pfi->rgpbuf, cbufNew * sizeof(PBUF));
|
|
|
|
// zero the enties
|
|
memset(&(pfi->rgpbuf[cbufOld]), '\0', sizeof(PBUF) * (cbufNew - cbufOld));
|
|
|
|
// set the new size
|
|
pfi->cbMap = cbufNew * cbIOBuf;
|
|
}
|
|
|
|
|
|
STATIC PFI
|
|
PfiAlloc(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a logical file descriptor.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfi;
|
|
|
|
if (!pfiFree) {
|
|
// no free logical file descriptors, hard close
|
|
pfi = pfiClosedHead;
|
|
TR_Cache_To_Free(pfi);
|
|
|
|
if (pfi->flags & FI_Mapped) {
|
|
MappedCloseHard(pfi);
|
|
} else {
|
|
BufferedCloseHard(pfi);
|
|
}
|
|
}
|
|
|
|
pfi = pfiFree;
|
|
assert(pfi);
|
|
|
|
TR_Free_To_Open(pfi);
|
|
|
|
return(pfi);
|
|
}
|
|
|
|
|
|
STATIC PFI
|
|
PfiClosedCached (
|
|
const char *szFileName)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check for a cached pfi in the closed chain.
|
|
|
|
Arguments:
|
|
|
|
szFileName - file name to find a cached pfi for
|
|
|
|
Return Value:
|
|
|
|
pointer to a logical file descriptor if found, NULL otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfiLast = pfiClosedHead;
|
|
PFI pfi = pfiClosedHead;
|
|
|
|
assert(szFileName);
|
|
|
|
// loop through the closed chain loooking for the file
|
|
while (pfi) {
|
|
if (!strcmp(pfi->szFileName, szFileName)) {
|
|
// found file, move from closed list to open list
|
|
TR_Cache_To_Open(pfi);
|
|
assert(pfi->flags & FI_Closed);
|
|
pfi->flags &= ~FI_Closed;
|
|
return(pfi);
|
|
}
|
|
pfiLast = pfi;
|
|
pfi = pfi->pfiNext;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
INT
|
|
FileChSize (
|
|
INT fd,
|
|
LONG cbSizeNew)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change the file size.
|
|
|
|
Arguments:
|
|
|
|
fd - file descriptor
|
|
|
|
cbSizeNew - new size of file
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(rgpfi[fd]);
|
|
assert(rgpfi[fd]->flags & FI_Write);
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
if (cbSizeNew == rgpfi[fd]->cbSoFar) {
|
|
// no change in size
|
|
return(0);
|
|
}
|
|
|
|
if (rgpfi[fd]->flags & FI_Mapped) {
|
|
return(MappedChSize(rgpfi[fd], cbSizeNew));
|
|
}
|
|
|
|
return(BufferedChSize(rgpfi[fd], cbSizeNew));
|
|
}
|
|
|
|
STATIC INT
|
|
MappedChSize (
|
|
PFI pfi,
|
|
LONG cbSizeNew)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change the file size. Same as _chsize() in the crt. This only operates
|
|
on buffered files, the code will assert on mapped files.
|
|
|
|
Arguments:
|
|
|
|
fd - file descriptor
|
|
|
|
cbSizeNew - new size of file
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL f = TRUE;
|
|
DWORD dwErr = 0;
|
|
|
|
assert(pfi->flags & FI_Mapped);
|
|
|
|
if (cbSizeNew > pfi->cbMapView) {
|
|
// Grow file
|
|
|
|
if (!ExtendMapView(pfi, cbSizeNew, &dwErr)) {
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, cbSizeNew);
|
|
}
|
|
} else {
|
|
// Shrink file
|
|
|
|
if (pfi->ibCur > cbSizeNew) {
|
|
pfi->ibCur = cbSizeNew;
|
|
}
|
|
}
|
|
|
|
pfi->cbSoFar = cbSizeNew;
|
|
|
|
return(0);
|
|
}
|
|
|
|
STATIC INT
|
|
BufferedChSize (
|
|
PFI pfi,
|
|
LONG cbSizeNew)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change the file size. Same as _chsize() in the crt. This only operates
|
|
on buffered files, the code will assert on mapped files.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
cbSizeNew - new size of file
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbuf;
|
|
LONG ibuf;
|
|
LONG cbuf;
|
|
#if DBG
|
|
BOOL f;
|
|
#endif // DBG
|
|
|
|
assert(!(pfi->flags & FI_Mapped));
|
|
|
|
if (cbSizeNew > pfi->cbSoFar) {
|
|
// grow file
|
|
while (pfi->cbMap <= cbSizeNew) {
|
|
GrowMapTable(pfi);
|
|
}
|
|
|
|
assert(cbSizeNew <= pfi->cbMap);
|
|
} else {
|
|
// shrink file
|
|
cbuf = pfi->cbMap >> cshiftBuf;
|
|
|
|
// make sure pfi->cbMap is cbIOBuf aligned
|
|
assert((cbuf * cbIOBuf) == pfi->cbMap);
|
|
|
|
for(ibuf = cbSizeNew >> cshiftBuf; ibuf < cbuf; ibuf++) {
|
|
pbuf = pfi->rgpbuf[ibuf];
|
|
if (pbuf != NULL && pbuf != pbufPreviousWrite) {
|
|
assert(pbuf->flags & BUF_Active);
|
|
#if DBG
|
|
f =
|
|
#endif // DBG
|
|
FDeactivateBuf(pbuf);
|
|
assert(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
pfi->cb = cbSizeNew;
|
|
pfi->cbSoFar = cbSizeNew;
|
|
|
|
return(_chsize(pfi->fd, cbSizeNew));
|
|
}
|
|
|
|
|
|
STATIC PFI
|
|
LookupCachedFiles (
|
|
const char *szFileName,
|
|
INT flags,
|
|
INT /* mode */,
|
|
OUT BOOL *pfNewPfi
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the cached list for the file
|
|
|
|
Arguments:
|
|
|
|
szFileName - A pointer to a file name.
|
|
|
|
flags - open flags
|
|
|
|
mode - open mode
|
|
|
|
pfNewPfi - TRUE if new pfi had to be allocated
|
|
|
|
Return Value:
|
|
|
|
PFI
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfi;
|
|
|
|
// get a file handle
|
|
if (!(pfi = PfiClosedCached(szFileName))) {
|
|
pfi = PfiAlloc();
|
|
|
|
*pfNewPfi = 1;
|
|
|
|
pfi->szFileName = SzDup(szFileName);
|
|
|
|
pfi->flags = 0;
|
|
pfi->cbSoFar = 0;
|
|
pfi->hFile = NULL;
|
|
pfi->pvMapView = NULL;
|
|
pfi->cbMapView = 0;
|
|
pfi->ibCur = 0;
|
|
pfi->MapAddr = 0;
|
|
|
|
// set read flags
|
|
if ((flags & 0x000f) == O_RDONLY ||
|
|
flags & O_RDWR) {
|
|
pfi->flags |= FI_Read;
|
|
}
|
|
|
|
// set write flags
|
|
if (flags & O_WRONLY ||
|
|
flags & O_RDWR) {
|
|
pfi->flags |= FI_Write;
|
|
}
|
|
|
|
// set the create flag
|
|
if (flags & O_CREAT) {
|
|
pfi->flags |= FI_Create;
|
|
}
|
|
}
|
|
|
|
assert(pfi);
|
|
return(pfi);
|
|
}
|
|
|
|
INT
|
|
FileOpen (
|
|
const char *szFileName,
|
|
INT flags,
|
|
INT mode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a file. Prints an error if can't open file. Do rudementary
|
|
file handle caching.
|
|
|
|
Arguments:
|
|
|
|
szFileName - A pointer to a file name.
|
|
|
|
flags - open flags
|
|
|
|
mode - open mode
|
|
|
|
Return Value:
|
|
|
|
Physical file descriptor.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fNewPfi = 0;
|
|
PFI pfi;
|
|
INT i = -1;
|
|
DWORD dwErr = 0;
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
pfi = LookupCachedFiles(szFileName, flags, mode, &fNewPfi);
|
|
assert(pfi);
|
|
|
|
if (fMappedIO) {
|
|
i = MappedOpen(pfi, flags, fNewPfi, &dwErr);
|
|
}
|
|
|
|
if (i == -1) {
|
|
BufferedIOInit();
|
|
BufferedOpen(pfi, flags, mode, fNewPfi);
|
|
}
|
|
|
|
return(pfi->ifi);
|
|
}
|
|
|
|
DWORD
|
|
RoundUpToNextPowerOf2 (
|
|
DWORD cb,
|
|
DWORD cbHigh
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (cb < cbHigh) {
|
|
return cbHigh;
|
|
}
|
|
|
|
return RoundUpToNextPowerOf2(cb, (cbHigh << 1));
|
|
}
|
|
|
|
INT
|
|
FileOpenMapped (
|
|
const char *szFileName,
|
|
INT flags,
|
|
INT mode,
|
|
DWORD *pMapAddr,
|
|
DWORD *pcb,
|
|
DWORD *pdwErr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens and maps a file to a specific address.
|
|
|
|
Arguments:
|
|
|
|
szFileName - name of file.
|
|
|
|
flags - open flags
|
|
|
|
mode - open mode
|
|
|
|
pMapAddr - address to map the file to. On return has actual address.
|
|
|
|
pcb - pointer to file size. On return has free space.
|
|
|
|
pdwErr - ptr to store error code in case of failure
|
|
|
|
Return Value:
|
|
|
|
file handle.
|
|
|
|
--*/
|
|
{
|
|
BOOL fNewPfi = 0;
|
|
PFI pfi;
|
|
INT i = -1;
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
// got to have mapped i/o
|
|
if (!fMappedIO) {
|
|
return i;
|
|
}
|
|
|
|
// lookup the cache first
|
|
pfi = LookupCachedFiles(szFileName, flags, mode, &fNewPfi);
|
|
assert(pfi);
|
|
pfi->MapAddr = *pMapAddr;
|
|
pfi->cbSoFar = *pcb;
|
|
|
|
// set the file size
|
|
if (flags & O_CREAT) {
|
|
// 256K is starting size of map (bufio.h)
|
|
|
|
pfi->cbMapView = cbInitialILKMapSize;
|
|
} else {
|
|
// Round up to 256K/512K/...etc for an open
|
|
|
|
pfi->cbMapView = RoundUpToNextPowerOf2(pfi->cbSoFar, cbInitialILKMapSize);
|
|
}
|
|
|
|
// Open the file
|
|
|
|
i = MappedOpen(pfi, flags, fNewPfi, pdwErr);
|
|
|
|
if (i == -1) {
|
|
// failed to map to specified address;
|
|
// insuffcient disk space OR couldn't allocate map at req. addr.
|
|
|
|
TR_Open_To_Free(pfi);
|
|
|
|
if (!_access(pfi->szFileName, 0)) {
|
|
_unlink(pfi->szFileName);
|
|
}
|
|
|
|
FreePv(pfi->szFileName);
|
|
|
|
return i;
|
|
}
|
|
|
|
*pcb = (flags & O_CREAT) ? pfi->cbMapView : pfi->cbMapView - pfi->cbSoFar;
|
|
*pMapAddr = pfi->MapAddr = (DWORD)pfi->pvMapView;
|
|
|
|
return(pfi->ifi);
|
|
}
|
|
|
|
|
|
STATIC INT
|
|
BufferedOpen (
|
|
PFI pfi,
|
|
INT flags,
|
|
INT mode,
|
|
BOOL fNewPfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a file. Prints an error if can't open file. Do rudementary
|
|
file handle caching.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
flags - open flags
|
|
|
|
mode - open mode
|
|
|
|
Return Value:
|
|
|
|
Logical file descriptor.
|
|
|
|
--*/
|
|
|
|
{
|
|
struct _stat statFile;
|
|
LONG cbuf;
|
|
LONG ibuf;
|
|
|
|
assert(pfi);
|
|
|
|
// if no cached logical file handle
|
|
if (fNewPfi) {
|
|
// open the file
|
|
if ((pfi->fd = _sopen(pfi->szFileName, flags,
|
|
((flags & (_O_RDWR|_O_WRONLY)) ? _SH_DENYRW : _SH_DENYWR), mode)) == -1)
|
|
{
|
|
PFI pfiTmp;
|
|
|
|
if (pfi == pfiOpen) {
|
|
pfiOpen = pfi->pfiNext;
|
|
cfiTot--;
|
|
} else {
|
|
for (pfiTmp = pfiOpen; pfiTmp; pfiTmp = pfiTmp->pfiNext) {
|
|
if (pfiTmp->pfiNext == pfi) {
|
|
pfiTmp->pfiNext = pfi->pfiNext;
|
|
cfiTot--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_doserrno == ERROR_DISK_FULL) {
|
|
Fatal(pfi->szFileName, DISKFULL);
|
|
}
|
|
|
|
Fatal(NULL, CANTOPENFILE, pfi->szFileName);
|
|
}
|
|
|
|
// we don't support O_APPEND
|
|
assert(!(flags & O_APPEND));
|
|
|
|
pfi->cbSoFar = 0L;
|
|
|
|
// get size of file, or set to default
|
|
// note this is !FI_Write because an opened .exe is readable, but
|
|
// its size is not defined yet
|
|
if (!(pfi->flags & FI_Write) ||
|
|
((pfi->flags & (FI_Read | FI_Write)) &&
|
|
!(pfi->flags & FI_Create))) {
|
|
if (_stat(pfi->szFileName, &statFile) == -1) {
|
|
Fatal(NULL, CANTOPENFILE, pfi->szFileName);
|
|
}
|
|
|
|
// Test for files of size 0 (mapped open will fall through in this case)
|
|
if (!statFile.st_size) {
|
|
// UNDONE: Why allocate before calling Fatal?
|
|
|
|
pfi->rgpbuf = (PPBUF) PvAllocZ(sizeof(PBUF));
|
|
|
|
Fatal(pfi->szFileName, BAD_FILE);
|
|
}
|
|
|
|
pfi->cb = statFile.st_size;
|
|
pfi->cbSoFar = statFile.st_size;
|
|
} else {
|
|
pfi->cb = cbDefaultFileSize;
|
|
}
|
|
|
|
cbuf = (pfi->cb + cbIOBuf) / cbIOBuf;
|
|
pfi->cbMap = cbuf * cbIOBuf;
|
|
|
|
// allocate file buffer map table
|
|
pfi->rgpbuf = (PPBUF) PvAllocZ(cbuf * sizeof(PBUF));
|
|
|
|
// blast previous writes into buffer map table if required
|
|
if ((pfi->flags & (FI_Read | FI_Write)) & !(pfi->flags & FI_Create)) {
|
|
for (ibuf = 0; ibuf < cbuf; ibuf++) {
|
|
pfi->rgpbuf[ibuf] = pbufPreviousWrite;
|
|
}
|
|
}
|
|
}
|
|
|
|
BufferedSeek(pfi, 0L, SEEK_SET);
|
|
assert(rgpfi[pfi->ifi]->ifi == rgpfi[pfi->ifi]->pbufCur->ifi);
|
|
return(0);
|
|
}
|
|
|
|
INT
|
|
FileClose (
|
|
INT fd,
|
|
BOOL fUnCache)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes a file. Prints an error if can't close file.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
fUnCache - hard close the file
|
|
|
|
Return Value:
|
|
|
|
Same as close();
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfi;
|
|
INT i;
|
|
|
|
pfi = rgpfi[fd];
|
|
assert(pfi);
|
|
assert(!(pfi->flags & FI_Closed));
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
// either close the file hard if specified or if writable or
|
|
// virtually close the file and cache the logical and physical handle
|
|
if (fUnCache || (pfi->flags & FI_Write)) {
|
|
TR_Open_To_Free(pfi);
|
|
|
|
if (pfi->flags & FI_Mapped) {
|
|
i = MappedCloseHard(pfi);
|
|
} else {
|
|
i = BufferedCloseHard(pfi);
|
|
}
|
|
} else {
|
|
TR_Open_To_Cache(pfi);
|
|
CloseSoft(pfi);
|
|
i = 0;
|
|
}
|
|
|
|
return(i);
|
|
}
|
|
|
|
STATIC INT
|
|
BufferedCloseHard (
|
|
PFI pfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close logical and physical file handles for a file. Does not manipulate
|
|
PFI pools.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
Return Value:
|
|
|
|
Same as close();
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbufN;
|
|
PBUF pbuf;
|
|
INT i;
|
|
#if DBG
|
|
BOOL f;
|
|
#endif // DBG
|
|
|
|
assert(pfi);
|
|
|
|
// delete active buffers from file
|
|
pbuf = pbufActive;
|
|
while (pbuf) {
|
|
assert(pbuf->flags & BUF_Active);
|
|
pbufN = pbuf->pbufNext;
|
|
if (pbuf->ifi == pfi->ifi) {
|
|
if (pbuf == pfi->pbufCur) {
|
|
pfi->pbufCur->flags &= ~BUF_Current;
|
|
}
|
|
|
|
#if DBG
|
|
f =
|
|
#endif //DBG
|
|
FDeactivateBuf(pbuf);
|
|
assert(f);
|
|
|
|
}
|
|
pbuf = pbufN;
|
|
}
|
|
|
|
// close the physical handle
|
|
if ((i = _close(pfi->fd)) == -1) {
|
|
Fatal(NULL, CANTCLOSEFILE, pfi->szFileName);
|
|
}
|
|
|
|
assert(pfi->szFileName);
|
|
FreePv(pfi->szFileName);
|
|
assert(pfi->rgpbuf);
|
|
FreePv(pfi->rgpbuf);
|
|
|
|
// return the result of the low level close
|
|
return(i);
|
|
}
|
|
|
|
STATIC VOID
|
|
CloseSoft (
|
|
PFI pfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close a logical file handle and cache it.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
Return Value:
|
|
|
|
Same as close();
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfiToFree;
|
|
|
|
assert(pfi);
|
|
assert(!(pfi->flags & FI_Closed));
|
|
|
|
// set logical file descriptor to closed
|
|
pfi->flags |= FI_Closed;
|
|
|
|
if (!(pfi->flags & FI_Mapped)) {
|
|
// set the current pbuf to not current and remove it from the
|
|
// logical file descriptor, this is safe since when the file
|
|
// is re-opened (logically) there is an immediate seek to offset
|
|
// 0 which can potentially reactivate or reallocate the buffer,
|
|
// this also frees up the buffer to be reallocated if need be
|
|
assert(pfi->pbufCur);
|
|
assert(pfi->pbufCur->flags & BUF_Current);
|
|
pfi->pbufCur->flags &= ~BUF_Current;
|
|
pfi->pbufCur = NULL;
|
|
}
|
|
|
|
// if cache is full, toss the last guy out
|
|
if (cfiCacheClosed == cfiCacheClosedMax) {
|
|
pfiToFree = pfiClosedHead;
|
|
assert(pfiToFree);
|
|
assert(pfiToFree);
|
|
TR_Cache_To_Free(pfiToFree);
|
|
|
|
(pfiToFree->flags & FI_Mapped) ?
|
|
MappedCloseHard(pfiToFree) :
|
|
BufferedCloseHard(pfiToFree);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FileCloseAll (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes all cached handles.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfiT;
|
|
PFI pfi;
|
|
|
|
#if DBG
|
|
assert(FPfiCheck());
|
|
#endif
|
|
// hard close all open files
|
|
pfi = pfiOpen;
|
|
while (pfi) {
|
|
// move open PFI to free PFI pool
|
|
assert(!(pfi->flags & FI_Closed));
|
|
pfiT = pfi->pfiNext;
|
|
TR_Open_To_Free(pfi);
|
|
|
|
(pfi->flags & FI_Mapped) ?
|
|
MappedCloseHard(pfi) :
|
|
BufferedCloseHard(pfi);
|
|
|
|
pfi = pfiT;
|
|
}
|
|
|
|
// hard close all closed/cached files
|
|
pfi = pfiClosedHead;
|
|
while (pfi) {
|
|
assert(pfi->flags & FI_Closed);
|
|
pfiT = pfi->pfiNext;
|
|
// move closed/cached PFI to free PFI pool
|
|
TR_Cache_To_Free(pfi);
|
|
|
|
(pfi->flags & FI_Mapped) ?
|
|
MappedCloseHard(pfi) :
|
|
BufferedCloseHard(pfi);
|
|
|
|
pfi = pfiT;
|
|
}
|
|
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("Buffer summary\n"));
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("--------------\n"));
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("size of buffers = %lu\n", cbIOBuf));
|
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("buffer re-reads = %lu\n", crereads));
|
|
#if DBG
|
|
assert(FPfiCheck());
|
|
#endif
|
|
}
|
|
|
|
STATIC VOID
|
|
FlushBuffer (
|
|
PBUF pbuf)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flush a buffer.
|
|
|
|
Arguments:
|
|
|
|
pbuf - buffer to flush
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG cb;
|
|
PFI pfi;
|
|
|
|
assert(pbuf);
|
|
|
|
// get the buffered file descriptor
|
|
pfi = rgpfi[pbuf->ifi];
|
|
assert(pfi);
|
|
|
|
if (pbuf->flags & BUF_Dirty) {
|
|
// calculate how much to flush
|
|
cb = pbuf->ibLast - pbuf->ibStart;
|
|
|
|
DBEXEC(DB_IO_FLUSH,
|
|
Trans_LOG(LOG_FlushBuffer, pfi->fd, pbuf->ibCur, cb, 0, NULL));
|
|
|
|
// seek to buffers beginning
|
|
if (_lseek(pfi->fd, pbuf->ibStart, SEEK_SET) == -1L) {
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, pbuf->ibStart);
|
|
}
|
|
|
|
// flush buffer
|
|
if (_write(pfi->fd, pbuf->rgbBuf, cb) != cb) {
|
|
if (_doserrno == ERROR_DISK_FULL) {
|
|
Fatal(pfi->szFileName, DISKFULL);
|
|
}
|
|
|
|
Fatal(pfi->szFileName, CANTWRITEFILE, _tell(pfi->fd));
|
|
}
|
|
|
|
// set the buffer to written to
|
|
pfi->rgpbuf[pbuf->ibStart >> cshiftBuf] = pbufPreviousWrite;
|
|
|
|
// set file size so far
|
|
if ((pfi->flags & FI_Write) &&
|
|
(pbuf->ibLast > pfi->cbSoFar)) {
|
|
pfi->cbSoFar = pbuf->ibLast;
|
|
}
|
|
|
|
} else {
|
|
// remove the buffer from the buffer map table
|
|
if (pbuf->flags & BUF_PreviousWrite) {
|
|
pfi->rgpbuf[pbuf->ibStart >> cshiftBuf] = pbufPreviousWrite;
|
|
} else {
|
|
pfi->rgpbuf[pbuf->ibStart >> cshiftBuf] = NULL;
|
|
}
|
|
}
|
|
#if DBG
|
|
assert(FBufListCheck());
|
|
#endif
|
|
}
|
|
|
|
STATIC LONG
|
|
BufferedSeek (
|
|
PFI pfi,
|
|
LONG ib,
|
|
INT origin
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Seeks a file that is buffered. Prints an error if can't seek file.
|
|
|
|
Arguments:
|
|
|
|
pfi - buffered file descriptor
|
|
|
|
ib - Number of bytes to seek.
|
|
|
|
origin - SEEK_SET, SEEK_CUR, or SEEK_END.
|
|
|
|
Return Value:
|
|
|
|
Same as lseek().
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG ibFile;
|
|
PBUF pbuf;
|
|
|
|
assert(pfi);
|
|
|
|
#if DBG
|
|
assert(FBufListCheck());
|
|
#endif
|
|
|
|
DBEXEC(DB_IO_SEEK,
|
|
Trans_LOG(LOG_BufSeek, pfi->ifi, ib, 0, origin, NULL));
|
|
|
|
// calculate SEEK_SET offset
|
|
switch (origin) {
|
|
case SEEK_SET: // already non-relative offset
|
|
ibFile = ib;
|
|
break;
|
|
|
|
case SEEK_CUR: // relative offset
|
|
assert(pfi->pbufCur);
|
|
// base off of current buffer
|
|
ibFile = pfi->pbufCur->ibCur + ib;
|
|
break;
|
|
|
|
default:
|
|
case SEEK_END:
|
|
ibFile = pfi->cbSoFar + ib;
|
|
break;
|
|
}
|
|
|
|
// check if we need to grow the buffer map table
|
|
while (ibFile >= pfi->cbMap) {
|
|
GrowMapTable(pfi);
|
|
}
|
|
|
|
// get buffer entry
|
|
assert(ibFile <= pfi->cbMap);
|
|
pbuf = pfi->rgpbuf[ibFile >> cshiftBuf];
|
|
|
|
// set current buffer to no buffer
|
|
if (pfi->pbufCur) {
|
|
assert(pfi->pbufCur->flags & BUF_Current);
|
|
pfi->pbufCur->flags &= ~BUF_Current;
|
|
pfi->pbufCur = NULL;
|
|
}
|
|
|
|
if ((DWORD) pbuf <= 1) {
|
|
// no buffer or previously buffered, attempt to allocate a buffer
|
|
pfi->pbufCur = PbufAlloc(pfi, ibFile);
|
|
assert(!(pfi->pbufCur->flags & BUF_Current));
|
|
pfi->pbufCur->flags |= BUF_Current;
|
|
assert(pfi->pbufCur);
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
}
|
|
|
|
// if the file has been previously written to at this range
|
|
// then read in buffer
|
|
if (pbuf == pbufPreviousWrite) {
|
|
|
|
ReadpbufCur(pfi);
|
|
crereads++;
|
|
|
|
DBEXEC(DB_BUFVERBOSE,
|
|
DBPRINT("re-read @%0x\n", pfi->pbufCur->ibStart));
|
|
|
|
// set the buffer to previously written out
|
|
pfi->pbufCur->flags |= BUF_PreviousWrite;
|
|
}
|
|
|
|
// found an entry in the buffer map table
|
|
if ((DWORD) pbuf > 1) {
|
|
pfi->pbufCur = pbuf;
|
|
assert(!(pbuf->flags & BUF_Current));
|
|
pfi->pbufCur->flags |= BUF_Current;
|
|
}
|
|
|
|
// set offset in buffer
|
|
assert(ibFile >= pfi->pbufCur->ibStart);
|
|
pfi->pbufCur->ibCur = ibFile;
|
|
pfi->pbufCur->pbCur = pfi->pbufCur->rgbBuf +
|
|
(ibFile - pfi->pbufCur->ibStart);
|
|
|
|
// update highest valid buffer access on writes
|
|
if ((pfi->flags & FI_Write) &&
|
|
(pfi->pbufCur->ibCur > pfi->pbufCur->ibLast)) {
|
|
pfi->pbufCur->ibLast = pfi->pbufCur->ibCur;
|
|
}
|
|
assert((ibFile - pfi->pbufCur->ibStart) < cbIOBuf);
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
#if DBG
|
|
assert(FBufListCheck());
|
|
#endif
|
|
|
|
// update LRU list
|
|
MoveToHeadLRU(pfi->pbufCur);
|
|
#if DBG
|
|
assert(FBufListCheck());
|
|
#endif
|
|
return(ibFile);
|
|
}
|
|
|
|
LONG
|
|
FileSeekEx (
|
|
INT fd,
|
|
LONG ib,
|
|
INT origin,
|
|
DWORD *pdwErr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Seeks a file. Prints an error if can't seek file.
|
|
|
|
Arguments:
|
|
|
|
fd - fd in which file was open.
|
|
|
|
ib - Number of bytes to seek.
|
|
|
|
origin - SEEK_SET, SEEK_CUR, or SEEK_END.
|
|
|
|
pdwErr- ptr to store error code in case of an error.
|
|
|
|
Return Value:
|
|
|
|
Same as lseek().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
if (rgpfi[fd]->flags & FI_Mapped) {
|
|
return(MappedSeek(rgpfi[fd], ib, origin, pdwErr));
|
|
}
|
|
|
|
return(BufferedSeek(rgpfi[fd], ib, origin));
|
|
}
|
|
|
|
// the error code of is of no interest
|
|
LONG
|
|
FileSeek (
|
|
INT fd,
|
|
LONG ib,
|
|
INT origin
|
|
)
|
|
{
|
|
DWORD dwErr = 0;
|
|
|
|
return FileSeekEx(fd, ib, origin, &dwErr);
|
|
}
|
|
|
|
LONG
|
|
FileLength (
|
|
INT fd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a file's length in bytes.
|
|
|
|
Arguments:
|
|
|
|
fd - handle in which file was open.
|
|
|
|
Return Value:
|
|
|
|
Number of bytes in file.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG cbFile;
|
|
LONG ibHere;
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
// save current file pointer
|
|
ibHere = FileSeek(fd, 0L, SEEK_CUR);
|
|
|
|
// seek to the end of the file
|
|
cbFile = FileSeek(fd, 0L, SEEK_END);
|
|
|
|
// if where we are now is not the end of the file,
|
|
// set us back to where we were originally
|
|
if (cbFile != ibHere) {
|
|
FileSeek(fd, ibHere, SEEK_SET);
|
|
}
|
|
|
|
// return the size
|
|
return(cbFile);
|
|
}
|
|
|
|
STATIC DWORD
|
|
BufferedRead (
|
|
PFI pfi,
|
|
PVOID pvBuf,
|
|
DWORD cb)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read file which is buffered.
|
|
|
|
Arguments:
|
|
|
|
pvBuf - buffer to read into
|
|
|
|
cb - number of bytes to read
|
|
|
|
Return Value:
|
|
|
|
number of bytes read
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG cbLeft;
|
|
LONG cbRead;
|
|
|
|
PVOID pvT = pvBuf;
|
|
|
|
assert(pfi);
|
|
assert(pfi->flags & FI_Read);
|
|
assert(pvBuf);
|
|
assert(pfi->pbufCur);
|
|
|
|
DBEXEC(DB_IO_READ,
|
|
Trans_LOG(LOG_BufRead, pfi->ifi, pfi->pbufCur->ibCur, cb, 0, NULL));
|
|
|
|
// check for reading past end of file
|
|
if ((pfi->pbufCur->ibCur + cb) > (DWORD)pfi->cbMap) {
|
|
Fatal(pfi->szFileName, CANTREADFILE, pfi->pbufCur->ibCur + cb);
|
|
}
|
|
|
|
// perform the read
|
|
for(cbLeft = cb;
|
|
cbLeft;
|
|
cbLeft -= cbRead, pvBuf = (PVOID) ((PCHAR) pvBuf + cbRead)) {
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
|
|
// check if buffer is random, if so read in the buffer
|
|
if (pfi->pbufCur->flags & BUF_Random) {
|
|
ReadpbufCur(pfi);
|
|
}
|
|
|
|
// bytes to read is the minimum of the total bytes to read or the
|
|
// bytes in the buffer
|
|
cbRead = __min(cbLeft, pfi->pbufCur->ibEnd - pfi->pbufCur->ibCur);
|
|
|
|
assert((LONG) (pfi->pbufCur->pbCur) <
|
|
(LONG) ((LONG) (pfi->pbufCur->rgbBuf) + cbIOBuf));
|
|
assert((LONG) pvBuf < (LONG) ((LONG) pvT + (LONG) cb));
|
|
assert((cbRead <= pfi->pbufCur->ibLast - pfi->pbufCur->ibStart) ||
|
|
(pfi->pbufCur->flags & BUF_PreviousWrite) ||
|
|
(pfi->pbufCur->flags & BUF_Active));
|
|
|
|
assert((DWORD) cbRead <= cb);
|
|
memcpy(pvBuf, pfi->pbufCur->pbCur, (DWORD) cbRead);
|
|
|
|
// adjust buffer pointers
|
|
pfi->pbufCur->ibCur += cbRead;
|
|
pfi->pbufCur->pbCur += cbRead;
|
|
|
|
if (pfi->pbufCur->ibCur == pfi->pbufCur->ibEnd) {
|
|
SlidePbufCur(pfi);
|
|
}
|
|
}
|
|
#if DBG
|
|
assert(FBufListCheck());
|
|
#endif
|
|
return(cb);
|
|
}
|
|
|
|
DWORD
|
|
FileRead (
|
|
INT fd,
|
|
PVOID pvBuf,
|
|
DWORD cb)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads a file. Prints an error if can't read file.
|
|
|
|
Arguments:
|
|
|
|
fd - fd in which file was open.
|
|
|
|
pvBuf - Location to receive bytes.
|
|
|
|
cb - Number of bytes to read into Buffer.
|
|
|
|
Return Value:
|
|
|
|
Same as read().
|
|
|
|
--*/
|
|
|
|
{
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
if (rgpfi[fd]->flags & FI_Mapped) {
|
|
return(MappedRead(rgpfi[fd], pvBuf, cb));
|
|
}
|
|
|
|
return(BufferedRead(rgpfi[fd], pvBuf, cb));
|
|
}
|
|
|
|
DWORD
|
|
FileTell (
|
|
INT fd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Give position of file pointer.
|
|
|
|
Arguments:
|
|
|
|
fd - file handle
|
|
|
|
Return Value:
|
|
|
|
position of file pointer
|
|
|
|
--*/
|
|
|
|
{
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
if (rgpfi[fd]->flags & FI_Mapped) {
|
|
return(rgpfi[fd]->ibCur);
|
|
} else {
|
|
assert(rgpfi[fd]);
|
|
assert(rgpfi[fd]->pbufCur);
|
|
return(rgpfi[fd]->pbufCur->ibCur);
|
|
}
|
|
}
|
|
|
|
STATIC DWORD
|
|
BufferedWrite (
|
|
PFI pfi,
|
|
const void *pvBuf,
|
|
DWORD cb)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to a buffer if possible, otherwise write to low io.
|
|
|
|
Arguments:
|
|
|
|
pvBuf - buffer for bytes to write
|
|
|
|
cb - count of bytes
|
|
|
|
Return Value:
|
|
|
|
number of bytes written
|
|
|
|
--*/
|
|
|
|
{
|
|
const void *pvT;
|
|
LONG cbLeft;
|
|
LONG cbWrite;
|
|
|
|
assert(pfi);
|
|
assert(pfi->flags & FI_Write);
|
|
assert(pfi->pbufCur);
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
assert(pvBuf);
|
|
|
|
// writes of zero bytes don't do anything
|
|
if (!cb) {
|
|
return(cb);
|
|
}
|
|
|
|
// check if we need to grow the buffer map table
|
|
while ((pfi->pbufCur->ibCur + (LONG) cb) >= pfi->cbMap) {
|
|
GrowMapTable(pfi);
|
|
}
|
|
|
|
DBEXEC(DB_IO_WRITE,
|
|
Trans_LOG(LOG_BufWrite, pfi->ifi, pfi->pbufCur->ibCur, cb, 0, NULL));
|
|
|
|
pvT = pvBuf;
|
|
|
|
// mark the buffer as dirty
|
|
pfi->pbufCur->flags |= BUF_Dirty;
|
|
|
|
// perform the write
|
|
for(cbLeft = cb;
|
|
cbLeft;
|
|
cbLeft -= cbWrite, pvBuf = (PVOID) ((PCHAR) pvBuf + cbWrite)) {
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
|
|
// bytes to write is the minimum of the bytes to write or the
|
|
// bytes that fit in the buffer
|
|
cbWrite = __min(cbLeft, pfi->pbufCur->ibEnd - pfi->pbufCur->ibCur);
|
|
|
|
assert((LONG) (pfi->pbufCur->pbCur) <
|
|
(LONG) ((LONG) (pfi->pbufCur->rgbBuf) + cbIOBuf));
|
|
assert((LONG) pvBuf < (LONG) ((LONG) pvT + (LONG) cb));
|
|
|
|
assert((DWORD) cbWrite <= cb);
|
|
memcpy(pfi->pbufCur->pbCur, pvBuf, (DWORD) cbWrite);
|
|
|
|
// adjust buffer pointers
|
|
pfi->pbufCur->ibCur += cbWrite;
|
|
pfi->pbufCur->pbCur += cbWrite;
|
|
|
|
// update highest valid buffer write
|
|
if (pfi->pbufCur->ibCur > pfi->pbufCur->ibLast) {
|
|
pfi->pbufCur->ibLast = pfi->pbufCur->ibCur;
|
|
}
|
|
|
|
if (pfi->pbufCur->ibCur == pfi->pbufCur->ibEnd) {
|
|
SlidePbufCur(pfi);
|
|
}
|
|
|
|
if (cbLeft) {
|
|
pfi->pbufCur->flags |= BUF_Dirty;
|
|
}
|
|
}
|
|
#if DBG
|
|
assert(FBufListCheck());
|
|
#endif
|
|
|
|
return(cb);
|
|
}
|
|
|
|
STATIC VOID
|
|
ReadpbufCur(
|
|
PFI pfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read in a buffer from disk.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG cbReRead;
|
|
LONG ibSeek;
|
|
LONG cbRead;
|
|
|
|
assert(pfi);
|
|
assert(pfi->pbufCur);
|
|
assert(pfi->pbufCur->ibStart <= pfi->cbSoFar);
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
|
|
// seek to buffer region on disk
|
|
if ((ibSeek = (LONG)_lseek(pfi->fd,
|
|
pfi->pbufCur->ibStart, SEEK_SET)) == -1L) {
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, ibSeek);
|
|
}
|
|
|
|
assert(ibSeek == pfi->pbufCur->ibStart);
|
|
|
|
// calculate bytes to read
|
|
cbReRead = __min(cbIOBuf, pfi->cbSoFar - pfi->pbufCur->ibStart);
|
|
assert((pfi->pbufCur->ibStart + cbReRead) <= pfi->cbSoFar);
|
|
|
|
// read in buffer
|
|
if ((cbRead = _read(pfi->fd, pfi->pbufCur->rgbBuf,
|
|
(DWORD) cbReRead)) != cbReRead) {
|
|
Fatal(pfi->szFileName, CANTREADFILE, _tell(pfi->fd));
|
|
}
|
|
|
|
// set the buffer to not containing random bits
|
|
pfi->pbufCur->flags &= ~BUF_Random;
|
|
}
|
|
|
|
STATIC VOID
|
|
SlidePbufCur(
|
|
PFI pfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Slide down the current buffer in a file one region. Write the contents
|
|
of the buffer to disk if the buffer was written to and the file was
|
|
opened as writeable. Read the contents of the buffer from disk if the
|
|
file was readable.
|
|
|
|
Arguments:
|
|
|
|
pfi - file descriptor of file to slide current buffer in
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbuf;
|
|
LONG ibuf;
|
|
PPBUF rgpbuf;
|
|
#if DBG
|
|
BOOL f;
|
|
#endif // DBG
|
|
|
|
assert(pfi);
|
|
assert(pfi->pbufCur);
|
|
assert(pfi->rgpbuf);
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
|
|
DBEXEC(
|
|
DB_BUFVERBOSE,
|
|
DBPRINT(" - slide pbuf = 0x%p from 0x%8lX to 0x%8lX in %s\n",
|
|
pfi->pbufCur, pfi->pbufCur->ibStart,
|
|
pfi->pbufCur->ibStart + 0x1000, pfi->szFileName));
|
|
|
|
// get buffer table entry
|
|
ibuf = pfi->pbufCur->ibStart >> cshiftBuf;
|
|
assert(((ibuf + 1) * cbIOBuf) < (pfi->cbMap));
|
|
|
|
// get the buffer table
|
|
rgpbuf = pfi->rgpbuf;
|
|
|
|
assert(rgpbuf[ibuf]);
|
|
pbuf = rgpbuf[ibuf + 1];
|
|
|
|
if ((pbuf == NULL) || (pbuf == pbufPreviousWrite)) {
|
|
// flush the current buffer, if the buffer is not dirty
|
|
// FlushBuffer will not write anything
|
|
FlushBuffer(pfi->pbufCur);
|
|
|
|
// move the buffer along in the buffer table
|
|
rgpbuf[ibuf + 1] = pfi->pbufCur;
|
|
|
|
// set the buffers new range
|
|
pfi->pbufCur->ibStart += cbIOBuf;
|
|
pfi->pbufCur->ibEnd += cbIOBuf;
|
|
pfi->pbufCur->ibLast = pfi->pbufCur->ibStart;
|
|
|
|
// set the buffer to containing random bits
|
|
// HELP: this might have to be done for !FI_Write only
|
|
if (!(pfi->flags & FI_Write)) {
|
|
pfi->pbufCur->flags |= BUF_Random;
|
|
}
|
|
|
|
// if the file has been previously written to at this range
|
|
// then read in buffer
|
|
if (pbuf == pbufPreviousWrite) {
|
|
ReadpbufCur(pfi);
|
|
crereads++;
|
|
|
|
DBEXEC(DB_BUFVERBOSE,
|
|
DBPRINT("re-read @%0x\n", pfi->pbufCur->ibStart));
|
|
|
|
// set the buffer to previously written out
|
|
pfi->pbufCur->flags |= BUF_PreviousWrite;
|
|
}
|
|
} else {
|
|
// we ran into an existing buffer, deactivate the current
|
|
// buffer, this causes the buffer to be flushed and updates
|
|
// the buffer table
|
|
|
|
assert(pfi->pbufCur->flags & BUF_Current);
|
|
pfi->pbufCur->flags &= ~BUF_Current;
|
|
#if DBG
|
|
f =
|
|
#endif //DBG
|
|
FDeactivateBuf(pfi->pbufCur);
|
|
assert(f);
|
|
// set the current buffer to the one we ran into,
|
|
// this assumes there will be no more writes to the
|
|
// previous file offset in this buffer
|
|
assert(((ibuf + 1) * cbIOBuf) <= pfi->cbMap);
|
|
pfi->pbufCur = rgpbuf[ibuf + 1];
|
|
assert(!(pfi->pbufCur->flags & BUF_Current));
|
|
pfi->pbufCur->flags |= BUF_Current;
|
|
}
|
|
|
|
// set the current offsets to the beginning of the buffer
|
|
pfi->pbufCur->ibCur = pfi->pbufCur->ibStart;
|
|
pfi->pbufCur->pbCur = pfi->pbufCur->rgbBuf;
|
|
assert(pfi->ifi == pfi->pbufCur->ifi);
|
|
}
|
|
|
|
DWORD
|
|
FileWrite (
|
|
INT fd,
|
|
const void *pvBuf,
|
|
DWORD cb)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a file. Prints an error if can't write file.
|
|
|
|
Arguments:
|
|
|
|
fd - file handle
|
|
|
|
pvBuf - location to receive bytes
|
|
|
|
cb - number of bytes to write from pvBuf
|
|
|
|
Return Value:
|
|
|
|
Same as write().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (fCtrlCSignal) {
|
|
BadExitCleanup();
|
|
}
|
|
|
|
if (rgpfi[fd]->flags & FI_Mapped) {
|
|
return(MappedWrite(rgpfi[fd], pvBuf, cb));
|
|
}
|
|
|
|
return(BufferedWrite(rgpfi[fd], pvBuf, cb));
|
|
}
|
|
|
|
STATIC VOID
|
|
MoveToHeadLRU(
|
|
PBUF pbuf
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Move a buffer to the head of the LRU chain.
|
|
|
|
Arguments:
|
|
|
|
pbuf - buffer to move to the head of the LRU chain
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fActive = 0;
|
|
PBUF pbufT;
|
|
|
|
assert(pbuf);
|
|
|
|
for (pbufT = pbufLRUHead;
|
|
pbufT && (pbufT != pbuf);
|
|
pbufT = pbufT->pbufLRURight);
|
|
|
|
// delete old links if the buffer is active
|
|
if (pbufT) {
|
|
assert(pbuf->flags & BUF_Active);
|
|
if (pbuf->pbufLRULeft) {
|
|
// left entry is not head
|
|
pbuf->pbufLRULeft->pbufLRURight = pbuf->pbufLRURight;
|
|
} else {
|
|
// left entry is head
|
|
pbufLRUHead = pbuf->pbufLRURight;
|
|
}
|
|
|
|
if (pbuf->pbufLRURight) {
|
|
// right entry is not tail
|
|
pbuf->pbufLRURight->pbufLRULeft = pbuf->pbufLRULeft;
|
|
} else {
|
|
// right entry is tail
|
|
pbufLRUTail = pbuf->pbufLRULeft;
|
|
}
|
|
}
|
|
|
|
// set the new LRU links
|
|
if ((!pbufLRUHead) && (!pbufLRUTail)) {
|
|
// add to empty list case
|
|
pbufLRUHead = pbuf;
|
|
pbufLRUTail = pbuf;
|
|
pbuf->pbufLRURight = NULL;
|
|
pbuf->pbufLRULeft = NULL;
|
|
} else {
|
|
// add to non-empty list
|
|
pbuf->pbufLRURight = pbufLRUHead;
|
|
pbuf->pbufLRULeft = NULL;
|
|
pbufLRUHead->pbufLRULeft = pbuf;
|
|
pbufLRUHead = pbuf;
|
|
}
|
|
}
|
|
|
|
STATIC PBUF
|
|
PbufLRU(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the LRU seeked buffer which isn't any PFI's current buffer.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Pointer a buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbuf;
|
|
|
|
assert(pbufLRUHead);
|
|
assert(pbufLRUTail);
|
|
|
|
// find the buffer
|
|
pbuf = pbufLRUTail;
|
|
while (pbuf) {
|
|
if (!(pbuf->flags & BUF_Current)) {
|
|
break;
|
|
} else {
|
|
pbuf = pbuf->pbufLRULeft;
|
|
}
|
|
}
|
|
|
|
assert(pbuf);
|
|
|
|
if (pbuf == pbufLRUTail) {
|
|
pbufLRUTail = pbuf->pbufLRULeft;
|
|
} else {
|
|
pbuf->pbufLRURight->pbufLRULeft = pbuf->pbufLRULeft;
|
|
}
|
|
|
|
if (pbuf == pbufLRUHead) {
|
|
pbufLRUHead = pbuf->pbufLRURight;
|
|
} else {
|
|
pbuf->pbufLRULeft->pbufLRURight = pbuf->pbufLRURight;
|
|
}
|
|
|
|
pbuf->pbufLRURight = NULL;
|
|
pbuf->pbufLRULeft = NULL;
|
|
|
|
return(pbuf);
|
|
}
|
|
|
|
STATIC BOOL
|
|
FActivateBuf(
|
|
PFI pfi,
|
|
PBUF pbuf
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Activate a buffer by removing the first buffer from the free list,
|
|
inserting it to the active list and setting its status to active.
|
|
|
|
Arguments:
|
|
|
|
pfi - file descriptor to bind buffer to
|
|
|
|
pbuf - buffer to activate
|
|
|
|
Return Value:
|
|
|
|
!0 on success, 0 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pbuf);
|
|
assert(!(pbuf->flags & BUF_Active));
|
|
assert(pbuf->ifi == -1);
|
|
assert(!(pbuf->flags & BUF_Dirty));
|
|
|
|
// if no free bufferes, cannot activate a buffer
|
|
if (!pbufFree) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// removed buffer from free list
|
|
assert(pbuf == pbufFree);
|
|
pbufFree = pbuf->pbufNext;
|
|
|
|
// bind buffer to physical file handle
|
|
pbuf->ifi = pfi->ifi;
|
|
|
|
// make sure there isn't an active buffer on this range
|
|
#if DBG
|
|
assert(FNoDupBuf(pbuf));
|
|
#endif
|
|
|
|
// insert buffer into active list
|
|
pbuf->pbufNext = pbufActive;
|
|
pbufActive = pbuf;
|
|
|
|
assert(pbufActive);
|
|
|
|
// activate the buffer
|
|
pbuf->flags |= BUF_Active;
|
|
assert(
|
|
pfi->rgpbuf[pbuf->ibStart >> cshiftBuf] == NULL ||
|
|
pfi->rgpbuf[pbuf->ibStart >> cshiftBuf] == pbufPreviousWrite);
|
|
assert(pbuf->ibStart <= pfi->cbMap);
|
|
pfi->rgpbuf[pbuf->ibStart >> cshiftBuf] = pbuf;
|
|
|
|
DBEXEC(DB_BUFVERBOSE,
|
|
DBPRINT(" - activated pbuf = 0x%p at offset %8lX on file %s\n",
|
|
pbuf, pbuf->ibStart, pfi->szFileName));
|
|
|
|
// success
|
|
return 1;
|
|
}
|
|
|
|
STATIC BOOL
|
|
FDeactivateBuf(
|
|
PBUF pbuf
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deactivate a buffer by flushing it, setting the buffer status to inactive,
|
|
deleting the buffer from the active list and adding the buffer to the
|
|
free list.
|
|
|
|
Arguments:
|
|
|
|
pbuf - buffer to deactivate
|
|
|
|
Return Value:
|
|
|
|
!0 on success, 0 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbufT;
|
|
PBUF pbufLast;
|
|
|
|
assert(pbuf);
|
|
assert(pbuf->flags & BUF_Active);
|
|
assert(pbuf->ifi != -1);
|
|
assert(pbufActive);
|
|
assert(rgpfi[pbuf->ifi]);
|
|
assert(rgpfi[pbuf->ifi]->rgpbuf);
|
|
assert(rgpfi[pbuf->ifi]->rgpbuf[pbuf->ibStart >> cshiftBuf]);
|
|
assert(!(pbuf->flags & BUF_Current));
|
|
|
|
if (!fErr) // don't flush if we had an error
|
|
FlushBuffer(pbuf);
|
|
|
|
DBEXEC(DB_BUFVERBOSE,
|
|
DBPRINT(" - deactivated pbuf = 0x%p at offset %8lX on file %s\n",
|
|
pbuf, pbuf->ibStart, rgpfi[pbuf->ifi]->szFileName));
|
|
|
|
// make the buffer inactive
|
|
pbuf->flags = 0;
|
|
pbuf->ifi = -1;
|
|
|
|
// removed buffer from active list
|
|
if (pbufActive == pbuf) {
|
|
pbufActive = pbufActive->pbufNext;
|
|
} else {
|
|
pbufLast = pbufActive;
|
|
for (pbufT = pbufActive->pbufNext; pbufT; pbufT = pbufT->pbufNext) {
|
|
assert(pbufT);
|
|
if (pbuf == pbufT) {
|
|
pbufLast->pbufNext = pbufT->pbufNext;
|
|
break;
|
|
}
|
|
pbufLast = pbufT;
|
|
}
|
|
}
|
|
|
|
// add the buffer to the free list
|
|
pbuf->pbufNext = pbufFree;
|
|
pbufFree = pbuf;
|
|
|
|
// success
|
|
return 1;
|
|
}
|
|
|
|
STATIC PBUF
|
|
PbufNew(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new buffer. Print an error if we run out of memory.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
pointer to a buffer
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbuf;
|
|
|
|
// allocate the buffer structure
|
|
pbuf = (PBUF) PvAllocZ(sizeof(BUF));
|
|
|
|
pbuf->ifi = -1;
|
|
|
|
assert(pbuf);
|
|
|
|
return(pbuf);
|
|
}
|
|
|
|
STATIC PBUF
|
|
PbufAlloc(
|
|
PFI pfi,
|
|
LONG ib)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a new buffer. Print an error if we run out of memory.
|
|
|
|
Arguments:
|
|
|
|
pfi - file descriptor to bind buffer to
|
|
|
|
ib - file offset to map buffer to
|
|
|
|
Return Value:
|
|
|
|
pointer to a buffer
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbuf;
|
|
#if DBG
|
|
BOOL f;
|
|
#endif //DBG
|
|
|
|
assert(FBufListCheck());
|
|
|
|
// get a buffer from the free list
|
|
pbuf = pbufFree;
|
|
|
|
// if no free buffers
|
|
if (!pbuf) {
|
|
// get LRU seeked buffer
|
|
pbuf = PbufLRU();
|
|
|
|
// this assumes more > 0 buffers in the system
|
|
assert(pbuf);
|
|
|
|
// deactivate buffer
|
|
#if DBG
|
|
f =
|
|
#endif // DBG
|
|
FDeactivateBuf(pbuf);
|
|
assert(f);
|
|
}
|
|
|
|
assert(pbuf);
|
|
|
|
// set the buffer range
|
|
pbuf->ibStart = (ib / cbIOBuf) * cbIOBuf;
|
|
pbuf->ibEnd = pbuf->ibStart + cbIOBuf;
|
|
pbuf->ibCur = ib;
|
|
pbuf->ibLast = pbuf->ibStart;
|
|
assert((ib >= pbuf->ibStart) && (ib <= pbuf->ibEnd));
|
|
pbuf->pbCur = (BYTE *) ((LONG) pbuf->rgbBuf + (ib - pbuf->ibStart));
|
|
|
|
// if file is writable, clear the buffer
|
|
if (pfi->flags & FI_Write) {
|
|
memset(pbuf->rgbBuf, '\0', cbIOBuf);
|
|
}
|
|
|
|
// if file is readable, set the random contents flag
|
|
if (!(pfi->flags & FI_Write)) {
|
|
pbuf->flags |= BUF_Random;
|
|
}
|
|
|
|
// activate buffer
|
|
#if DBG
|
|
f =
|
|
#endif // DBG
|
|
FActivateBuf(pfi, pbuf);
|
|
|
|
assert(f);
|
|
assert(FBufListCheck());
|
|
// return the buffer
|
|
return(pbuf);
|
|
}
|
|
|
|
STATIC VOID
|
|
TransitionPFI(
|
|
PFI pfi,
|
|
PPFI ppfiFromHead,
|
|
PPFI ppfiFromTail,
|
|
PPFI ppfiToHead,
|
|
PPFI ppfiToTail,
|
|
BOOL fAddToTail)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Move a logical file descriptor from one pool to another.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor to move
|
|
|
|
ppfiFromHead - head of pool to remove pfi from
|
|
|
|
ppfiFromTail - tail of pool to remove pfi from, update if ppfiFromTail
|
|
|
|
ppfiToHead - head of pool to put pfi in
|
|
|
|
ppfiToTail - tail of pool to put pfi in, update if ppfiToTail
|
|
|
|
fAddToTail - add pfi to tail of ppfiTo
|
|
|
|
Return Value:
|
|
|
|
!0 on success, 0 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfiT; // current
|
|
PFI pfiL; // last
|
|
|
|
assert(pfi);
|
|
assert(ppfiFromHead);
|
|
assert(ppfiToHead);
|
|
#if DBG
|
|
assert(FPfiInList(pfi, *ppfiFromHead));
|
|
assert(!FPfiInList(pfi, *ppfiToHead));
|
|
#endif // DBG
|
|
|
|
// remove pfi from ppfiFromHead
|
|
if (pfi == *ppfiFromHead) {
|
|
// pfi is the first element
|
|
*ppfiFromHead = (*ppfiFromHead)->pfiNext;
|
|
if (ppfiFromTail && ((*ppfiFromTail) == pfi)) {
|
|
*ppfiFromTail = NULL;
|
|
}
|
|
} else {
|
|
pfiL = *ppfiFromHead;
|
|
for (pfiT = (*ppfiFromHead)->pfiNext; pfiT; pfiT = pfiT->pfiNext) {
|
|
if (pfi == pfiT) {
|
|
pfiL->pfiNext = pfiT->pfiNext;
|
|
break;
|
|
} else {
|
|
pfiL = pfiT;
|
|
}
|
|
}
|
|
if (ppfiFromTail && ((*ppfiFromTail) == pfi)) {
|
|
*ppfiFromTail = pfiL;
|
|
}
|
|
}
|
|
|
|
// add pfi to ppfiTo
|
|
if (!(*ppfiToHead)) {
|
|
// list is empty
|
|
*ppfiToHead = pfi;
|
|
pfi->pfiNext = NULL;
|
|
if (ppfiToTail) {
|
|
assert(!(*ppfiToTail));
|
|
*ppfiToTail = pfi;
|
|
}
|
|
} else {
|
|
if (fAddToTail) {
|
|
// add pfi to tail
|
|
assert(ppfiToTail);
|
|
pfi->pfiNext = NULL;
|
|
(*ppfiToTail)->pfiNext = pfi;
|
|
*ppfiToTail = pfi;
|
|
} else {
|
|
pfi->pfiNext = *ppfiToHead;
|
|
*ppfiToHead = pfi;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
|
|
STATIC BOOL
|
|
FBufListCheck(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check the active, free and LRU list for errors. This is a debug routine.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbuf;
|
|
LONG cbuf;
|
|
|
|
// ensure the correct number of buffers in the lists
|
|
for(cbuf = 0, pbuf = pbufFree;
|
|
pbuf && cbuf < cbufTot;
|
|
pbuf = pbuf->pbufNext) {
|
|
cbuf++;
|
|
}
|
|
|
|
for(pbuf = pbufActive;
|
|
pbuf && cbuf < cbufTot;
|
|
pbuf = pbuf->pbufNext) {
|
|
cbuf++;
|
|
}
|
|
|
|
if (cbuf != cbufTot) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// walk down the LRU list counting members, walk back and see
|
|
// if we get to the same place
|
|
for(cbuf = 0, pbuf = pbufLRUHead;
|
|
pbuf && cbuf < cbufTot;
|
|
pbuf = pbuf->pbufLRURight) {
|
|
cbuf++;
|
|
}
|
|
|
|
for(pbuf = pbufLRUTail;
|
|
pbuf && cbuf > 0;
|
|
pbuf = pbuf->pbufLRULeft) {
|
|
cbuf--;
|
|
}
|
|
|
|
return(cbuf == 0);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
#if DBG
|
|
|
|
STATIC BOOL
|
|
FNoDupBuf(
|
|
PBUF pbuf)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scan the active list to see if pbuf's range is covered by another buffer.
|
|
|
|
Arguments:
|
|
|
|
pbuf - buffer to check for duplicates ranges of
|
|
|
|
Return Value:
|
|
|
|
!0 if no duplicates, 0 if duplicates
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUF pbufT;
|
|
|
|
assert(pbuf);
|
|
for (pbufT = pbufActive; pbufT; pbufT = pbufT->pbufNext) {
|
|
assert(pbufT);
|
|
if ((pbuf->ifi == pbufT->ifi) &&
|
|
(pbuf->ibStart == pbufT->ibStart)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
#if DBG
|
|
|
|
STATIC BOOL
|
|
FPfiInList(
|
|
PFI pfi,
|
|
PFI pfiList)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if pfi is in pfiList.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor to check for
|
|
|
|
pfiList - list to look for logical file descriptor in
|
|
|
|
Return Value:
|
|
|
|
!0 if found, 0 if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
PFI pfiT;
|
|
DWORD ifi = 0;
|
|
|
|
for (pfiT = pfiList, ifi = 0; pfiT; pfiT = pfiT->pfiNext, ifi++) {
|
|
if (pfiT == pfi) {
|
|
return(TRUE);
|
|
}
|
|
|
|
if (ifi > cfiTot) {
|
|
assert(FALSE);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
#if DBG
|
|
|
|
STATIC BOOL
|
|
FPfiCheck(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check the pfi lists.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
!0 if found, 0 if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD ifi = 0;
|
|
PFI pfi;
|
|
|
|
pfi = pfiFree;
|
|
while (pfi) {
|
|
ifi++;
|
|
if (ifi > cfiTot) {
|
|
return(FALSE);
|
|
}
|
|
pfi = pfi->pfiNext;
|
|
}
|
|
|
|
pfi = pfiOpen;
|
|
while (pfi) {
|
|
ifi++;
|
|
if (ifi > cfiTot) {
|
|
return(FALSE);
|
|
}
|
|
if (!(pfi->flags & FI_Mapped) && pfi->pbufCur != NULL) {
|
|
if (pfi->ifi != pfi->pbufCur->ifi) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
pfi = pfi->pfiNext;
|
|
}
|
|
|
|
pfi = pfiClosedHead;
|
|
while (pfi) {
|
|
ifi++;
|
|
if (ifi > cfiTot) {
|
|
return(FALSE);
|
|
}
|
|
if (!(pfi->flags & FI_Mapped) && pfi->pbufCur != NULL) {
|
|
if (pfi->ifi != pfi->pbufCur->ifi) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
pfi = pfi->pfiNext;
|
|
}
|
|
|
|
return(cfiTot == ifi);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
INT
|
|
MappedOpen(
|
|
PFI pfi,
|
|
LONG flags,
|
|
BOOL fNewPfi,
|
|
DWORD *pdwErr)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NT mapped i/o open.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
flags - flags to open file with
|
|
|
|
fNewPfi - !0 if pfi is new
|
|
|
|
pdwErr - ptr to store error code in case of failure.
|
|
|
|
Return Value:
|
|
|
|
0 on success, !0 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fWriteable, fCreate;
|
|
int i;
|
|
DWORD dwSize;
|
|
|
|
if (fNewPfi) {
|
|
// open the file through win32
|
|
fWriteable = flags & (O_WRONLY | O_RDWR);
|
|
fCreate = flags & O_CREAT;
|
|
if (fWriteable) {
|
|
for (i = 0; i < MAX_WRITEABLE_FILES; i++) {
|
|
if (pfiCloseOnBadExit[i] == NULL) {
|
|
pfiCloseOnBadExit[i] = pfi;
|
|
break;
|
|
}
|
|
}
|
|
pfi->hFile = CreateFile(pfi->szFileName,
|
|
GENERIC_READ | GENERIC_WRITE, // access
|
|
0, // share
|
|
NULL, // security
|
|
fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
} else {
|
|
assert(!fCreate); // no read-only create
|
|
pfi->hFile = CreateFile(pfi->szFileName,
|
|
GENERIC_READ, // access
|
|
FILE_SHARE_READ, // share
|
|
NULL, // security
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
// abort on error
|
|
if (pfi->hFile == INVALID_HANDLE_VALUE) {
|
|
(*pdwErr) = GetLastError();
|
|
pfi->hFile = NULL;
|
|
|
|
if (!fWriteable &&
|
|
((*pdwErr) == ERROR_PATH_NOT_FOUND ||
|
|
(*pdwErr) == ERROR_FILE_NOT_FOUND) ) {
|
|
TR_Open_To_Free(pfi);
|
|
Fatal(NULL, CANTOPENINPUTFILE, pfi->szFileName);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (fCreate) {
|
|
dwSize = 0;
|
|
} else {
|
|
dwSize = GetFileSize(pfi->hFile, NULL);
|
|
|
|
if (dwSize == 0xFFFFFFFF) {
|
|
Fatal(NULL, CANTOPENFILE, pfi->szFileName);
|
|
}
|
|
}
|
|
|
|
pfi->cbSoFar = dwSize;
|
|
|
|
if (fWriteable) {
|
|
if (pfi->cbMapView == 0) { // Set in the case of FileOpenMapped()
|
|
pfi->cbMapView = __max(cbMapViewDefault, pfi->cbSoFar);
|
|
}
|
|
} else {
|
|
pfi->cbMapView = pfi->cbSoFar;
|
|
}
|
|
|
|
if (!FMapPfi(pfi, FALSE, pdwErr)) {
|
|
return -1;
|
|
}
|
|
|
|
pfi->flags |= FI_Mapped;
|
|
} else {
|
|
MappedSeek(pfi, 0, SEEK_SET, pdwErr);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
LONG
|
|
MappedSeek(
|
|
PFI pfi,
|
|
LONG ib,
|
|
INT origin,
|
|
DWORD *pdwErr)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NT mapped i/o seek.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
if - offset
|
|
|
|
origin - type of seek
|
|
|
|
pdwErr - ptr to store error code in.
|
|
|
|
Return Value:
|
|
|
|
offset seek to
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (origin) {
|
|
#if DBG
|
|
default:
|
|
assert(FALSE);
|
|
break;
|
|
#endif
|
|
case SEEK_CUR:
|
|
assert((pfi->ibCur + ib) >= 0 );
|
|
pfi->ibCur += ib;
|
|
break;
|
|
|
|
case SEEK_END:
|
|
assert(pfi->cbSoFar + ib >= 0);
|
|
pfi->ibCur = pfi->cbSoFar + ib;
|
|
break;
|
|
|
|
case SEEK_SET:
|
|
assert(ib >= 0 );
|
|
pfi->ibCur = ib;
|
|
break;
|
|
}
|
|
|
|
if (pfi->ibCur > pfi->cbMapView) {
|
|
if (!ExtendMapView(pfi, pfi->ibCur, pdwErr)) {
|
|
if (pfi->MapAddr == 0) { // not the ILK file
|
|
if ((*pdwErr) == ERROR_DISK_FULL) {
|
|
Fatal(pfi->szFileName, DISKFULL);
|
|
}
|
|
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, pfi->ibCur);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
DBEXEC(DB_IO_SEEK,
|
|
Trans_LOG(LOG_MapSeek, pfi->ifi, ib, 0, origin, NULL));
|
|
|
|
return(pfi->ibCur);
|
|
}
|
|
|
|
STATIC DWORD
|
|
MappedRead(
|
|
PFI pfi,
|
|
PVOID pv,
|
|
DWORD cb)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NT mapped i/o read.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
pv - buffer to read into
|
|
|
|
cb - bytes to read
|
|
|
|
Return Value:
|
|
|
|
bytes read
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pvT;
|
|
DWORD dwErr = 0;
|
|
|
|
assert(pfi);
|
|
assert(pv);
|
|
|
|
DBEXEC(DB_IO_READ,
|
|
Trans_LOG(LOG_MapRead, pfi->ifi, pfi->ibCur, cb, 0, NULL));
|
|
|
|
// extend map view only for writeable files
|
|
if ((pfi->ibCur + (LONG) cb) > pfi->cbMapView) {
|
|
if (pfi->flags & FI_Write) {
|
|
if (!ExtendMapView(pfi, pfi->ibCur + (LONG) cb, &dwErr)) {
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, pfi->ibCur + (LONG) cb);
|
|
}
|
|
} else {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
pvT = (PVOID) ((DWORD) (pfi->pvMapView) + (DWORD) (pfi->ibCur));
|
|
|
|
memcpy(pv, pvT, cb);
|
|
pfi->ibCur += cb;
|
|
|
|
return(cb);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NT mapped i/o tell.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
Return Value:
|
|
|
|
file offset
|
|
|
|
--*/
|
|
|
|
#if DBG
|
|
LONG WriteBreakAddress = (LONG) -1;
|
|
#endif
|
|
|
|
STATIC DWORD
|
|
MappedWrite(
|
|
PFI pfi,
|
|
const void *pv,
|
|
DWORD cb)
|
|
{
|
|
PVOID pvT;
|
|
DWORD dwErr = 0;
|
|
|
|
assert(pfi);
|
|
assert(pfi->ibCur >= 0);
|
|
|
|
DBEXEC(DB_IO_WRITE,
|
|
Trans_LOG(LOG_MapWrite, pfi->ifi, pfi->ibCur, cb, 0, NULL));
|
|
|
|
if ((pfi->ibCur + (LONG) cb) > pfi->cbMapView) {
|
|
if (!ExtendMapView(pfi, pfi->ibCur + cb, &dwErr)) {
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, pfi->ibCur + (LONG) cb);
|
|
}
|
|
}
|
|
|
|
pvT = (PVOID) ((DWORD) (pfi->pvMapView) + (DWORD) (pfi->ibCur));
|
|
|
|
if ((pfi->ibCur + (LONG) cb) > pfi->cbSoFar) {
|
|
pfi->cbSoFar = pfi->ibCur + (LONG) cb;
|
|
}
|
|
|
|
#if DBG
|
|
if (((DWORD) pfi->ibCur <= (DWORD) WriteBreakAddress) && ((DWORD) (pfi->ibCur + cb) >= (DWORD) WriteBreakAddress)) {
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
|
|
memcpy(pvT, pv, cb);
|
|
pfi->ibCur += cb;
|
|
|
|
return(cb);
|
|
}
|
|
|
|
INT
|
|
MappedCloseHard (
|
|
PFI pfi)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NT mapped i/o hard close.
|
|
|
|
Arguments:
|
|
|
|
pfi - logical file descriptor
|
|
|
|
Return Value:
|
|
|
|
file offset
|
|
|
|
--*/
|
|
|
|
{
|
|
INT i, retval;
|
|
|
|
assert(pfi);
|
|
assert(pfi->flags & FI_Mapped);
|
|
|
|
// already closed
|
|
if (!pfi->hFile) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!UnmapViewOfFile(pfi->pvMapView)) {
|
|
Fatal(NULL, CANTCLOSEFILE, pfi->szFileName);
|
|
}
|
|
|
|
if (pfi->flags & FI_Write) {
|
|
// If writeable, set the end of file pointer to the last place we
|
|
// wrote. This prevents the file from being created with the full
|
|
// size of the mapping.
|
|
|
|
if ((pfi->cbSoFar != (LONG)SetFilePointer(pfi->hFile, pfi->cbSoFar, NULL, FILE_BEGIN)) ||
|
|
!SetEndOfFile(pfi->hFile)) {
|
|
Fatal(NULL, CANTCLOSEFILE, pfi->szFileName);
|
|
}
|
|
}
|
|
|
|
if (!(retval = CloseHandle(pfi->hFile))) {
|
|
Fatal(NULL, CANTCLOSEFILE, pfi->szFileName);
|
|
}
|
|
|
|
for (i = 0; i < MAX_WRITEABLE_FILES; i++) {
|
|
if (pfi == pfiCloseOnBadExit[i]) {
|
|
pfiCloseOnBadExit[i] = NULL;
|
|
}
|
|
}
|
|
|
|
FreePv(pfi->szFileName);
|
|
|
|
return(retval);
|
|
}
|
|
|
|
BOOL
|
|
ExtendMapView(PFI pfi, DWORD ibNeeded, DWORD *pdwErr)
|
|
// Makes the memory-map bigger for a mapped file. This only makes sense
|
|
// for writeable files, not readable files.
|
|
{
|
|
|
|
// Free the existing map.
|
|
|
|
if (!UnmapViewOfFile(pfi->pvMapView)) {
|
|
*pdwErr = GetLastError();
|
|
|
|
CloseHandle(pfi->hFile);
|
|
pfi->hFile = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Set the new size (it must grow by at least a factor of 2).
|
|
|
|
pfi->cbMapView = __max((LONG) ibNeeded, pfi->cbMapView * 2);
|
|
|
|
if (!FMapPfi(pfi, TRUE, pdwErr)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FMapPfi(PFI pfi, BOOL fExtend, DWORD *pdwErr)
|
|
{
|
|
HANDLE hMap;
|
|
|
|
if (pfi->flags & FI_Write && fExtend) {
|
|
// Extend file to size specified. This works around a problem in
|
|
// NT 3.10 where the cache isn't flushed to disk properly.
|
|
|
|
if ((pfi->cbMapView != (LONG)SetFilePointer(pfi->hFile, pfi->cbMapView, NULL, FILE_BEGIN)) ||
|
|
!SetEndOfFile(pfi->hFile)) {
|
|
|
|
(*pdwErr) = GetLastError();
|
|
CloseHandle(pfi->hFile);
|
|
pfi->hFile = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
hMap = CreateFileMapping(pfi->hFile,
|
|
NULL,
|
|
(pfi->flags & FI_Write)
|
|
? PAGE_READWRITE
|
|
: PAGE_READONLY,
|
|
0,
|
|
pfi->cbMapView,
|
|
NULL);
|
|
|
|
if (hMap == NULL) {
|
|
(*pdwErr) = GetLastError();
|
|
|
|
CloseHandle(pfi->hFile);
|
|
pfi->hFile = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
pfi->pvMapView = MapViewOfFileEx(hMap,
|
|
(pfi->flags & FI_Write)
|
|
? FILE_MAP_ALL_ACCESS
|
|
: FILE_MAP_READ,
|
|
0,
|
|
0,
|
|
0,
|
|
(LPVOID) pfi->MapAddr);
|
|
|
|
if (!pfi->pvMapView) {
|
|
(*pdwErr) = GetLastError();
|
|
|
|
CloseHandle(hMap);
|
|
CloseHandle(pfi->hFile);
|
|
pfi->hFile = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// The handle to the mapping object isn't needed any more
|
|
if (!CloseHandle(hMap)) {
|
|
(*pdwErr) = GetLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
MoveToFreeListPfi (PFI pfi)
|
|
{
|
|
PFI pfiT;
|
|
|
|
pfiT = pfiOpen;
|
|
while (pfiT) {
|
|
if (pfiT == pfi) {
|
|
TR_Open_To_Free(pfi);
|
|
return;
|
|
} else {
|
|
pfiT = pfiT->pfiNext;
|
|
}
|
|
} // end while
|
|
|
|
pfiT = pfiClosedHead;
|
|
while (pfiT) {
|
|
if (pfiT == pfi) {
|
|
TR_Cache_To_Free(pfi);
|
|
return;
|
|
} else {
|
|
pfiT = pfiT->pfiNext;
|
|
}
|
|
} // end while
|
|
|
|
// should have been in one of the lists
|
|
Fatal(NULL, INTERNAL_ERR);
|
|
}
|
|
|
|
VOID
|
|
BadExitCleanup(VOID)
|
|
// Warning ... may be called asynchronously by the control-C handler thread.
|
|
{
|
|
int i;
|
|
|
|
// on an ilink free up ILK space and close file
|
|
if (fINCR) {
|
|
FreeHeap();
|
|
if (!_access(szIncrDbFilename, 0)) {
|
|
_unlink(szIncrDbFilename);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_WRITEABLE_FILES; i++) {
|
|
if (pfiCloseOnBadExit[i] != NULL) {
|
|
|
|
// remove pfi from list of open/cached pfi (otherwise we will try to close it again in FileCloseAll()).
|
|
MoveToFreeListPfi(pfiCloseOnBadExit[i]);
|
|
|
|
if (!UnmapViewOfFile(pfiCloseOnBadExit[i]->pvMapView)) {
|
|
Fatal(NULL, CANTCLOSEFILE, pfiCloseOnBadExit[i]->szFileName);
|
|
}
|
|
|
|
if (pfiCloseOnBadExit[i]->flags & FI_Write) {
|
|
// Set end of file to 0, this speeds up the close
|
|
|
|
if ((SetFilePointer(pfiCloseOnBadExit[i]->hFile, 0, NULL, FILE_BEGIN) != 0) ||
|
|
!SetEndOfFile(pfiCloseOnBadExit[i]->hFile))
|
|
Fatal(NULL, CANTCLOSEFILE, pfiCloseOnBadExit[i]->szFileName);
|
|
}
|
|
|
|
if (!CloseHandle(pfiCloseOnBadExit[i]->hFile)) {
|
|
Fatal(NULL, CANTCLOSEFILE, pfiCloseOnBadExit[i]->szFileName);
|
|
}
|
|
|
|
if (!DeleteFile(pfiCloseOnBadExit[i]->szFileName)) {
|
|
Fatal(NULL, CANTREMOVEFILE, pfiCloseOnBadExit[i]->szFileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
FileCloseAll();
|
|
RemoveConvertTempFiles();
|
|
|
|
// Close the pdb w/o committing.
|
|
|
|
if (fPdb) {
|
|
DBG_ClosePDB();
|
|
}
|
|
|
|
ExitProcess(1);
|
|
}
|
|
|
|
|
|
// PbMappedRegion: if possible, returns a pointer to a memory-mapped region
|
|
// of an open file. If the file is writeable, its size is extended to
|
|
// the end of the region if necessary.
|
|
//
|
|
// Returns NULL if mapping is not available.
|
|
//
|
|
// The "current position" in the file is not changed.
|
|
//
|
|
// Caveats:
|
|
// * caller must handle NULL return value
|
|
// * return value is invalidated by subsequent operations on the same
|
|
// file handle.
|
|
|
|
BYTE *
|
|
PbMappedRegion(INT fd, DWORD ibStart, DWORD cb)
|
|
{
|
|
PFI pfi;
|
|
DWORD dwErr = 0;
|
|
|
|
pfi = rgpfi[fd];
|
|
|
|
if (!(pfi->flags & FI_Mapped)) {
|
|
return NULL;
|
|
}
|
|
|
|
DBEXEC(DB_IO_WRITE,
|
|
Trans_LOG(LOG_MapWrite, pfi->ifi, pfi->ibCur, cb, 0, NULL));
|
|
|
|
if (ibStart + cb > (DWORD)pfi->cbMapView) {
|
|
if (!ExtendMapView(pfi, ibStart + cb, &dwErr)) {
|
|
if (dwErr == ERROR_DISK_FULL) {
|
|
Fatal(pfi->szFileName, DISKFULL);
|
|
}
|
|
|
|
Fatal(pfi->szFileName, CANTSEEKFILE, ibStart + cb);
|
|
}
|
|
}
|
|
|
|
if ((ibStart + cb) > (DWORD) pfi->cbSoFar) {
|
|
pfi->cbSoFar = ibStart + cb;
|
|
}
|
|
|
|
return((BYTE *) pfi->pvMapView + ibStart);
|
|
}
|
|
|
|
|
|
VOID
|
|
FileSetSize (
|
|
INT fd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API is used in conjunction with Malloc() to set the
|
|
current size of file since the writes don't go through
|
|
the file API.
|
|
|
|
Arguments:
|
|
|
|
fd - hanlde to file
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
// file better be mapped
|
|
assert(rgpfi[fd]->flags & FI_Mapped);
|
|
|
|
// update cbSoFar
|
|
rgpfi[fd]->cbSoFar = rgpfi[fd]->ibCur;
|
|
}
|
|
|
|
VOID
|
|
FileCloseMap (
|
|
INT fd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the map and file without writing out anything.
|
|
|
|
Arguments:
|
|
|
|
fd - hanlde to file
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
// don't look at interrupts here
|
|
|
|
// file must be a mapped file
|
|
assert(rgpfi[fd]->flags & FI_Mapped);
|
|
|
|
// set the size to zero
|
|
rgpfi[fd]->cbSoFar = 0;
|
|
|
|
// remove pfi from open list
|
|
TR_Open_To_Free(rgpfi[fd]);
|
|
|
|
// close the map & file
|
|
MappedCloseHard(rgpfi[fd]);
|
|
}
|