1733 lines
45 KiB
C++
1733 lines
45 KiB
C++
//+--------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1991 - 1992.
|
|
//
|
|
// File: filest.cxx
|
|
//
|
|
// Contents: x86 DOS FAT LStream implementation
|
|
//
|
|
// History: 20-Nov-91 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#include <exphead.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <fcntl.h>
|
|
#include <sys\types.h>
|
|
#include <sys\stat.h>
|
|
#include <share.h>
|
|
#include <dos.h>
|
|
#include <io.h>
|
|
|
|
#include <marshl.hxx>
|
|
#include <time16.hxx>
|
|
#include <dfdeb.hxx>
|
|
|
|
// #define CFSLOG // Use to log file operations
|
|
#include <logfile.hxx>
|
|
|
|
#define DEBOUT_FILEST
|
|
|
|
#ifdef DEBOUT_FILEST
|
|
#define olFileStOut(x) olDebugOut(x)
|
|
#else
|
|
#define olFileStOut(x)
|
|
#endif
|
|
|
|
#define hfChk(e) if ((e) == HFILE_ERROR) olErr(EH_Err, STG_E_UNKNOWN) else 1
|
|
#define hfChkTo(l, e) \
|
|
if ((e) == HFILE_ERROR) olErr(l, STG_E_UNKNOWN) else 1
|
|
#define negChk(e) if ((e) == (ULONG)-1) olErr(EH_Err, STG_E_UNKNOWN) else 1
|
|
#define negChkTo(l, e) \
|
|
if ((e) == (ULONG)-1) olErr(l, STG_E_UNKNOWN) else 1
|
|
|
|
// Number of characters in a volume name (non-null terminated)
|
|
#define CCH_VOLUME 11
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DosDup, private
|
|
//
|
|
// Synopsis: Duplicates a DOS file handle
|
|
//
|
|
// Arguments: [fh] - Existing handle
|
|
//
|
|
// Returns: New handle or INVALID_FH
|
|
//
|
|
// History: 22-Feb-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_DosDup)
|
|
#endif
|
|
|
|
int DosDup(int fh)
|
|
{
|
|
int nfh;
|
|
|
|
__asm
|
|
{
|
|
mov nfh, INVALID_FH
|
|
mov bx, fh
|
|
mov ah, 45h
|
|
clc
|
|
int 21h
|
|
jc dup_fail
|
|
mov nfh, ax
|
|
dup_fail:
|
|
}
|
|
return nfh;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DosGetSectorSize, private
|
|
//
|
|
// Synopsis: Retrieve the sector size for a disk
|
|
//
|
|
// Arguments: [fh] - File handle of file on disk
|
|
// [pcbSector] - Sector size return
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbSector]
|
|
//
|
|
// History: 27-Feb-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_DosGetSectorSize)
|
|
#endif
|
|
|
|
SCODE DosGetSectorSize(int fh, WORD *pcbSector)
|
|
{
|
|
int iDrive, iRt;
|
|
SCODE sc = S_OK;
|
|
|
|
olAssert(fh >= 0);
|
|
|
|
// Get drive number from file handle
|
|
__asm
|
|
{
|
|
mov bx, fh
|
|
mov ax, 4400h
|
|
mov iRt, 0
|
|
clc
|
|
int 21h
|
|
mov iDrive, dx
|
|
jnc drive_succ
|
|
mov iRt, ax
|
|
drive_succ:
|
|
}
|
|
if (iRt != 0)
|
|
olErr(EH_Err, STG_SCODE(iRt));
|
|
|
|
iDrive = (iDrive & 0x3f)+1;
|
|
|
|
// Get sector size
|
|
__asm
|
|
{
|
|
mov ah, 36h
|
|
mov dl, BYTE PTR iDrive
|
|
int 21h
|
|
mov iRt, cx
|
|
cmp ax, 0ffffh
|
|
jne secsize_succ
|
|
mov iRt, 0
|
|
secsize_succ:
|
|
}
|
|
if (iRt == 0)
|
|
sc = STG_E_READFAULT;
|
|
else
|
|
*pcbSector = iRt;
|
|
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DosGetVolumeInfo, private
|
|
//
|
|
// Synopsis: Gets the volume info for a file handle
|
|
//
|
|
// Arguments: [fh] - File handle
|
|
// [cbSector] - Size of a sector on disk
|
|
// [pchVolume] - Name return
|
|
// [pdwId] - Volume Id return (if proper version)
|
|
// [fCheck] - Whether this is for init for for check
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 24-Feb-93 DrewB Created
|
|
// 27-Jan-94 PhilipLa Changed boot sector code
|
|
// to work on DOS 3
|
|
//
|
|
// Note: Please refer to the MS-DOS Programmer's Reference for
|
|
// an explanation of where this information comes from
|
|
// and what all the ugly assembly does.
|
|
//
|
|
// fCheck controls whether this function considers certain
|
|
// types of failure fatal or not. The volume ID check
|
|
// requires a sector-sized buffer which is dynamically
|
|
// allocated. If you are initializing you want this
|
|
// function to fail if it can't get the memory and you
|
|
// don't if you're just checking. In the checking,
|
|
// no-memory case pdwId is *untouched*. This means
|
|
// that you can set *pdwId to your cached ID and call
|
|
// this function for checking. If it couldn't get
|
|
// memory, your ID is unchanged so a comparison will
|
|
// show equality, which what you generally want.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_DosGetVolumeInfo)
|
|
#endif
|
|
|
|
SCODE DosGetVolumeInfo(int fh, WORD cbSector,
|
|
char *pchVolume, DWORD *pdwId, BOOL fCheck)
|
|
{
|
|
SCODE sc = S_OK;
|
|
int iRt, iDrive;
|
|
BYTE extFCB[44] = {0xff, 0, 0, 0, 0, 0, _A_VOLID, 0,
|
|
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?'};
|
|
BYTE dta[64];
|
|
|
|
olAssert(fh >= 0);
|
|
|
|
// Get drive number from file handle
|
|
__asm
|
|
{
|
|
mov bx, fh
|
|
mov ax, 4400h
|
|
mov iRt, 0
|
|
clc
|
|
int 21h
|
|
mov iDrive, dx
|
|
jnc drive_succ
|
|
mov iRt, ax
|
|
drive_succ:
|
|
}
|
|
if (iRt != 0)
|
|
olErr(EH_Err, STG_SCODE(iRt));
|
|
|
|
iDrive = iDrive & 0x3f;
|
|
extFCB[7] = iDrive+1;
|
|
|
|
// Try to get a volume label
|
|
__asm
|
|
{
|
|
//Preserve registers
|
|
push ds
|
|
push es
|
|
|
|
//Save address of current DTA on stack
|
|
mov ah, 2fh
|
|
int 21h
|
|
push es
|
|
push bx
|
|
|
|
//Set DTA to point to our buffer
|
|
mov ax, ss
|
|
mov ds, ax
|
|
lea dx, dta
|
|
mov ah, 1ah
|
|
int 21h
|
|
|
|
//Read volume info
|
|
lea dx, extFCB
|
|
mov ah, 11h
|
|
mov iRt, 0
|
|
clc
|
|
int 21h
|
|
|
|
//Set up error return
|
|
xor ah,ah
|
|
mov iRt, ax
|
|
|
|
//Restore original DTA
|
|
pop dx
|
|
pop ds
|
|
mov ah, 1ah
|
|
int 21h
|
|
|
|
//Restore registers
|
|
pop es
|
|
pop ds
|
|
}
|
|
if (iRt == 0)
|
|
memcpy(pchVolume, dta+8, CCH_VOLUME);
|
|
else
|
|
memset(pchVolume, 0, CCH_VOLUME);
|
|
|
|
// Try to get a volume ID
|
|
BYTE *pbSector;
|
|
|
|
//NOTE: Allocate twice the amount we think we need to get around
|
|
// a DOS/J bug. Bleah.
|
|
pbSector = (BYTE *) DfMemAlloc(cbSector * 2);
|
|
if (pbSector != NULL)
|
|
{
|
|
// Default ID return
|
|
*pdwId = 0;
|
|
|
|
*(WORD *)pbSector = 0;
|
|
pbSector[0x26] = 0;
|
|
__asm
|
|
{
|
|
//Set return code
|
|
mov iRt, 0
|
|
|
|
//Save everything that might need saving
|
|
push bp
|
|
push si
|
|
push di
|
|
push ds
|
|
|
|
//Make the call:
|
|
// CX == Number of sectors to read
|
|
// DX == Logical sector to read
|
|
// DS:BX == Buffer to read into
|
|
// AL == Drive number
|
|
mov cx, 01h
|
|
mov dx, 00h
|
|
mov ax, WORD PTR pbSector + 2
|
|
mov ds, ax
|
|
mov bx, WORD PTR pbSector
|
|
mov al, BYTE PTR iDrive
|
|
clc
|
|
int 25h
|
|
|
|
//int 25h leaves garbage on the stack - get rid of it.
|
|
add sp,2
|
|
|
|
//Restore everything we saved before
|
|
pop ds
|
|
pop di
|
|
pop si
|
|
pop bp
|
|
|
|
jnc volid_succ
|
|
mov iRt, 1
|
|
volid_succ:
|
|
}
|
|
|
|
if (iRt == 0)
|
|
{
|
|
// We read the sector successfully, now check
|
|
// and see if we understand it
|
|
if (pbSector[0] == 0xe9 ||
|
|
(pbSector[0] == 0xeb && pbSector[2] == 0x90) &&
|
|
pbSector[0x26] == 0x29)
|
|
*pdwId = *(DWORD *)(pbSector+0x27);
|
|
}
|
|
else
|
|
// Bad sector read
|
|
sc = STG_E_READFAULT;
|
|
DfMemFree(pbSector);
|
|
}
|
|
else if (!fCheck)
|
|
sc = STG_E_INSUFFICIENTMEMORY;
|
|
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::CheckIdentity, private
|
|
//
|
|
// Synopsis: Attempts to verify that the open file handle
|
|
// refers to the same file
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Algorithm: If tick count < delta then succeeded.
|
|
// Otherwise, DosGetVolumeInfo.
|
|
// If volume is different or volume ID
|
|
// is different, fail. If both are the same, then
|
|
// try _access on the file. If access succeeds
|
|
// succeed.
|
|
//
|
|
// History: 24-Feb-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_CheckIdentity)
|
|
#endif
|
|
|
|
// Set delta in milliseconds
|
|
#define TICK_DELTA 2000
|
|
|
|
SCODE CFileStream::CheckIdentity(void)
|
|
{
|
|
char achVolume[CCH_VOLUME];
|
|
DWORD dwVolId;
|
|
SCODE sc = S_OK;
|
|
DWORD dwTicks;
|
|
|
|
dwTicks = GetTickCount();
|
|
if (dwTicks >= _dwTicks && (dwTicks-_dwTicks) <= TICK_DELTA)
|
|
return S_OK;
|
|
|
|
dwVolId = _dwVolId;
|
|
olChk(DosGetVolumeInfo(_hFile, _cbSector, achVolume, &dwVolId, TRUE));
|
|
|
|
sc = STG_E_INVALIDHANDLE;
|
|
if (memcmp(_achVolume, achVolume, CCH_VOLUME) == 0 && _dwVolId == dwVolId)
|
|
{
|
|
char achOemName[_MAX_PATH];
|
|
|
|
AnsiToOem(_pgfst->GetName(), achOemName);
|
|
if (_access(achOemName, 0) == 0)
|
|
sc = S_OK;
|
|
}
|
|
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetWinTempFile, private
|
|
//
|
|
// Synopsis: Attempts to create a temporary file in the Windows directory
|
|
//
|
|
// Arguments: [pszPrefix] - Prefix of temp file
|
|
// [pszPath] - Path return
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pszPath]
|
|
//
|
|
// History: 29-Jul-93 DrewB Created
|
|
//
|
|
// Notes: Actually creates the file if succesful
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_GetWinTempFile)
|
|
#endif
|
|
|
|
// Number of retries before failure
|
|
#define CTRIES 10
|
|
|
|
SCODE GetWinTempFile(char *pszPrefix, char *pszPath)
|
|
{
|
|
UINT uiRc;
|
|
int cch;
|
|
SCODE sc;
|
|
HFILE hf;
|
|
WORD wRnd;
|
|
int cTry;
|
|
|
|
uiRc = GetWindowsDirectory(pszPath, _MAX_PATH);
|
|
if (uiRc == 0 || uiRc > _MAX_PATH)
|
|
olErr(EH_Err, STG_E_UNKNOWN);
|
|
|
|
// Make sure it ends with a backslash
|
|
cch = strlen(pszPath);
|
|
if (*AnsiPrev(pszPath, pszPath+cch) != '\\')
|
|
{
|
|
pszPath[cch++] = '\\';
|
|
pszPath[cch] = 0;
|
|
}
|
|
|
|
// Generate a "random" value from the current time
|
|
wRnd = (WORD)(GetTickCount() & 0xffff);
|
|
|
|
cTry = 0;
|
|
sc = STG_E_TOOMANYOPENFILES;
|
|
while (cTry < CTRIES)
|
|
{
|
|
sprintf(pszPath+cch, "~%s%04x.tmp", pszPrefix, wRnd);
|
|
|
|
// Check for a previously existing file
|
|
// If nothing exists, see if we can create it
|
|
if (_access(pszPath, 0) != 0 &&
|
|
(hf = _lcreat(pszPath, 0)) != HFILE_ERROR)
|
|
{
|
|
_lclose(hf);
|
|
sc = S_OK;
|
|
break;
|
|
}
|
|
|
|
cTry++;
|
|
|
|
// Jumble the number before trying again
|
|
wRnd ^= (WORD)((GetTickCount() & 0xffff)+cTry);
|
|
}
|
|
|
|
EH_Err:
|
|
#if DBG == 1
|
|
if (FAILED(sc))
|
|
olDebugOut((DEB_IERROR,"GetWinTempFile returned %lx\n",sc));
|
|
#endif
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Init, public
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// Arguments: [pwcsPath] - Path, NULL creates a temporary name
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
// Notes: This Init function does more than the usual init
|
|
// function. Rather than construct an invalid object,
|
|
// this also can construct a fully or partially
|
|
// constructed object. For fully constructed objects,
|
|
// nothing is done other than return. In all other cases,
|
|
// as much construction is done as is necessary. This
|
|
// allows us to do lazy construction of filestream by
|
|
// simply calling Init on a filestream that we want
|
|
// lazily constructed. An initial new'ed filestream
|
|
// will be fully constructed and later Init's will
|
|
// do nothing.
|
|
//
|
|
// [pwcsPath] may be unsafe memory but since this is 16-bit
|
|
// it has already been validated
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_Init) // CFS_Init
|
|
#endif
|
|
|
|
SCODE CFileStream::Init(WCHAR const *pwcsPath)
|
|
{
|
|
char szPath[_MAX_PATH+1];
|
|
SCODE sc;
|
|
int iOpenMode;
|
|
OFSTRUCT of;
|
|
DWORD dwStartFlags;
|
|
BOOL fSetName = FALSE;
|
|
BOOL fCreated = FALSE;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::Init(%ws)\n", pwcsPath));
|
|
|
|
// If we've already been constructed, leave
|
|
if (_hFile != INVALID_FH)
|
|
return S_OK;
|
|
|
|
// If we don't have a name, get one
|
|
if (!_pgfst->HasName())
|
|
{
|
|
// File hasn't been opened yet, so use the start flags
|
|
dwStartFlags = _pgfst->GetStartFlags();
|
|
|
|
if (pwcsPath == NULL)
|
|
{
|
|
char szOemPath[_MAX_PATH+1];
|
|
HFILE hf;
|
|
|
|
// Can't truncate since for temporary files we will
|
|
// always be creating
|
|
olAssert((dwStartFlags & RSF_TRUNCATE) == 0);
|
|
|
|
GetTempFileName(0, "DFT", 0, szOemPath);
|
|
if ((hf = _lcreat(szOemPath, 0)) != HFILE_ERROR)
|
|
{
|
|
_lclose(hf);
|
|
}
|
|
else
|
|
{
|
|
olChk(GetWinTempFile("DFT", szOemPath));
|
|
}
|
|
OemToAnsi(szOemPath, szPath);
|
|
|
|
// We created the temp file, so we just want to open it
|
|
dwStartFlags = (dwStartFlags & ~RSF_CREATE) | RSF_OPEN;
|
|
}
|
|
else
|
|
{
|
|
if (wcstombs(szPath, pwcsPath, _MAX_PATH) == (size_t)-1)
|
|
olErr(EH_Err, STG_E_INVALIDNAME);
|
|
}
|
|
_pgfst->SetName(szPath);
|
|
fSetName = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Use the name somebody else gave us
|
|
strcpy(szPath, _pgfst->GetName());
|
|
|
|
// File has already been started, so just open it
|
|
dwStartFlags = (_pgfst->GetStartFlags() & ~RSF_CREATEFLAGS) | RSF_OPEN;
|
|
}
|
|
|
|
// Open the file
|
|
if (!P_WRITE(_pgfst->GetDFlags()))
|
|
iOpenMode = OF_READ;
|
|
else
|
|
iOpenMode = OF_READWRITE;
|
|
if (P_DENYWRITE(_pgfst->GetDFlags()) && !P_WRITE(_pgfst->GetDFlags()))
|
|
iOpenMode |= OF_SHARE_DENY_WRITE;
|
|
else
|
|
iOpenMode |= OF_SHARE_DENY_NONE;
|
|
|
|
// Make sure we're not attempting to create/truncate a read-only thing
|
|
olAssert(iOpenMode != OF_READ ||
|
|
!(dwStartFlags & (RSF_CREATE | RSF_TRUNCATE)));
|
|
|
|
_hFile = OpenFile(szPath, &of, iOpenMode);
|
|
if (dwStartFlags & RSF_CREATE)
|
|
{
|
|
if (_hFile < 0)
|
|
_hFile = OpenFile(szPath, &of, iOpenMode | OF_CREATE);
|
|
else if (((dwStartFlags & RSF_TRUNCATE) == 0) &&
|
|
((dwStartFlags & RSF_OPENCREATE) == 0))
|
|
olErr(EH_hFile, STG_E_FILEALREADYEXISTS);
|
|
if (_hFile < 0)
|
|
{
|
|
olErr(EH_Path, STG_SCODE(of.nErrCode));
|
|
}
|
|
else
|
|
{
|
|
olVerify(_lclose(_hFile) != HFILE_ERROR);
|
|
_hFile = OpenFile(szPath, &of, iOpenMode);
|
|
fCreated = TRUE;
|
|
}
|
|
}
|
|
if (_hFile < 0)
|
|
olErr(EH_Path, STG_SCODE(of.nErrCode));
|
|
|
|
//Check to see if this name is a device - if it is, return
|
|
// STG_E_INVALIDNAME
|
|
|
|
if (pwcsPath != NULL)
|
|
{
|
|
//We only need to check if the user passed in the name.
|
|
int iRetval;
|
|
int fd = _hFile;
|
|
__asm
|
|
{
|
|
mov ax, 4400h
|
|
mov bx, fd
|
|
int 21h
|
|
mov iRetval, dx
|
|
}
|
|
if (iRetval & 0x80)
|
|
{
|
|
//We are a device, so we couldn't have created this
|
|
// handle. Reset fCreated so we don't try to delete
|
|
// the device name in the cleanup path.
|
|
fCreated = FALSE;
|
|
olErr(EH_hFile, STG_E_INVALIDNAME);
|
|
}
|
|
}
|
|
|
|
|
|
// Set name to fully qualified path return in OFSTRUCT
|
|
// This removes any current-directory dependencies in the name
|
|
if (fSetName)
|
|
OemToAnsi(of.szPathName, _pgfst->GetName());
|
|
|
|
if (dwStartFlags & RSF_TRUNCATE)
|
|
{
|
|
hfChkTo(EH_hFile, _llseek(_hFile, 0, STREAM_SEEK_SET));
|
|
hfChkTo(EH_hFile, _lwrite(_hFile, szPath, 0));
|
|
}
|
|
|
|
_fFixedDisk = of.fFixedDisk;
|
|
if (!_fFixedDisk)
|
|
{
|
|
// These errors are not critical errors - if either of
|
|
// the following calls fail, we just turn off the floppy
|
|
// checking.
|
|
|
|
sc = DosGetSectorSize(_hFile, &_cbSector);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
sc = DosGetVolumeInfo(
|
|
_hFile,
|
|
_cbSector,
|
|
_achVolume,
|
|
&_dwVolId,
|
|
FALSE);
|
|
}
|
|
|
|
if (FAILED(sc))
|
|
{
|
|
_fFixedDisk = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::Init\n"));
|
|
return S_OK;
|
|
|
|
EH_hFile:
|
|
olVerify(_lclose(_hFile) != HFILE_ERROR);
|
|
_hFile = INVALID_FH;
|
|
if (fCreated)
|
|
OpenFile(szPath, &of, OF_DELETE);
|
|
EH_Path:
|
|
if (fSetName)
|
|
_pgfst->SetName(NULL);
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::~CFileStream, public
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_1CFS) // CFS_Shutdown
|
|
#endif
|
|
|
|
CFileStream::~CFileStream(void)
|
|
{
|
|
#ifdef FULL_CHECK_IDENTITY
|
|
SCODE scIdentity;
|
|
#endif
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::~CFileStream()\n"));
|
|
olAssert(_cReferences == 0);
|
|
_sig = CFILESTREAM_SIGDEL;
|
|
|
|
#ifdef FULL_CHECK_IDENTITY
|
|
if (_fFixedDisk || _hFile == INVALID_FH ||
|
|
SUCCEEDED(scIdentity = CheckIdentity()))
|
|
{
|
|
#endif
|
|
if (_hFile >= 0)
|
|
olVerify(_lclose(_hFile) != HFILE_ERROR);
|
|
if (_hReserved >= 0)
|
|
olVerify(_lclose(_hReserved) != HFILE_ERROR);
|
|
#ifdef FULL_CHECK_IDENTITY
|
|
}
|
|
#endif
|
|
if (_pgfst)
|
|
{
|
|
_pgfst->Remove(this);
|
|
if (_pgfst->HasName())
|
|
{
|
|
if (_pgfst->GetRefCount() == 1)
|
|
{
|
|
if ((_pgfst->GetStartFlags() & RSF_DELETEONRELEASE)
|
|
#ifdef FULL_CHECK_IDENTITY
|
|
&& (_fFixedDisk || _hFile == INVALID_FH ||
|
|
SUCCEEDED(scIdentity))
|
|
#endif
|
|
)
|
|
{
|
|
OFSTRUCT of;
|
|
|
|
// This is allowed to fail if somebody else has
|
|
// the file open
|
|
OpenFile(_pgfst->GetName(), &of, OF_DELETE);
|
|
}
|
|
}
|
|
}
|
|
_pgfst->Release();
|
|
}
|
|
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::~CFileStream\n"));
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ReadAt, public
|
|
//
|
|
// Synopsis: Reads bytes at a specific point in a stream
|
|
//
|
|
// Arguments: [ulPosition] - Offset in file to read at
|
|
// [pb] - Buffer
|
|
// [cb] - Count of bytes to read
|
|
// [pcbRead] - Return of bytes read
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbRead]
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_ReadAt) //
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::ReadAt(ULARGE_INTEGER ulPosition,
|
|
VOID HUGEP *pb,
|
|
ULONG cb,
|
|
ULONG *pcbRead)
|
|
{
|
|
SCODE sc;
|
|
ULONG cbRead = 0;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::ReadAt(%lu, %p, %lu, %p)\n",
|
|
ULIGetLow(ulPosition), pb, cb, pcbRead));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
if (pcbRead)
|
|
{
|
|
olChk(ValidateOutBuffer(pcbRead, sizeof(ULONG)));
|
|
#endif
|
|
*pcbRead = 0;
|
|
#ifdef CFS_SECURE
|
|
}
|
|
olChk(ValidateHugeOutBuffer(pb, cb));
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
olAssert(ULIGetHigh(ulPosition) == 0);
|
|
hfChk(_llseek(_hFile, (LONG)ULIGetLow(ulPosition), STREAM_SEEK_SET));
|
|
negChk(cbRead = _hread(_hFile, pb, (long)cb));
|
|
if (pcbRead)
|
|
*pcbRead = cbRead;
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::ReadAt => %lu\n", cbRead));
|
|
EH_Err:
|
|
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::ReadAt(%ld, %ld), cbRead = %ld, sc = %lx\n",
|
|
this, ULIGetLow(ulPosition), cb, cbRead, sc));
|
|
#endif
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::WriteAt, public
|
|
//
|
|
// Synopsis: Writes bytes at a specific point in a stream
|
|
//
|
|
// Arguments: [ulPosition] - Offset in file
|
|
// [pb] - Buffer
|
|
// [cb] - Count of bytes to write
|
|
// [pcbWritten] - Return of bytes written
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbWritten]
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_WriteAt)
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::WriteAt(ULARGE_INTEGER ulPosition,
|
|
VOID const HUGEP *pb,
|
|
ULONG cb,
|
|
ULONG *pcbWritten)
|
|
{
|
|
SCODE sc;
|
|
ULONG cbWritten = 0;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::WriteAt(%lu, %p, %lu, %p)\n",
|
|
ULIGetLow(ulPosition), pb, cb, pcbWritten));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
if (pcbWritten)
|
|
{
|
|
olChk(ValidateOutBuffer(pcbWritten, sizeof(ULONG)));
|
|
#endif
|
|
*pcbWritten = 0;
|
|
#ifdef CFS_SECURE
|
|
}
|
|
olChk(ValidateHugeBuffer(pb, cb));
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
#if DBG == 1
|
|
ULONG ulCurrentSize;
|
|
|
|
hfChk(ulCurrentSize = _llseek(_hFile, 0, STREAM_SEEK_END));
|
|
|
|
if ((ULIGetLow(ulPosition) + cb) > ulCurrentSize)
|
|
{
|
|
if (SimulateFailure(DBF_DISKFULL))
|
|
{
|
|
olErr(EH_Err, STG_E_MEDIUMFULL);
|
|
}
|
|
}
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
|
|
olAssert(ULIGetHigh(ulPosition) == 0);
|
|
if (cb == 0)
|
|
olErr(EH_Err, S_OK);
|
|
hfChk(_llseek(_hFile, (LONG)ULIGetLow(ulPosition), STREAM_SEEK_SET));
|
|
negChk(cbWritten = _hwrite(_hFile, pb, (long)cb));
|
|
if (cbWritten < cb)
|
|
olErr(EH_Err, STG_E_MEDIUMFULL);
|
|
if (pcbWritten)
|
|
*pcbWritten = cbWritten;
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::WriteAt => %lu\n",
|
|
cbWritten));
|
|
EH_Err:
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::WriteAt(%ld, %ld), cbWritten = %ld, sc = %lx\n",
|
|
this, ULIGetLow(ulPosition), cb, cbWritten, sc));
|
|
#endif
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::FlushCache, public
|
|
//
|
|
// Synopsis: Flushes buffers
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 24-Mar-92 DrewB Created
|
|
// 12-Feb-93 AlexT Flush -> FlushCache
|
|
//
|
|
// Notes: Flush as thoroughly as possible
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_FlushCache)
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::FlushCache(void)
|
|
{
|
|
int iRt, fd;
|
|
unsigned int uiVer;
|
|
SCODE sc;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::FlushCache()\n"));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
__asm
|
|
{
|
|
mov ax, 3000h
|
|
clc
|
|
int 21h
|
|
xchg al, ah
|
|
mov uiVer, ax
|
|
}
|
|
if (uiVer >= 0x31E)
|
|
{
|
|
// Function 68h is only supported on DOS 3.3 and later
|
|
fd = _hFile;
|
|
// Commit file
|
|
__asm
|
|
{
|
|
mov iRt, 0
|
|
mov ah, 068h
|
|
mov bx, fd
|
|
clc // DOS doesn't properly clear this
|
|
int 21h
|
|
jnc flush_succ
|
|
mov iRt, ax
|
|
flush_succ:
|
|
}
|
|
if (iRt != 0)
|
|
olErr(EH_Err, STG_SCODE(iRt));
|
|
}
|
|
else
|
|
{
|
|
if (_hReserved == INVALID_FH)
|
|
{
|
|
// We try to pick up a handle here in two cases:
|
|
// a) We tried to reserve one before and failed
|
|
// b) We're not supposed to have a reserve handle
|
|
|
|
_hReserved = DosDup(_hFile);
|
|
if (_hReserved == INVALID_FH)
|
|
olErr(EH_Err, STG_E_TOOMANYOPENFILES);
|
|
}
|
|
|
|
olVerify(_lclose(_hReserved) != HFILE_ERROR);
|
|
|
|
if (_grfLocal & LFF_RESERVE_HANDLE)
|
|
{
|
|
// Reacquire a reserve handle (doesn't matter if we fail)
|
|
_hReserved = DosDup(_hFile);
|
|
}
|
|
else
|
|
_hReserved = INVALID_FH;
|
|
|
|
}
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::FlushCache\n"));
|
|
EH_Err:
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::FlushCache() sc = %lx\n",
|
|
this, sc));
|
|
#endif
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Flush, public
|
|
//
|
|
// Synopsis: Flushes buffers
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 24-Mar-92 DrewB Created
|
|
// 12-Feb-93 AlexT Use dup/close
|
|
//
|
|
// Notes: We flush DOS (but not any disk cache)
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_Flush)
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::Flush(void)
|
|
{
|
|
BOOL fhrValid = FALSE;
|
|
HRESULT hr;
|
|
SCODE sc = S_OK;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::Flush()\n"));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
if (_hReserved == INVALID_FH)
|
|
{
|
|
// We try to pick up a handle here in two cases:
|
|
// a) We tried to reserve one before and failed
|
|
// b) We're not supposed to have a reserve handle
|
|
|
|
_hReserved = DosDup(_hFile);
|
|
if (_hReserved == INVALID_FH)
|
|
{
|
|
// We couldn't dup; flush everything (to be safe)
|
|
hr = FlushCache();
|
|
fhrValid = TRUE;
|
|
}
|
|
}
|
|
if (_hReserved != INVALID_FH)
|
|
{
|
|
// Close the dup'd handle, flushing changes out of DOS
|
|
olVerify(_lclose(_hReserved) != HFILE_ERROR);
|
|
|
|
if (_grfLocal & LFF_RESERVE_HANDLE)
|
|
{
|
|
// Reacquire a reserve handle (doesn't matter if we fail)
|
|
_hReserved = DosDup(_hFile);
|
|
}
|
|
else
|
|
_hReserved = INVALID_FH;
|
|
|
|
}
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#endif
|
|
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::Flush\n"));
|
|
if (!fhrValid)
|
|
hr = ResultFromScode(sc);
|
|
EH_Err:
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::Flush() sc = %lx\n", this, sc));
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SetSize, public
|
|
//
|
|
// Synopsis: Sets the size of the LStream
|
|
//
|
|
// Arguments: [ulSize] - New size
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_SetSize)
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::SetSize(ULARGE_INTEGER ulSize)
|
|
{
|
|
SCODE sc;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::SetSize(%lu)\n",
|
|
ULIGetLow(ulSize)));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
olAssert(ULIGetHigh(ulSize) == 0);
|
|
|
|
ULARGE_INTEGER ulCurrentSize;
|
|
hfChk(ULISetLow(ulCurrentSize, _llseek(_hFile, 0, STREAM_SEEK_END)));
|
|
|
|
hfChk(_llseek(_hFile, (LONG)ULIGetLow(ulSize), STREAM_SEEK_SET));
|
|
hfChk(_lwrite(_hFile, &sc, 0));
|
|
|
|
if (ULIGetLow(ulSize) > ULIGetLow(ulCurrentSize))
|
|
{
|
|
// Make sure we didn't run out of disk space
|
|
LONG lNewSize;
|
|
|
|
hfChk(lNewSize = _llseek(_hFile, 0, STREAM_SEEK_END));
|
|
if (lNewSize < (LONG)ULIGetLow(ulSize)
|
|
#if DBG == 1
|
|
|| SimulateFailure(DBF_DISKFULL)
|
|
#endif
|
|
)
|
|
{
|
|
// we were unable to allocate enough space;
|
|
// reset to ulCurrentSize (to conserve space) if possible
|
|
|
|
if (_llseek(_hFile, (LONG)ULIGetLow(ulCurrentSize),
|
|
STREAM_SEEK_SET) ==
|
|
(LONG)ULIGetLow(ulCurrentSize))
|
|
{
|
|
_lwrite(_hFile, &sc, 0);
|
|
}
|
|
olErr(EH_Err, STG_E_MEDIUMFULL);
|
|
}
|
|
}
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::SetSize\n"));
|
|
EH_Err:
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::SetSize(%ld) sc = %lx\n",
|
|
this, ULIGetLow(ulSize), sc));
|
|
#endif
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::LockRegion, public
|
|
//
|
|
// Synopsis: Gets a lock on a portion of the LStream
|
|
//
|
|
// Arguments: [ulStartOffset] - Lock start
|
|
// [cbLockLength] - Length
|
|
// [dwLockType] - Exclusive/Read only
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_LockRegion)
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::LockRegion(ULARGE_INTEGER ulStartOffset,
|
|
ULARGE_INTEGER cbLockLength,
|
|
DWORD dwLockType)
|
|
{
|
|
int iRt, fd;
|
|
SCODE sc;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::LockRegion(%lu, %lu, %lu)\n",
|
|
ULIGetLow(ulStartOffset), ULIGetLow(cbLockLength),
|
|
dwLockType));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
if (!VALID_LOCKTYPE(dwLockType))
|
|
olErr(EH_Err, STG_E_INVALIDFUNCTION);
|
|
if (dwLockType != LOCK_EXCLUSIVE && dwLockType != LOCK_ONLYONCE)
|
|
olErr(EH_Err, STG_E_INVALIDFUNCTION);
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
olAssert(ULIGetHigh(ulStartOffset) == 0);
|
|
olAssert(ULIGetHigh(cbLockLength) == 0);
|
|
fd = _hFile;
|
|
__asm
|
|
{
|
|
mov ax, 5C00H
|
|
mov bx, fd
|
|
// Assumes low DWORD is first in ULARGE_INTEGER
|
|
mov cx, WORD PTR ulStartOffset+2
|
|
mov dx, WORD PTR ulStartOffset
|
|
mov si, WORD PTR cbLockLength+2
|
|
mov di, WORD PTR cbLockLength
|
|
mov iRt, 0
|
|
clc
|
|
int 21h
|
|
jnc grl_noerror
|
|
mov iRt, ax
|
|
grl_noerror:
|
|
}
|
|
if (iRt != 0)
|
|
{
|
|
if (iRt == 1)
|
|
{
|
|
// INVALID_FUNCTION is impossible because the
|
|
// function is hardcoded. This means locking
|
|
// isn't supported
|
|
olErr(EH_Err, STG_E_SHAREREQUIRED);
|
|
}
|
|
else
|
|
olErr(EH_Err, STG_SCODE(iRt));
|
|
}
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::LockRegion\n"));
|
|
EH_Err:
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::LockRegion(%lx, %lx) sc = %lx\n",
|
|
this, ULIGetLow(ulStartOffset), ULIGetLow(cbLockLength), sc));
|
|
#endif
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::UnlockRegion, public
|
|
//
|
|
// Synopsis: Releases an existing lock
|
|
//
|
|
// Arguments: [ulStartOffset] - Lock start
|
|
// [cbLockLength] - Length
|
|
// [dwLockType] - Lock type
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
// Notes: Must match an existing lock exactly
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_UnlockRegion)
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::UnlockRegion(ULARGE_INTEGER ulStartOffset,
|
|
ULARGE_INTEGER cbLockLength,
|
|
DWORD dwLockType)
|
|
{
|
|
int iRt, fd;
|
|
SCODE sc;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::UnlockRegion(%lu, %lu)\n",
|
|
ULIGetLow(ulStartOffset), ULIGetLow(cbLockLength),
|
|
dwLockType));
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
if (!VALID_LOCKTYPE(dwLockType))
|
|
olErr(EH_Err, STG_E_INVALIDFUNCTION);
|
|
if (dwLockType != LOCK_EXCLUSIVE && dwLockType != LOCK_ONLYONCE)
|
|
olErr(EH_Err, STG_E_INVALIDFUNCTION);
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
#ifdef FULL_CHECK_IDENTITY
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
#endif
|
|
olAssert(ULIGetHigh(ulStartOffset) == 0);
|
|
olAssert(ULIGetHigh(cbLockLength) == 0);
|
|
fd = _hFile;
|
|
__asm
|
|
{
|
|
mov ax, 5C01H
|
|
mov bx, fd
|
|
// Assumes low DWORD is first in ULARGE_INTEGER
|
|
mov cx, WORD PTR ulStartOffset+2
|
|
mov dx, WORD PTR ulStartOffset
|
|
mov si, WORD PTR cbLockLength+2
|
|
mov di, WORD PTR cbLockLength
|
|
mov iRt, 0
|
|
clc
|
|
int 21h
|
|
jnc rrl_noerror
|
|
mov iRt, ax
|
|
rrl_noerror:
|
|
}
|
|
if (iRt != 0)
|
|
olErr(EH_Err, STG_SCODE(iRt));
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
#ifdef FULL_CHECK_IDENTITY
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
#endif
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::UnlockRegion\n"));
|
|
EH_Err:
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::UnlockRegion(%lx, %lx) sc = %lx\n",
|
|
this, ULIGetLow(ulStartOffset), ULIGetLow(cbLockLength), sc));
|
|
#endif
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Stat, public
|
|
//
|
|
// Synopsis: Fills in a stat buffer for this object
|
|
//
|
|
// Arguments: [pstatstg] - Buffer
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pstatstg]
|
|
//
|
|
// History: 25-Mar-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_Stat) // Stat_TEXT
|
|
#endif
|
|
|
|
_OLESTDMETHODIMP CFileStream::Stat(STATSTGW *pstatstg, DWORD grfStatFlag)
|
|
{
|
|
int iRt, fd;
|
|
SCODE sc;
|
|
WORD tm, dt;
|
|
|
|
olFileStOut((DEB_ITRACE, "In CFileStream::Stat(%p)\n", pstatstg));
|
|
|
|
#ifdef CFSLOG
|
|
olLog(("%p CFS::Stat(%ld)\n", this, grfStatFlag));
|
|
#endif
|
|
|
|
#ifdef CFS_SECURE
|
|
TRY
|
|
{
|
|
olChkTo(EH_RetSc, ValidateOutBuffer(pstatstg, sizeof(STATSTGW)));
|
|
olAssert(SUCCEEDED(VerifyStatFlag(grfStatFlag)));
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
ULISetHigh(pstatstg->cbSize, 0);
|
|
hfChk(ULISetLow(pstatstg->cbSize,
|
|
_llseek(_hFile, 0, STREAM_SEEK_END)));
|
|
fd = _hFile;
|
|
// Retrieve file time
|
|
__asm
|
|
{
|
|
mov iRt, 0
|
|
mov bx, fd
|
|
mov ax, 05700h
|
|
clc
|
|
int 21h
|
|
mov tm, cx
|
|
mov dt, dx
|
|
jnc time_succ
|
|
mov iRt, ax
|
|
time_succ:
|
|
}
|
|
if (iRt != 0)
|
|
olErr(EH_Err, STG_SCODE(iRt));
|
|
|
|
if (!CoDosDateTimeToFileTime(dt, tm, &pstatstg->mtime))
|
|
olErr(EH_Err, STG_E_UNKNOWN);
|
|
pstatstg->ctime.dwLowDateTime = pstatstg->ctime.dwHighDateTime = 0;
|
|
pstatstg->atime.dwLowDateTime = pstatstg->atime.dwHighDateTime = 0;
|
|
|
|
olHVerSucc(GetLocksSupported(&pstatstg->grfLocksSupported));
|
|
pstatstg->type = STGTY_LOCKBYTES;
|
|
pstatstg->grfMode = DFlagsToMode(_pgfst->GetDFlags());
|
|
pstatstg->pwcsName = NULL;
|
|
if ((grfStatFlag & STATFLAG_NONAME) == 0)
|
|
{
|
|
olChk(GetName(&pstatstg->pwcsName));
|
|
}
|
|
#ifdef CFS_SECURE
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
sc = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
#else
|
|
sc = S_OK;
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
olFileStOut((DEB_ITRACE, "Out CFileStream::Stat\n"));
|
|
return NOERROR;
|
|
|
|
EH_Err:
|
|
#ifdef CFS_SECURE
|
|
memset(pstatstg, 0, sizeof(STATSTGW));
|
|
EH_RetSc:
|
|
#endif
|
|
_OLERETURN(sc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SwitchToFile, public
|
|
//
|
|
// Synopsis: Changes the file this filestream uses
|
|
//
|
|
// Arguments: [ptcsFile] - File name
|
|
// [ulCommitSize] -- Size needed to do overwrite commit
|
|
// [cbBuffer] - Buffer size
|
|
// [pvBuffer] - Buffer for file copying
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 08-Jan-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_SwitchToFile) // CFS_SwitchToFile
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::SwitchToFile(TCHAR const *ptcsFile,
|
|
ULONG ulCommitSize,
|
|
ULONG cbBuffer,
|
|
void *pvBuffer)
|
|
{
|
|
SCODE sc;
|
|
UINT cbRead, cbWritten;
|
|
HFILE hOldFile;
|
|
TCHAR atcOldName[_MAX_PATH];
|
|
WCHAR wcsFile[_MAX_PATH];
|
|
DWORD dwOldStartFlags;
|
|
OFSTRUCT of;
|
|
|
|
olDebugOut((DEB_ITRACE, "In CFileStream::SwitchToFile:%p(%s, %lu, %p)\n",
|
|
this, ptcsFile, cbBuffer, pvBuffer));
|
|
|
|
#ifdef CFS_SECURE
|
|
olChk(Validate());
|
|
olChk(CheckHandle());
|
|
#else
|
|
olAssert(_hFile != INVALID_FH);
|
|
#endif
|
|
if (!_fFixedDisk)
|
|
olChk(CheckIdentity());
|
|
|
|
// Check for marshals
|
|
if (_pgfst->GetRefCount() != 1)
|
|
olErr(EH_Err, STG_E_EXTANTMARSHALLINGS);
|
|
|
|
// Seek to beginning
|
|
hfChk(_llseek(_hFile, 0, STREAM_SEEK_SET));
|
|
|
|
// Preserve old file information
|
|
tcscpy(atcOldName, _pgfst->GetName());
|
|
hOldFile = _hFile;
|
|
dwOldStartFlags = _pgfst->GetStartFlags();
|
|
|
|
// Set file information to prepare for new Init
|
|
_pgfst->SetName(NULL);
|
|
_hFile = INVALID_FH;
|
|
_pgfst->SetStartFlags((dwOldStartFlags & ~(RSF_CREATEFLAGS |
|
|
RSF_CONVERT |
|
|
RSF_DELETEONRELEASE |
|
|
RSF_OPEN)) |
|
|
RSF_CREATE);
|
|
|
|
// Release reserved file handle so it can be consumed
|
|
if (_hReserved >= 0)
|
|
{
|
|
olVerify(_lclose(_hReserved) != HFILE_ERROR);
|
|
_hReserved = INVALID_FH;
|
|
}
|
|
|
|
// Attempt to create new file
|
|
if (mbstowcs(wcsFile, ptcsFile, _MAX_PATH) == (size_t)-1)
|
|
olErr(EH_ReplaceOld, STG_E_INVALIDNAME);
|
|
olChkTo(EH_ReplaceOld, Init(wcsFile));
|
|
|
|
ULARGE_INTEGER ulNewSize;
|
|
ULISet32(ulNewSize, ulCommitSize);
|
|
|
|
// SetSize to minimum commit size
|
|
olHChkTo(EH_NewFile, SetSize(ulNewSize));
|
|
// SetSize changes the file pointer, so move it back to the beginning
|
|
hfChkTo(EH_NewFile, _llseek(_hFile, 0, STREAM_SEEK_SET));
|
|
|
|
// Copy file contents
|
|
for (;;)
|
|
{
|
|
negChkTo(EH_NewFile,
|
|
cbRead = _lread(hOldFile, pvBuffer, (UINT)cbBuffer));
|
|
if (cbRead == 0)
|
|
// EOF
|
|
break;
|
|
negChkTo(EH_NewFile,
|
|
cbWritten = _lwrite(_hFile, pvBuffer, cbRead));
|
|
if (cbWritten != cbRead)
|
|
olErr(EH_NewFile, STG_E_WRITEFAULT);
|
|
}
|
|
|
|
olVerify(_lclose(hOldFile) != HFILE_ERROR);
|
|
if (dwOldStartFlags & RSF_DELETEONRELEASE)
|
|
{
|
|
// This is allowed to fail if somebody else has
|
|
// the file open
|
|
OpenFile(atcOldName, &of, OF_DELETE);
|
|
}
|
|
|
|
if (_grfLocal & LFF_RESERVE_HANDLE)
|
|
{
|
|
// Nothing we can do about errors here
|
|
_hReserved = DosDup(_hFile);
|
|
}
|
|
|
|
if (!_fFixedDisk)
|
|
_dwTicks = GetTickCount();
|
|
|
|
olDebugOut((DEB_ITRACE, "Out CFileStream::SwitchToFile\n"));
|
|
return NOERROR;
|
|
|
|
EH_NewFile:
|
|
olVerify(_lclose(_hFile) != HFILE_ERROR);
|
|
olVerify(OpenFile(_pgfst->GetName(), &of, OF_DELETE) != HFILE_ERROR);
|
|
EH_ReplaceOld:
|
|
_pgfst->SetName(atcOldName);
|
|
_hFile = hOldFile;
|
|
_pgfst->SetStartFlags(dwOldStartFlags);
|
|
if (_grfLocal & LFF_RESERVE_HANDLE)
|
|
// Nothing we can do about errors here
|
|
_hReserved = DosDup(_hFile);
|
|
EH_Err:
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Delete, public
|
|
//
|
|
// Synopsis: Closes and deletes the file, errors ignored
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 09-Feb-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_Delete) // CFS_Shutdown
|
|
#endif
|
|
|
|
void CFileStream::Delete(void)
|
|
{
|
|
OFSTRUCT of;
|
|
|
|
olDebugOut((DEB_ITRACE, "In CFileStream::Delete:%p()\n", this));
|
|
if (_hFile >= 0)
|
|
_lclose(_hFile);
|
|
_hFile = INVALID_FH;
|
|
if (_hReserved >= 0)
|
|
_lclose(_hReserved);
|
|
_hReserved = INVALID_FH;
|
|
OpenFile(_pgfst->GetName(), &of, OF_DELETE);
|
|
olDebugOut((DEB_ITRACE, "Out CFileStream::Delete\n"));
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ReserveHandle, public
|
|
//
|
|
// Synopsis: Reserves a backup file handle for handle-required operations
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 01-Jul-93 DrewB Created
|
|
//
|
|
// Notes: May be called with a handle already reserved
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_ReserveHandle) // CFS_ReserveHandle
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::ReserveHandle(void)
|
|
{
|
|
SCODE sc;
|
|
|
|
olDebugOut((DEB_ITRACE, "In CFileStream::ReserveHandle:%p()\n", this));
|
|
if (_hReserved == INVALID_FH &&
|
|
(_hReserved = DosDup(_hFile)) == INVALID_FH)
|
|
{
|
|
sc = STG_E_TOOMANYOPENFILES;
|
|
}
|
|
else
|
|
{
|
|
sc = S_OK;
|
|
_grfLocal |= LFF_RESERVE_HANDLE;
|
|
}
|
|
olDebugOut((DEB_ITRACE, "Out CFileStream::ReserveHandle => %lX\n", sc));
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::GetSize, public
|
|
//
|
|
// Synopsis: Return the size of the stream
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 12-Jul-93 AlexT Created
|
|
//
|
|
// Notes: This is a separate method from Stat as an optimization
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef CODESEGMENTS
|
|
#pragma code_seg(SEG_CFS_GetSize) // CFS_ReserveHandle
|
|
#endif
|
|
|
|
STDMETHODIMP CFileStream::GetSize(ULARGE_INTEGER *puliSize)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
ULISetHigh(*puliSize, 0);
|
|
hfChk(ULISetLow(*puliSize, _llseek(_hFile, 0, STREAM_SEEK_END)));
|
|
|
|
EH_Err:
|
|
return(ResultFromScode(sc));
|
|
}
|