3215 lines
63 KiB
C
3215 lines
63 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (C) 1989 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
bufio.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Functions which are common to the COFF Linker/Librarian/Dumper.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Brent Mills (BrentM) 17-Aug-1992
|
||
|
Azeem Khan (AzeemK) 09-Jun-1992
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
21-Oct-1992 BrentM added a while on GrowMapTable calls, fixed a bug
|
||
|
for JimSa in FileChSize()
|
||
|
20-Oct-1992 BrentM added FileChSize() to the fileio api
|
||
|
11-Oct-1992 BrentM writes of 0 bytes don't do anything, files opened
|
||
|
with read, write and !create are now handled
|
||
|
05-Oct-1992 BrentM added STATIC #define for profiling
|
||
|
05-Oct-1992 BrentM don't round off in GrowMapTable(), now links c++ p1
|
||
|
05-Oct-1992 BrentM marked buffers BUF_Dirty on writes spanning >1 buffers
|
||
|
02-Oct-1992 BrentM BufferedSeek() adds the offset on SEEK_END
|
||
|
01-Oct-1992 BrentM mapped i/o option for reads if on NT
|
||
|
30-Sep-1992 BrentM encapsalated BUF_Random setting in if !FI_Write
|
||
|
28-Sep-1992 BrentM better summary on DB_BUFVERBOSE
|
||
|
27-Sep-1992 BrentM dump logical file descriptor for i/o logging
|
||
|
25-Sep-1992 BrentM made the current buffer of cached file handles
|
||
|
available for reallocation
|
||
|
23-Sep-1992 BrentM added read buffering and logical file handle caching
|
||
|
10-Sep-1992 BrentM added [f]open/[f]close logging
|
||
|
09-Sep-1992 BrentM added read logging and added separate db switches
|
||
|
04-Sep-1992 BrentM removed pragmas to turn off compiler optimizations
|
||
|
03-Sep-1992 BrentM put diagnostics under ifdefs and DBEXECs
|
||
|
20-Aug-1992 BrentM fixed bogus assert
|
||
|
19-Aug-1992 BrentM increased max .exe size to 7000000, links XL nondebug
|
||
|
03-Aug-1992 BrentM file mapped buffering
|
||
|
29-Jul-1992 BrentM split from shared.c
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "compact.h"
|
||
|
|
||
|
// Get Rid of Debug logging, this requires too much external support
|
||
|
#define DBEXEC(flg, expr)
|
||
|
#define DBEXEC_REL(flg, expr)
|
||
|
|
||
|
#define RemoveConvertTempFiles()
|
||
|
|
||
|
void ErrorExit(unsigned, const char *, const char *);
|
||
|
|
||
|
#define Error(a, b, c) ErrorExit(a, b, c)
|
||
|
#define OutOfMemory() ErrorExit(ERR_NOMEM, NULL, NULL)
|
||
|
|
||
|
|
||
|
void FreePv(void *pv)
|
||
|
{
|
||
|
free(pv);
|
||
|
}
|
||
|
|
||
|
|
||
|
void *PvAllocZ(size_t cb)
|
||
|
{
|
||
|
void *pv = calloc(1, cb);
|
||
|
|
||
|
if (pv == NULL) {
|
||
|
OutOfMemory();
|
||
|
}
|
||
|
|
||
|
return(pv);
|
||
|
}
|
||
|
|
||
|
|
||
|
void *PvRealloc(void *pv, size_t cb)
|
||
|
{
|
||
|
void *pvNew = realloc(pv, cb);
|
||
|
|
||
|
if (pvNew == NULL) {
|
||
|
OutOfMemory();
|
||
|
}
|
||
|
|
||
|
return(pvNew);
|
||
|
}
|
||
|
|
||
|
|
||
|
char *SzDup(const char *sz)
|
||
|
{
|
||
|
char *szNew = _strdup(sz);
|
||
|
|
||
|
if (szNew == NULL) {
|
||
|
OutOfMemory();
|
||
|
}
|
||
|
|
||
|
return(szNew);
|
||
|
}
|
||
|
|
||
|
|
||
|
// function static define
|
||
|
#define STATIC static
|
||
|
|
||
|
extern BOOL fCtrlCSignal;
|
||
|
|
||
|
#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);
|
||
|
INT MappedCloseHard(PFI);
|
||
|
LONG MappedSeek(PFI, LONG, INT);
|
||
|
STATIC DWORD MappedWrite(PFI, const void *, DWORD);
|
||
|
STATIC DWORD MappedRead(PFI, PVOID, DWORD);
|
||
|
BOOL ExtendMapView(PFI pfi, DWORD ibNeeded);
|
||
|
BOOL FMapPfi(PFI pfi);
|
||
|
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 = NULL; // LRU buffer chain head (MRU)
|
||
|
static PBUF pbufLRUTail = NULL; // LRU buffer chain tail (LRU)
|
||
|
static PBUF pbufFree = NULL; // free file buffers
|
||
|
static PBUF pbufActive = NULL; // active file buffers
|
||
|
|
||
|
/* statistics */
|
||
|
static LONG crereads = 0L; // number of buffer re-reads
|
||
|
static LONG cfiTot = 0L; // total number of logical file descriptors
|
||
|
static LONG cfiCacheClosed = 0L;// current size of closed/cached pool
|
||
|
static LONG cfiCacheClosedMax = 0L; // maximum size of closed/cached pool
|
||
|
static LONG cbufTot = 0L; // total number of buffers
|
||
|
static DWORD cfiInSystem; // # of FI's chosen
|
||
|
static DWORD cfiCacheableInSystem; // # of FI's chosen
|
||
|
|
||
|
/* logical file descriptor containers */
|
||
|
static PFI pfiFree = NULL; // free logical file handles
|
||
|
static PFI pfiOpen = NULL; // active logical file handles
|
||
|
static PFI pfiClosedHead = NULL;// head of list of closed file handles
|
||
|
static PFI pfiClosedTail = NULL;// tail of list of closed file handles
|
||
|
PFI *rgpfi; // map of cached file handles
|
||
|
|
||
|
/* error handlers */
|
||
|
static BOOL fMappedIO = 0;
|
||
|
|
||
|
/* 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 (
|
||
|
LONG cbuf,
|
||
|
USHORT cfiForSystem_NT,
|
||
|
USHORT cfiCacheClosedT_NT,
|
||
|
USHORT cfiForSystem_TNT,
|
||
|
USHORT cfiCacheClosedT_TNT,
|
||
|
BOOL fTryMapped)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Initialize the buffered i/o package.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
cbuf - number of buffers to allocate to package
|
||
|
|
||
|
cfiForSystem - number of logical file handles for system
|
||
|
|
||
|
cfiCacheClosedT - size of logical file handle cache
|
||
|
|
||
|
fTryMapped - if !0 attempte mapped i/o, otherwise use buffered i/o
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
HINSTANCE hLib;
|
||
|
LONG ibuf;
|
||
|
PBUF pbuf;
|
||
|
PFI pfi;
|
||
|
LONG ifi;
|
||
|
LONG cfiForSystem, cfiCacheClosedT;
|
||
|
|
||
|
int i;
|
||
|
|
||
|
if (fTryMapped) {
|
||
|
// First check for TNT specifically. This is so we can also
|
||
|
// reduce the count of open file handles, for TNT only.
|
||
|
|
||
|
hLib = GetModuleHandle("kernel32.dll");
|
||
|
if (hLib != 0 && GetProcAddress(hLib, "IsTNT") != 0) {
|
||
|
fTryMapped = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DBEXEC(DB_BUFVERBOSE, DBPRINT(fTryMapped ?
|
||
|
"Using NT I/O parameters (file mapping)\n" :
|
||
|
"Using TNT I/O parameters (no mapping, fewer handles)\n"));
|
||
|
|
||
|
cfiForSystem = fTryMapped ? cfiForSystem_NT : cfiForSystem_TNT;
|
||
|
cfiCacheClosedT = fTryMapped ? cfiCacheClosedT_NT : cfiCacheClosedT_TNT;
|
||
|
|
||
|
cfiInSystem = cfiForSystem; // store in global (for statistics)
|
||
|
cfiCacheableInSystem = cfiCacheClosedT;
|
||
|
|
||
|
// set the total buffers for the system
|
||
|
cbufTot = cbuf;
|
||
|
|
||
|
// allocate buffers and add them to the free list
|
||
|
for (ibuf = 0; ibuf < cbuf; ibuf++) {
|
||
|
pbuf = PbufNew();
|
||
|
if (!pbufFree) {
|
||
|
pbufFree = pbuf;
|
||
|
} else {
|
||
|
pbuf->pbufNext = pbufFree;
|
||
|
pbufFree = pbuf;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// allocate logical file handle map
|
||
|
rgpfi = (PFI *) PvAllocZ(cfiForSystem * sizeof(PFI));
|
||
|
|
||
|
// allocate buffered file handles
|
||
|
for (ifi = 0; ifi < cfiForSystem; ifi++) {
|
||
|
pfi = PfiNew();
|
||
|
pfi->ifi = ifi;
|
||
|
if (!pfiFree) {
|
||
|
pfiFree = pfi;
|
||
|
} else {
|
||
|
pfi->pfiNext = pfiFree;
|
||
|
pfiFree = pfi;
|
||
|
}
|
||
|
|
||
|
rgpfi[ifi] = pfi;
|
||
|
}
|
||
|
|
||
|
// set the maximum cache size
|
||
|
cfiCacheClosedMax = cfiCacheClosedT;
|
||
|
|
||
|
// set the mapped i/o flag
|
||
|
fMappedIO = fTryMapped;
|
||
|
DBEXEC_REL(DB_NO_FILE_MAP, fMappedIO = FALSE);
|
||
|
|
||
|
// initialize the CloseOnBadExit array
|
||
|
for (i = 1; i < MAX_WRITEABLE_FILES; i++) {
|
||
|
pfiCloseOnBadExit[i] = NULL;
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
assert(FPfiCheck());
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
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);
|
||
|
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 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
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;
|
||
|
|
||
|
assert(pfi->flags & FI_Mapped);
|
||
|
|
||
|
if (cbSizeNew > pfi->cbMapView) {
|
||
|
// Grow file
|
||
|
|
||
|
if (!ExtendMapView(pfi, cbSizeNew)) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
} 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,
|
||
|
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;
|
||
|
|
||
|
#if 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pfi = LookupCachedFiles(szFileName, flags, mode, &fNewPfi);
|
||
|
assert(pfi);
|
||
|
|
||
|
if (fMappedIO) {
|
||
|
i = MappedOpen(pfi, flags, fNewPfi);
|
||
|
}
|
||
|
|
||
|
if (i == -1) {
|
||
|
BufferedOpen(pfi, flags, mode, fNewPfi);
|
||
|
}
|
||
|
|
||
|
return (pfi->ifi);
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
RoundUp (
|
||
|
DWORD cb,
|
||
|
DWORD cbHigh
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (cb < cbHigh) {
|
||
|
return cbHigh;
|
||
|
}
|
||
|
|
||
|
return RoundUp(cb, (cbHigh << 1));
|
||
|
}
|
||
|
|
||
|
INT
|
||
|
FileOpenMapped (
|
||
|
const char *szFileName,
|
||
|
INT flags,
|
||
|
INT mode,
|
||
|
DWORD *pMapAddr,
|
||
|
DWORD *pcb
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
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.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
file handle.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL fNewPfi = 0;
|
||
|
PFI pfi;
|
||
|
INT i = -1;
|
||
|
|
||
|
#if 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// 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 = RoundUp(pfi->cbSoFar, cbInitialILKMapSize);
|
||
|
}
|
||
|
|
||
|
// Open the file
|
||
|
|
||
|
i = MappedOpen(pfi, flags, fNewPfi);
|
||
|
|
||
|
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 = (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;
|
||
|
}
|
||
|
}
|
||
|
Error(ERR_EXEOPEN, pfi->szFileName, NULL);
|
||
|
}
|
||
|
|
||
|
// 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) {
|
||
|
Error(ERR_EXEOPEN, pfi->szFileName, NULL);
|
||
|
}
|
||
|
|
||
|
// Test for files of size 0 (mapped open will fall through in this case)
|
||
|
if (!statFile.st_size) {
|
||
|
// UNDONE: Why allocate before calling Error?
|
||
|
|
||
|
pfi->rgpbuf = (PPBUF) PvAllocZ(sizeof(PBUF));
|
||
|
|
||
|
Error(ERR_INVALIDEXE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
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 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// 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) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
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("# file handles = %d\n", cfiInSystem));
|
||
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("# cacheable file handles = %d\n",
|
||
|
cfiCacheableInSystem));
|
||
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("size of buffers = %d\n", cbIOBuf));
|
||
|
DBEXEC(DB_BUFVERBOSE, DBPRINT("buffer re-reads = %d\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) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
// flush buffer
|
||
|
if (_write(pfi->fd, pbuf->rgbBuf, cb) != cb) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
FileSeek (
|
||
|
INT fd,
|
||
|
LONG ib,
|
||
|
INT origin
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
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.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Same as lseek().
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
#if 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (rgpfi[fd]->flags & FI_Mapped) {
|
||
|
return (MappedSeek(rgpfi[fd], ib, origin));
|
||
|
}
|
||
|
|
||
|
return(BufferedSeek(rgpfi[fd], ib, origin));
|
||
|
}
|
||
|
|
||
|
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 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// 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) {
|
||
|
cb = pfi->cb - pfi->pbufCur->ibCur;
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
// These asserts are not valid for os/2 (segment will change to ???)
|
||
|
|
||
|
assert((LONG) (pfi->pbufCur->pbCur) <
|
||
|
(LONG) ((LONG) (pfi->pbufCur->rgbBuf) + (LONG) (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 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
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 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
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) + (LONG) (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) {
|
||
|
Error(ERR_INVALIDEXE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
Error(ERR_INVALIDEXE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
// 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,
|
||
|
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 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
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 0;
|
||
|
}
|
||
|
|
||
|
// 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));
|
||
|
|
||
|
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 (0);
|
||
|
}
|
||
|
|
||
|
// 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--;
|
||
|
}
|
||
|
|
||
|
if (cbuf == 0) {
|
||
|
return (1);
|
||
|
} else {
|
||
|
return (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 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
LONG ifi = 0;
|
||
|
|
||
|
for (pfiT = pfiList, ifi = 0; pfiT; pfiT = pfiT->pfiNext, ifi++) {
|
||
|
if (pfiT == pfi) {
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
if (ifi > cfiTot) {
|
||
|
assert(0);
|
||
|
return (0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
#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
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LONG ifi = 0;
|
||
|
PFI pfi;
|
||
|
|
||
|
pfi = pfiFree;
|
||
|
while (pfi) {
|
||
|
ifi++;
|
||
|
if (ifi > cfiTot) {
|
||
|
return (0);
|
||
|
}
|
||
|
pfi = pfi->pfiNext;
|
||
|
}
|
||
|
|
||
|
pfi = pfiOpen;
|
||
|
while (pfi) {
|
||
|
ifi++;
|
||
|
if (ifi > cfiTot) {
|
||
|
return (0);
|
||
|
}
|
||
|
if (!(pfi->flags & FI_Mapped) && pfi->pbufCur != NULL) {
|
||
|
if (pfi->ifi != pfi->pbufCur->ifi) {
|
||
|
return (0);
|
||
|
}
|
||
|
}
|
||
|
pfi = pfi->pfiNext;
|
||
|
}
|
||
|
|
||
|
pfi = pfiClosedHead;
|
||
|
while (pfi) {
|
||
|
ifi++;
|
||
|
if (ifi > cfiTot) {
|
||
|
return (0);
|
||
|
}
|
||
|
if (!(pfi->flags & FI_Mapped) && pfi->pbufCur != NULL) {
|
||
|
if (pfi->ifi != pfi->pbufCur->ifi) {
|
||
|
return (0);
|
||
|
}
|
||
|
}
|
||
|
pfi = pfi->pfiNext;
|
||
|
}
|
||
|
|
||
|
if (cfiTot == ifi) {
|
||
|
return (1);
|
||
|
} else {
|
||
|
return (0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // DBG
|
||
|
|
||
|
|
||
|
INT
|
||
|
MappedOpen(
|
||
|
PFI pfi,
|
||
|
LONG flags,
|
||
|
BOOL fNewPfi)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
NT mapped i/o open.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pfi - logical file descriptor
|
||
|
|
||
|
flags - flags to open file with
|
||
|
|
||
|
fNewPfi - !0 if pfi is new
|
||
|
|
||
|
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) {
|
||
|
pfi->hFile = NULL;
|
||
|
return (-1);
|
||
|
}
|
||
|
|
||
|
if (!fCreate) {
|
||
|
dwSize = GetFileSize(pfi->hFile, NULL);
|
||
|
|
||
|
if (dwSize == 0xFFFFFFFF) {
|
||
|
Error(ERR_INVALIDEXE, NULL, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fWriteable) {
|
||
|
pfi->cbSoFar = fCreate ? 0 : dwSize;
|
||
|
if (!pfi->cbMapView) { // set in the case of FileOpenMapped()
|
||
|
pfi->cbMapView = max(cbMapViewDefault, pfi->cbSoFar);
|
||
|
}
|
||
|
} else {
|
||
|
pfi->cbMapView = pfi->cbSoFar = dwSize;
|
||
|
}
|
||
|
|
||
|
if (!FMapPfi(pfi)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pfi->flags |= FI_Mapped;
|
||
|
} else {
|
||
|
MappedSeek(pfi, 0, SEEK_SET);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
MappedSeek(
|
||
|
PFI pfi,
|
||
|
LONG ib,
|
||
|
INT origin)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
NT mapped i/o seek.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pfi - logical file descriptor
|
||
|
|
||
|
if - offset
|
||
|
|
||
|
origin - type of seek
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
offset seek to
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
switch (origin) {
|
||
|
#if DBG
|
||
|
default:
|
||
|
assert(0);
|
||
|
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)) {
|
||
|
if (pfi->MapAddr == 0) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
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)) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
} 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
|
||
|
|
||
|
--*/
|
||
|
|
||
|
STATIC DWORD
|
||
|
MappedWrite(
|
||
|
PFI pfi,
|
||
|
const void *pv,
|
||
|
DWORD cb)
|
||
|
{
|
||
|
PVOID pvT;
|
||
|
|
||
|
assert(pfi);
|
||
|
|
||
|
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)) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pvT = (PVOID) ((DWORD) (pfi->pvMapView) + (DWORD) (pfi->ibCur));
|
||
|
|
||
|
if ((pfi->ibCur + (LONG) cb) > pfi->cbSoFar) {
|
||
|
pfi->cbSoFar = pfi->ibCur + (LONG) cb;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
|
||
|
#if 1
|
||
|
// UNDONE: UnmapViewOfFile is very expensive on NT. It forces a
|
||
|
// UNDONE: synchronous flush of the dirty pages to disk. By not
|
||
|
// UNDONE: calling the API, the data is written by the lazy writer.
|
||
|
|
||
|
UnmapViewOfFile(pfi->pvMapView);
|
||
|
#endif
|
||
|
|
||
|
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.
|
||
|
|
||
|
SetFilePointer(pfi->hFile, pfi->cbSoFar, NULL, FILE_BEGIN);
|
||
|
SetEndOfFile(pfi->hFile);
|
||
|
}
|
||
|
|
||
|
retval = CloseHandle(pfi->hFile);
|
||
|
|
||
|
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)
|
||
|
// Makes the memory-map bigger for a mapped file. This only makes sense
|
||
|
// for writeable files, not readable files.
|
||
|
{
|
||
|
#if 1
|
||
|
{
|
||
|
#else
|
||
|
if (pfi->MapAddr != 0) {
|
||
|
#endif
|
||
|
// UNDONE: UnmapViewOfFile is very expensive on NT. It forces a
|
||
|
// UNDONE: synchronous flush of the dirty pages to disk. By not
|
||
|
// UNDONE: calling the API, the data is written by the lazy writer.
|
||
|
|
||
|
// Free the existing map.
|
||
|
|
||
|
UnmapViewOfFile(pfi->pvMapView);
|
||
|
}
|
||
|
|
||
|
// 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)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
FMapPfi(PFI pfi)
|
||
|
{
|
||
|
HANDLE hMap;
|
||
|
|
||
|
if (pfi->flags & FI_Write) {
|
||
|
// Extend file to size specified. This works around a problem in
|
||
|
// NT 3.10 where the cache isn't flushed to disk properly.
|
||
|
|
||
|
SetFilePointer(pfi->hFile, pfi->cbMapView, NULL, FILE_BEGIN);
|
||
|
SetEndOfFile(pfi->hFile);
|
||
|
}
|
||
|
|
||
|
hMap = CreateFileMapping(pfi->hFile,
|
||
|
NULL,
|
||
|
(pfi->flags & FI_Write)
|
||
|
? PAGE_READWRITE
|
||
|
: PAGE_READONLY,
|
||
|
0,
|
||
|
pfi->cbMapView,
|
||
|
NULL);
|
||
|
|
||
|
if (hMap == NULL) {
|
||
|
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);
|
||
|
|
||
|
// The handle to the mapping object isn't needed any more
|
||
|
|
||
|
CloseHandle(hMap);
|
||
|
|
||
|
if (!pfi->pvMapView) {
|
||
|
CloseHandle(pfi->hFile);
|
||
|
pfi->hFile = NULL;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
BadExitCleanup(VOID)
|
||
|
// Warning ... may be called asynchronously by the control-C handler thread.
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < MAX_WRITEABLE_FILES; i++) {
|
||
|
if (pfiCloseOnBadExit[i] != NULL) {
|
||
|
UnmapViewOfFile(pfiCloseOnBadExit[i]->pvMapView);
|
||
|
|
||
|
if (pfiCloseOnBadExit[i]->flags & FI_Write) {
|
||
|
// Set end of file to 0, this speeds up the close
|
||
|
|
||
|
SetFilePointer(pfiCloseOnBadExit[i]->hFile, 0, NULL, FILE_BEGIN);
|
||
|
SetEndOfFile(pfiCloseOnBadExit[i]->hFile);
|
||
|
}
|
||
|
|
||
|
CloseHandle(pfiCloseOnBadExit[i]->hFile);
|
||
|
|
||
|
DeleteFile(pfiCloseOnBadExit[i]->szFileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RemoveConvertTempFiles();
|
||
|
|
||
|
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;
|
||
|
|
||
|
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)) {
|
||
|
Error(ERR_NOSPACE, NULL, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((ibStart + cb) > (DWORD) pfi->cbSoFar) {
|
||
|
pfi->cbSoFar = ibStart + cb;
|
||
|
}
|
||
|
|
||
|
return (PUCHAR)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
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
#if 0
|
||
|
if (fCtrlCSignal) {
|
||
|
BadExitCleanup();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// 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]);
|
||
|
}
|