windows-nt/Source/XPSP1/NT/enduser/stuff/itircl/common/filesys/subfile.c
2020-09-26 16:20:57 +08:00

2860 lines
70 KiB
C

/*****************************************************************************
* *
* VFILE.C *
* *
* Copyright (C) Microsoft Corporation 1995. *
* All Rights reserved. *
* *
******************************************************************************
* *
* Module Intent *
* *
* "Virtual File" functions - Actual data may reside in a parent file at *
* any given offset, or in a temp file. *
* *
******************************************************************************
* *
* Current Owner: davej *
*****************************************************************************
* Notes:
*
* #ifndef ITWRAP added around all calls that have a wrapped implementation
* (wrapstor.lib provides calls into the InfoTech IStorage/IStream
* implementation)
*
*****************************************************************************/
/*****************************************************************************
*
* Created 07/17/95 - davej
*
*****************************************************************************/
static char s_aszModule[] = __FILE__; /* For error report */
#include <mvopsys.h>
#include <iterror.h>
#include <orkin.h>
#include <misc.h>
#include <wrapstor.h>
#include <_mvutil.h>
/*****************************************************************************
* *
* Globals *
* *
*****************************************************************************/
SF FAR * mv_gsfa = NULL; // Subfile headers (better/faster than globalalloc'ing them)
LONG mv_gsfa_count = -1L; // User count, equals number of MV titles opened or -1 for not init
CRITICAL_SECTION mv_gsfa_cs; // Ensure accessing the array is OK in multi-threaded environment
/*****************************************************************************
* *
* Defines *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Prototypes *
* *
*****************************************************************************/
#ifdef _DEBUGMVFS
void DumpMV_GSFA(void);
#endif
/***************************************************************************
* *
* Private Functions *
* *
***************************************************************************/
/***************************************************************************
*
* @doc PRIVATE
*
* @func int PASCAL NEAR | SFHeaderSize |
* Get the 'extra' header size for a sub file (the part that lives with
* the file data itself)
*
* @parm QSFH | qsfh |
* Pointer to subfile header block (contains file length and flags)
*
* @rdesc Return the number of bytes of header that exist before the
* file data
*
* @comm
* Currently this only returns zero, but if at the file's starting location,
* there exists a header, (check the <p qsfh> <e bFlags> element to determine
* the number of bytes being used for the file header.
*
***************************************************************************/
int PASCAL NEAR SFHeaderSize(QSFH qsfh)
{
return 0; // based on the qsfh->bFlags value, determine header size, or
// if other header info exists before the file data
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func FILEOFFSET PASCAL NEAR | GetFreeBlock |
* Get the file offset of a free block of the given size
*
* @parm QFSHR | qfshr |
* Pointer to file system header
*
* @parm FILEOFFSET | foBlockSize |
* Size of block to retrieve offset to
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns valid file offset, or foNil if an error occurs
*
* @comm
* state IN:
* state OUT: Free list no longer contains the given block
* Notes:
*
***************************************************************************/
FILEOFFSET PASCAL NEAR GetFreeBlock(QFSHR qfshr,FILEOFFSET foBlock,PHRESULT phr)
{
HRESULT errb;
FILEOFFSET foStart;
assert(qfshr->hfl);
foStart = FreeListGetBestFit(qfshr->hfl,foBlock,&errb);
if (errb!=S_OK)
{
FILEOFFSET foLastBlock;
FILEOFFSET foLastBlockSize;
// If last free list block goes to end of file, start there
// Last block is automatically removed in the GetLastBlock call
if (FreeListGetLastBlock(qfshr->hfl,&foLastBlock,&foLastBlockSize,qfshr->fsh.foEof)==S_OK)
{
foStart=foLastBlock;
qfshr->fsh.foEof=FoAddFo(foLastBlock,foBlock);
}
else
{
// otherwise allocate it from end
foStart=qfshr->fsh.foEof;
qfshr->fsh.foEof=FoAddFo(qfshr->fsh.foEof,foBlock);
}
}
return foStart;
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func short PASCAL NEAR | GetNewHf |
* Get a new handle to a subfile. These handles are actually
* indices into the mv_gsfa global array.
*
* @parm LPCSTR | szFilename |
* Filename to use for hashing an Hf index
*
* @rdesc Returns valid index of a subfile, or hfNil for error
*
* @comm
* Array is not modified, so calling twice will give same index
*
***************************************************************************/
static int giMaxSubFiles=MAXSUBFILES;
short PASCAL NEAR GetNewHf(LPCSTR szFilename)
{
int iLen;
unsigned short i,uiStart=0;
int iMaxFilled;
iMaxFilled=giMaxSubFiles-1;
_ENTERCRITICALSECTION(&mv_gsfa_cs);
assert(mv_gsfa);
assert(mv_gsfa_count);
iLen=(szFilename)?min(8,lstrlen(szFilename)):0;
while (iLen--)
{
uiStart=(uiStart<<1)^(*(szFilename++));
}
i=uiStart%=giMaxSubFiles;
while (mv_gsfa[i].hsfb)
{
i=(i+1)%giMaxSubFiles;
if (!(--iMaxFilled))
{
// Increase our space!
HANDLE hNew;
giMaxSubFiles+=64;
_GLOBALUNLOCK(GlobalHandle(mv_gsfa));
if ((hNew=_GLOBALREALLOC(GlobalHandle(mv_gsfa),sizeof(SF)*giMaxSubFiles,GMEM_MOVEABLE|GMEM_ZEROINIT))==NULL)
{
giMaxSubFiles-=64;
i=0;
break;
}
mv_gsfa=(SF FAR *)_GLOBALLOCK(hNew);
i=uiStart%giMaxSubFiles;
iMaxFilled=giMaxSubFiles-1;
}
}
_LEAVECRITICALSECTION(&mv_gsfa_cs);
#ifdef _DEBUGMVFS
DPF2("GetNewHf: subfile '%s' is hf %d\n", (szFilename) ? szFilename : "NULL", i);
DumpMV_GSFA();
#endif
return i;
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func KEY PASCAL FAR | NewKeyFromSz |
* Convert a filename to a Key type for the file system btree. Currently
* a key type is a variable byte length size followed by the chars.
*
* @parm LPCSTR | szName |
* Name to convert
*
* @rdesc Returns valid KEY.
*
* @comm
* state IN:
* state OUT: String is allocated in the global string memory area
* Notes: Must call DisposeMemory((LPSTR)key) when finished using.
*
***************************************************************************/
KEY PASCAL FAR EXPORT_API NewKeyFromSz(LPCSTR sz)
{
int iLLen;
LPSTR szKey;
FILEOFFSET foLen;
foLen.dwHigh=0;
foLen.dwOffset=lstrlen(sz);
szKey=NewMemory((WORD)(foLen.dwOffset+5));
iLLen=FoToSz(foLen, szKey);
lstrcpy(szKey+iLLen,sz);
return (KEY)szKey;
}
// Returns number of bytes used in the fr struct
// Sets fr.szData to the File Offset, Length, and status byte
/***************************************************************************
*
* @doc PRIVATE
*
* @func WORD PASCAL NEAR | SetFrData |
* Set the szData field of pfr to the start, size, and byte values.
*
* @parm FILE_REC FAR * | pfr |
* Pointer to FILE_REC structure
*
* @parm FILEOFFSET | foStart |
* Starting address of a file
*
* @parm FILEOFFSET | foSize |
* Size of a file
*
* @parm BYTE | bFlags |
* The file flags (not used at the moment)
*
* @rdesc Returns the number of bytes used in the fr struct after FILEOFFSETs
* are converted to their equivalent variable-length byte values. This is
* the amount of space the filerec takes in the system btree.
*
* @comm
* <p pfr> is set to the foStart, foSize, and bFlags values. The
* <e szData> field of <p pfr> is set to the compressed version of the
* data (3 to 19 bytes in size for the three fields).
*
***************************************************************************/
WORD PASCAL NEAR SetFrData(FILE_REC FAR * pfr, FILEOFFSET foStart, FILEOFFSET foSize, BYTE bFlags)
{
WORD wTotalLen;
assert(pfr);
wTotalLen=FoToSz(foStart, pfr->szData);
wTotalLen+=FoToSz(foSize, pfr->szData+wTotalLen);
pfr->szData[wTotalLen++]=(char)bFlags;
pfr->foStart=foStart;
pfr->foSize=foSize;
pfr->bFlags=bFlags;
return wTotalLen;
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func WORD PASCAL NEAR | GetFrData |
* Get the size, start, and flags fields from the szData field of the
* FILE_REC.
*
* @parm FILE_REC FAR * | pfr |
* Pointer to FILE_REC structure
*
* @rdesc nothing.
*
* @comm
* All the fields are set based on the szData compressed
* version of the offset, size, and flags from the <p pfr> <e szData> field.
*
***************************************************************************/
void PASCAL FAR EXPORT_API GetFrData(FILE_REC FAR *pfr)
{
LPSTR szCursor;
assert(pfr);
szCursor=pfr->szData;
pfr->foStart=FoFromSz(szCursor);
ADVANCE_FO(szCursor);
pfr->foSize=FoFromSz(szCursor);
ADVANCE_FO(szCursor);
pfr->bFlags=*szCursor;
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func HF PASCAL NEAR | HfOpenFileHfsInternal |
* Open or Create a non-system subfile. Use the public HfOpenHfs,
* HfCreateFileHfs, and HfOpenHfsReserve functions, not this one.
*
* @parm HFS | hfs|
* File system for this sub file.
*
* @parm LPCSTR | szFilename |
* Name of file
*
* @parm BYTE | bFlags |
* Any of the following HFOPEN_ flags (except for HFOPEN_SYSTEM)
* @flag HFOPEN_READWRITE | (0x01) Open a file for reading and writing.
* @flag HFOPEN_READ | (0x02) Open a file for reading only.
* @flag HFOPEN_CREATE | (0x04) Create a new file, or truncate an
* existing file.
* @flag HFOPEN_NOTEMP | (0x10) If file is opened for read/write, do
* not make a temporary file if possible,
* so edits to the file are made directly
* in the file system.
* @flag HFOPEN_FORCETEMP | (0x20) A temp file is always created (space
* permitting) in read-only mode.
* @flag HFOPEN_ENABLELOGGING| (0x40) Logging usage of this file will be
* enabled.
*
* @parm FILEOFFSET | foReserve |
* When creating or opening a r/w file, reserve this amount of space
* for the file in the file system.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns a valid HF, or hfNil if an error.
*
* @comm
* state IN:
* state OUT: Valid HF
*
***************************************************************************/
#ifndef ITWRAP
HF PASCAL NEAR HfOpenFileHfsInternal(HFS hfs, LPCSTR sz,
BYTE bFlags, FILEOFFSET foReserve, PHRESULT phr)
{
#ifdef MOSMAP // {
// Disable function
if (bFlags & (HFOPEN_CREATE|HFOPEN_READWRITE))
{
SetErrCode (phr, ERR_NOTSUPPORTED);
return hfNil ;
}
#else // } {
DWORD hf;
QSFB qsfb;
QFSHR qfshr;
HRESULT rc;
FILE_REC fr;
BOOL bShouldInsert=FALSE;
HSFB hsfbFound=NULL;
KEY key;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return hfNil;
}
_ENTERCRITICALSECTION(&qfshr->cs);
key = NewKeyFromSz(sz);
if (bFlags&HFOPEN_CREATE)
{ bFlags|=HFOPEN_READWRITE;
if (bFlags&HFOPEN_READ)
{
SetErrCode(phr, E_INVALIDARG);
goto error_return;
}
}
// Reserving space implies no temporary file if possible
if (!FoIsNil(foReserve))
{
bFlags|=HFOPEN_NOTEMP;
}
else // Conversly, reserving NO space implies a temporary file
{
bFlags&=~HFOPEN_NOTEMP;
}
assert(!(bFlags&HFOPEN_SYSTEM));
// make sure file system is writable
if ((bFlags&(HFOPEN_CREATE|HFOPEN_READWRITE)) && (qfshr->fsh.bFlags&FSH_READONLY))
{
SetErrCode (phr, E_NOPERMISSION);
goto error_return;
}
rc = RcLookupByKey(qfshr->hbt, key, NULL, &fr);
// File already opened and being written to for first time
// or file locked, then we exit with E_NOPERMISSION
if (rc==S_OK)
{
GetFrData(&fr);
if ((fr.bFlags&SFH_INVALID) || ((fr.bFlags&SFH_LOCKED) && (bFlags&HFOPEN_READWRITE)))
{
SetErrCode(phr, E_NOPERMISSION);
goto error_return;
}
}
if (bFlags&HFOPEN_CREATE)
{
if (rc == S_OK)
{
// Already exists, let's truncate to zero!!!
assert(!FoIsNil(fr.foStart));
//SetErrCode(phr,ERR_EXIST);
//goto error_return;
}
else
{
// *** OK, Create node with '-1' for 'exclusive'
SetFrData(&fr,foNil,foNil,SFH_INVALID);
bShouldInsert = TRUE;
}
}
else
{
if (rc!=S_OK)
{
SetErrCode(phr, rc);
goto error_return;
}
}
if ((hf=(DWORD)GetNewHf(sz))== hfNil)
{
SetErrCode(phr, ERR_NOHANDLE); // Out of file handles
goto error_return;
}
mv_gsfa[hf].foCurrent=foNil;
// Look for file block in opened file list
if (!(fr.bFlags&SFH_INVALID))
{
HSFB hsfb = qfshr->hsfbFirst;
QSFB qsfb;
HSFB hsfbNext;
while ((hsfb) && (!hsfbFound))
{
qsfb=(QSFB)_GLOBALLOCK(hsfb);
if (FoEquals(qsfb->foHeader,fr.foStart))
{
hsfbFound=hsfb;
}
hsfbNext=qsfb->hsfbNext;
_GLOBALUNLOCK(hsfb);
hsfb=hsfbNext;
}
}
if (!hsfbFound)
{
// Create new subfile block
FILEOFFSET foExtraSize=foNil;
mv_gsfa[hf].hsfb = _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE| GMEM_MOVEABLE,
(ULONG)sizeof(SFB) + ( sz == NULL ? 0 : lstrlen(sz)));
qsfb = (QSFB)_GLOBALLOCK(mv_gsfa[hf].hsfb);
if (mv_gsfa[hf].hsfb == NULL)
{
SetErrCode (phr, E_OUTOFMEMORY);
goto error_return;
}
lstrcpy(qsfb->rgchKey, sz);
qsfb->bOpenFlags=bFlags;
qsfb->foHeader=fr.foStart;
if (bFlags&HFOPEN_ENABLELOGGING)
qsfb->sfh.bFlags|=SFH_LOGGING;
qsfb->hfs = hfs;
qsfb->wLockCount=1;
if (!FoIsNil(qsfb->foHeader))
{
qsfb->sfh.foBlockSize=fr.foSize;
qsfb->sfh.bFlags=fr.bFlags;
}
// If given foReserve, try to reserve space if file already lives in fs,
// or allocate space now for all of reserve, and give it to file
// if we can't reserve, then even though notemp is specified, a temp will
// be created at write time, so force it to temp here in that case.
if (!FoIsNil(foReserve))
{
assert(qfshr->hfl);
if (FoCompare(foReserve,qsfb->sfh.foBlockSize)>0)
{
if (!(FoIsNil(qsfb->foHeader)))
{
// If file exists already, try to extend file
foExtraSize = FreeListGetBlockAt(qfshr->hfl,FoAddFo(qsfb->foHeader,qsfb->sfh.foBlockSize),phr);
// VFileOpen will handle things properly if file fits or not
if (FoCompare(FoAddFo(qsfb->sfh.foBlockSize,foExtraSize),foReserve)<0)
{
bFlags&=(~HFOPEN_NOTEMP);
}
}
else
{
// New file, try to get free space for file
// We reserve amount we want plus subfile file header size
qsfb->foHeader=GetFreeBlock(qfshr,FoAddDw(foReserve,SFHeaderSize(&qsfb->sfh)),NULL);
foExtraSize=foReserve;
}
}
}
if ((bFlags&HFOPEN_READ) || (bFlags&HFOPEN_NOTEMP))
{
if (bFlags&HFOPEN_CREATE)
{
qsfb->hvf=VFileOpen(qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),
foNil,FoAddFo(qsfb->sfh.foBlockSize,foExtraSize),
((bFlags&HFOPEN_READWRITE)?VFOPEN_READWRITE:VFOPEN_READ)|VFOPEN_ASFID,&qfshr->sb,phr);
}
else
{
qsfb->hvf=VFileOpen(qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),
qsfb->sfh.foBlockSize,foExtraSize,
((bFlags&HFOPEN_READWRITE)?VFOPEN_READWRITE:VFOPEN_READ)|VFOPEN_ASFID,&qfshr->sb,phr);
}
}
else
{
if (bFlags&HFOPEN_CREATE)
{
qsfb->hvf=VFileOpen(qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),
foNil,FoAddFo(qsfb->sfh.foBlockSize,foExtraSize),VFOPEN_ASTEMP|VFOPEN_READWRITE,&qfshr->sb,phr);
}
else
{
qsfb->hvf=VFileOpen(qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),
qsfb->sfh.foBlockSize,foExtraSize,VFOPEN_ASTEMP|VFOPEN_READWRITE,&qfshr->sb,phr);
}
}
if (!qsfb->hvf)
{
SetErrCode(phr, ERR_NOHANDLE);
exit1:
_GLOBALUNLOCK(mv_gsfa[hf].hsfb);
_GLOBALFREE(mv_gsfa[hf].hsfb);
mv_gsfa[hf].hsfb=NULL;
goto error_return;
}
// Extend block size to account for any extra allocation we did
qsfb->sfh.foBlockSize=FoAddFo(qsfb->sfh.foBlockSize,foExtraSize);
qsfb->hsfbNext = qfshr->hsfbFirst;
qfshr->hsfbFirst = mv_gsfa[hf].hsfb;
if (bShouldInsert)
{
SetFrData(&fr,foNil,foNil,SFH_INVALID);
//fr.lifBase=lifNil;
if ((rc = RcInsertHbt(qfshr->hbt, key, &fr))!=S_OK)
{ // some other error!
VFileAbandon(qsfb->hvf);
VFileClose(qsfb->hvf);
goto exit1;
}
}
}
else
{
// Ignoring reserve if found, we could call VFileSetEOF though ?!?
if (bFlags & HFOPEN_READWRITE)
{
// Trying to write to a file that's already opened for read
SetErrCode(phr, E_NOPERMISSION);
goto error_return;
}
mv_gsfa[hf].hsfb=hsfbFound;
qsfb = (QSFB)_GLOBALLOCK(mv_gsfa[hf].hsfb);
qsfb->wLockCount++;
}
_GLOBALUNLOCK(mv_gsfa[hf].hsfb);
// File header always in RAM, so we never place it into the temp file
DisposeMemory((LPSTR)key);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
#ifdef _DEBUGMVFS
DPF2("HfOpenInternal: hf=%d, sz='%s'\n", hf, sz);
DumpMV_GSFA();
#endif
return (HF)hf;
error_return:
DisposeMemory((LPSTR)key);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
return hfNil;
#endif //}
}
#endif
/***************************************************************************
*
* @doc PRIVATE
*
* @func HF PASCAL NEAR | HfOpenSystemFileHfsInternal |
* Open or Create a system subfile. Not for civilians.
*
* @parm HFS | hfs|
* File system for this sub file.
*
* @parm BYTE | bFlags |
* Any of the following HFOPEN_ flags (HFOPEN_SYSTEM required)
* @flag HFOPEN_READWRITE | (0x01) Open a file for reading and writing.
* @flag HFOPEN_READ | (0x02) Open a file for reading only.
* @flag HFOPEN_CREATE | (0x04) Create a new file, or truncate an
* existing file.
* @flag HFOPEN_SYSTEM | (0x08) Open or create a system file. Only one
* system file is permitted per file system,
* and it is the system directory btree.
* @flag HFOPEN_NOTEMP | (0x10) If file is opened for read/write, do
* not make a temporary file if possible,
* so edits to the file are made directly
* in the file system.
* @flag HFOPEN_FORCETEMP | (0x20) A temp file is always created (space
* permitting) in read-only mode.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns a valid HF, or hfNil if an error.
*
***************************************************************************/
#ifndef ITWRAP
HF PASCAL NEAR HfOpenSystemFileHfsInternal(HFS hfs, BYTE bFlags, PHRESULT phr)
{
#ifdef MOSMAP // {
// Disable function
if (bFlags & (HFOPEN_CREATE|HFOPEN_READWRITE))
{
SetErrCode (phr, ERR_NOTSUPPORTED);
return hfNil ;
}
#else // } {
DWORD hf;
QSFB qsfb;
QFSHR qfshr;
//BOOL bShouldInsert=FALSE;
HSFB hsfbFound=NULL; // when valid, use this instead of new one
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return hfNil;
}
_ENTERCRITICALSECTION(&qfshr->cs);
if (bFlags&HFOPEN_CREATE)
{ bFlags|=HFOPEN_READWRITE;
if (bFlags&HFOPEN_READ)
{
SetErrCode(phr, E_INVALIDARG);
goto error_return;
}
}
assert(bFlags&HFOPEN_SYSTEM);
if ((bFlags&(HFOPEN_CREATE|HFOPEN_READWRITE)) && (qfshr->fsh.bFlags & FSH_READONLY))
{
SetErrCode (phr, E_NOPERMISSION);
goto error_return;
}
if ((hf=GetNewHf(NULL))== hfNil)
{
SetErrCode(phr, ERR_NOHANDLE); // Out of file handles
goto error_return;
}
// qfshr->fsh.foDirectory is used for system file offset
// Only one system file allowed
if (bFlags&HFOPEN_CREATE)
{ if (!FoIsNil(qfshr->fsh.foDirectory))
{
SetErrCode(phr, ERR_EXIST);
goto error_return;
}
}
else if (FoIsNil(qfshr->fsh.foDirectory))
{
SetErrCode(phr, E_NOTEXIST);
goto error_return;
}
if (qfshr->hsfbSystem)
{
if (bFlags&HFOPEN_READWRITE)
{
SetErrCode (phr, E_NOPERMISSION);
goto error_return;
}
qsfb = (QSFB)_GLOBALLOCK(qfshr->hsfbSystem);
qsfb->wLockCount++;
}
else
{
qfshr->hsfbSystem = _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE| GMEM_MOVEABLE,
(ULONG)sizeof(SFB) );
if (qfshr->hsfbSystem == NULL)
{
SetErrCode (phr, E_OUTOFMEMORY);
goto error_return;
}
qsfb = (QSFB)_GLOBALLOCK(qfshr->hsfbSystem);
qsfb->bOpenFlags=bFlags;
qsfb->foHeader = qfshr->fsh.foDirectory;
qsfb->hfs = hfs;
qsfb->wLockCount=1;
qsfb->hsfbNext=NULL; // always since only one sys file
if (!FoIsNil(qsfb->foHeader))
{
qsfb->sfh=qfshr->fsh.sfhSystem;
}
// System file, only time we open it to Tempfile is if READWRITE & !NOTEMP
if ((bFlags&HFOPEN_READWRITE) && (!(bFlags&HFOPEN_NOTEMP)))
qsfb->hvf=VFileOpen(qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),qsfb->sfh.foBlockSize,foNil,
VFOPEN_ASTEMP|VFOPEN_READWRITE,&qfshr->sb,phr);
else
qsfb->hvf=VFileOpen(qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),qsfb->sfh.foBlockSize,foNil,
((bFlags&HFOPEN_READWRITE)?VFOPEN_READWRITE:VFOPEN_READ)|((bFlags&HFOPEN_FORCETEMP)?VFOPEN_ASTEMP:VFOPEN_ASFID),
&qfshr->sb,phr);
}
_GLOBALUNLOCK(qfshr->hsfbSystem);
mv_gsfa[hf].foCurrent=foNil;
mv_gsfa[(DWORD)hf].hsfb = qfshr->hsfbSystem;
// File header always in RAM, so we never place it into the temp file
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
#ifdef _DEBUGMVFS
DPF2("HfOpenSystemInternal: hf=%d, bflags=%d\n", hf, bFlags);
DumpMV_GSFA();
#endif
return (HF)hf;
error_return:
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
return hfNil;
#endif //}
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HF PASCAL FAR | HfOpenHfs |
* Open (or create) a subfile that lives in the file system.
*
* @parm HFS | hfs|
* File system.
*
* @parm LPCSTR | szFilename |
* Name of file (length limited to 1/2 the block size given when creating
* the file system)
*
* @parm BYTE | bFlags |
* Any of the following HFOPEN_ flags
* @flag HFOPEN_READWRITE | (0x01) Open a file for reading and writing.
* @flag HFOPEN_READ | (0x02) Open a file for reading only.
* @flag HFOPEN_CREATE | (0x04) Create a new file, or truncate an
* existing file.
* @flag HFOPEN_NOTEMP | (0x10) If file is opened for read/write, do
* not make a temporary file if possible,
* so edits to the file are made directly
* in the file system.
* @flag HFOPEN_FORCETEMP | (0x20) A temp file is always created (space
* permitting) in read-only mode.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns a valid HF, or hfNil if an error.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC HF PASCAL FAR EXPORT_API HfOpenHfs(HFS hfs, LPCSTR sz,
BYTE bFlags, PHRESULT phr)
{
if (!(bFlags&HFOPEN_SYSTEM))
{
return HfOpenFileHfsInternal(hfs,sz,bFlags,foNil,phr);
}
else
{
return HfOpenSystemFileHfsInternal(hfs,bFlags,phr);
}
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HF PASCAL FAR | HfCreateFileHfs |
* Create a subfile that lives in the file system. If file already
* exists, it will be truncated to zero first. This API is left in
* for compatibility - HfOpenHfs may be called with the HFOPEN_CREATE
* flag instead.
*
* @parm HFS | hfs |
* File system.
*
* @parm LPCSTR | szFilename |
* Name of file (length limited to 1/2 the block size given when creating
* the file system)
*
* @parm BYTE | bFlags |
* Any of the following HFOPEN_ flags (HFOPEN_CREATE implied)
* @flag HFOPEN_READWRITE | (0x01) Open a file for reading and writing.
* @flag HFOPEN_READ | (0x02) Open a file for reading only.
* @flag HFOPEN_CREATE | (0x04) Create file (implied)
* @flag HFOPEN_NOTEMP | (0x10) If file is opened for read/write, do
* not make a temporary file if possible,
* so edits to the file are made directly
* in the file system.
* @flag HFOPEN_FORCETEMP | (0x20) A temp file is always created (space
* permitting) in read-only mode.
* @flag HFOPEN_ENABLELOGGING| (0x40) Logging usage of this file will be
* enabled.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns a valid HF, or hfNil if an error.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC HF PASCAL FAR EXPORT_API HfCreateFileHfs(HFS hfs, LPCSTR sz,
BYTE bFlags, PHRESULT phr)
{
bFlags|=HFOPEN_CREATE;
if (!(bFlags&HFOPEN_SYSTEM))
{
return HfOpenFileHfsInternal(hfs,sz,bFlags,foNil,phr);
}
else
{
return HfOpenSystemFileHfsInternal(hfs,bFlags,phr);
}
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HF PASCAL FAR | HfOpenHfsReserve |
* Create or open a subfile that lives in the file system, and reserve
* some space for the file. If creating a file, the space is reserved
* directly in the file system, and all data is written directly to the
* file system.
*
* @parm HFS | hfs |
* File system.
*
* @parm LPCSTR | szFilename |
* Name of file (length limited to 1/2 the block size given when creating
* the file system)
*
* @parm BYTE | bFlags |
* Any of the following HFOPEN_ flags:
* @flag HFOPEN_READWRITE | (0x01) Open a file for reading and writing.
* @flag HFOPEN_READ | (0x02) Open a file for reading only.
* @flag HFOPEN_CREATE | (0x04) Create a new file, or truncate an
* existing file.
* @flag HFOPEN_NOTEMP | (0x10) If file is opened for read/write, do
* not make a temporary file if possible,
* so edits to the file are made directly
* in the file system.
* @flag HFOPEN_FORCETEMP | (0x20) A temp file is always created (space
* permitting) in read-only mode.
*
* @parm FILEOFFSET | foReserve |
* Number of bytes to reserve in the file system. If opening a file
* in r/w mode, and the requested space cannot be reserved, the file
* will be copied to a temp file transparently.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns a valid HF, or hfNil if an error.
*
* @comm
* Just leave the HFOPEN_NOTEMP and HFOPEN_FORCETEMP flags alone for
* the default behavior.
*
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC HF PASCAL FAR EXPORT_API HfOpenHfsReserve(HFS hfs, LPCSTR sz,
BYTE bFlags, FILEOFFSET foReserve, PHRESULT phr)
{
if (!(bFlags&HFOPEN_SYSTEM))
{
return HfOpenFileHfsInternal(hfs,sz,bFlags,foReserve,phr);
}
else
{
SetErrCode(phr, E_INVALIDARG);
return hfNil;
}
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcCopyDosFileHfs |
* Create or open a subfile that lives in the file system, and reserve
* some space for the file. If creating a file, the space is reserved
* directly in the file system, and all data is written directly to the
* file system.
*
* @parm HFS | hfs |
* File system to receive file.
*
* @parm LPCSTR | szFsFilename |
* Name of file used in the file system (any length is OK -- name does not
* have to match the DOS filename). Max length of filename is 64K.
*
* @parm LPCSTR | szDosFilename |
* Pathname of file to copy.
*
* @parm BYTE | bExtraFlags |
* Set to zero for normal files. Use HFOPEN_ENABLELOGGING to enable file
* for logging.
*
* @parm PROGFUNC | lpfnProg |
* Progress callback function (parameter passed to function is always zero).
* Function returns non-zero to interrupt and cancel copy operation.
*
* @rdesc Returns S_OK if all is OK, else the error code.
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API RcCopyDosFileHfs(HFS hfs, LPCSTR szFsFilename, LPCSTR szDosFilename, BYTE bExtraFlags, PROGFUNC lpfnProg)
{
#ifdef MOSMAP // {
// Disable function
return ERR_NOTSUPPORTED;
#else // } {
FID fidSource;
HRESULT errb;
FILEOFFSET foSize;
FILEOFFSET foTemp;
DWORD dwT;
HF hfDest;
QFSHR qfshr;
HRESULT rc=S_OK;
if (szDosFilename==NULL || hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
return E_INVALIDARG;
}
if ((fidSource=FidOpenFm((FM)szDosFilename,wReadOnly,&errb))==fidNil)
{
rc=errb;
exit0:
_GLOBALUNLOCK(hfs);
return rc;
}
foSize=FoSeekFid(fidSource,foNil,wSeekEnd,&errb);
FoSeekFid(fidSource,foNil,wSeekSet,&errb);
// insert only the extra flags that we allow here...
bExtraFlags&=HFOPEN_ENABLELOGGING;
#ifndef ITWRAP
if ((hfDest=HfOpenFileHfsInternal(hfs,(szFsFilename)?szFsFilename:szDosFilename,
(BYTE)(HFOPEN_READWRITE|HFOPEN_CREATE|bExtraFlags),
foSize,&errb))==(HF)hfNil)
#else
// erinfox: I'm not sure if this is going to work!
if ((hfDest=HfOpenHfsReserve(hfs,(szFsFilename)?szFsFilename:szDosFilename,
(BYTE)(HFOPEN_READWRITE|HFOPEN_CREATE|bExtraFlags),
foSize,&errb))==(HF)hfNil)
#endif
{
rc=errb;
exit1:
RcCloseFid(fidSource);
goto exit0;
}
_ENTERCRITICALSECTION(&qfshr->sb.cs);
foTemp.dwHigh=0;
do
{
// perform a progress callback
if (lpfnProg)
{
if ((*lpfnProg)(0)!=0)
{
rc=E_INTERRUPT;
exit2:
_LEAVECRITICALSECTION(&qfshr->sb.cs);
RcCloseHf(hfDest);
goto exit1;
}
}
if (!foSize.dwHigh)
dwT = min((DWORD)qfshr->sb.lcbBuffer, foSize.dwOffset);
else
dwT=qfshr->sb.lcbBuffer;
if (LcbReadFid( fidSource, qfshr->sb.lpvBuffer, (LONG)dwT, &errb) != (LONG)dwT )
{
rc=errb;
break;
}
if (LcbWriteHf( hfDest, qfshr->sb.lpvBuffer, (LONG)dwT, &errb) != (LONG)dwT )
{
rc=errb;
break;
}
foTemp.dwOffset=dwT;
foSize=FoSubFo(foSize,foTemp);
}
while (!FoIsNil(foSize));
goto exit2;
#endif // } MOSMAP
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func LONG PASCAL FAR | LcbReadHf |
* Read data from a subfile.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm LPVOID | lpvBuffer |
* Buffer to receive data (>64K OK)
*
* @parm LONG | lcb |
* Number of bytes to read
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the number of bytes read. If not lcb, an error occurred.
*
* @comm
* The file pointer is incremented by the number of bytes read. Files
* may be larger than 4 gigs, but only 2 gigs at a time maximum can be read.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC LONG PASCAL FAR EXPORT_API LcbReadHf(HF hf, LPVOID lpvBuffer,
LONG lcb, PHRESULT phr)
{
QSFB qsfb;
LONG lRead=0L;
QFSHR qfshr;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (!FHfValid(hf))
{
SetErrCode(phr, E_INVALIDARG);
return 0L;
}
if (mv_gsfa[(DWORD)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD)hf].hsfb)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return 0L;
}
assert(qsfb->hvf);
if (qsfb->hfs == NULL || (qfshr = _GLOBALLOCK(qsfb->hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
goto exit1;
}
_ENTERCRITICALSECTION(&qfshr->cs);
lRead = VFileSeekRead(qsfb->hvf, mv_gsfa[(DWORD)hf].foCurrent, lpvBuffer, lcb, phr);
mv_gsfa[(DWORD)hf].foCurrent=FoAddDw(mv_gsfa[(DWORD)hf].foCurrent,lRead);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(qsfb->hfs);
exit1:
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
return lRead;
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func LONG PASCAL FAR | LcbWriteHf |
* Write data to a subfile
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm LPVOID | lpvBuffer |
* Buffer to receive data (>64K OK)
*
* @parm LONG | lcb |
* Number of bytes to write
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the number of bytes written. If not lcb, an error occurred.
*
* @comm
* File pointer is incremented by the number of bytes written. Only 2 gigs
* maximum at one time can be read, but files can be larger than 4 gigs.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC LONG PASCAL FAR EXPORT_API LcbWriteHf(HF hf, LPVOID lpvBuffer,
LONG lcb, PHRESULT phr)
{
QSFB qsfb;
LONG lWrote=0L;
QFSHR qfshr;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (!FHfValid(hf))
{
SetErrCode(phr, E_INVALIDARG);
return 0L;
}
if (mv_gsfa[(DWORD)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD)hf].hsfb)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return 0L;
}
if (qsfb->bOpenFlags&HFOPEN_READ)
{
SetErrCode(phr, E_NOPERMISSION);
goto exit1;
}
if (qsfb->hfs == NULL || (qfshr = _GLOBALLOCK(qsfb->hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
goto exit1;
}
assert(qsfb->hvf);
_ENTERCRITICALSECTION(&qfshr->cs);
lWrote = VFileSeekWrite(qsfb->hvf, mv_gsfa[(DWORD)hf].foCurrent, lpvBuffer, lcb, phr);
mv_gsfa[(DWORD)hf].foCurrent=FoAddDw(mv_gsfa[(DWORD)hf].foCurrent,lWrote);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(qsfb->hfs);
exit1:
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
return lWrote;
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func FILEOFFSET PASCAL FAR | FoSeekHf |
* Seeks to a location in the subfile (replaces LSeekHf). To seek
* beyond 4 gigs, use pdwHigh.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm FILEOFFSET | foOffset |
* File offset to seek to. For standard 4-byte offsets, the .dwHigh
* member of foOffset should be zero. When seeking from current
* position, and the seek is negative, remember that dwHigh should
* also be 0xffff. Just treat the dwHigh and dwOffset members of
* foOffset as the High and Low DWORDS of a quad word.
*
* @parm WORD | wOrigin |
* wFSSeekSet (0), wFSSeekCur (1), or wFSSeekEnd (2). When Cur or End,
* dwOffset is treated as signed.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the offset actually pointed to in the file after seeking.
*
* @comm
* The file pointer moved to the given offset, and pdwHigh is
* set to the high dword if not NULL.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC FILEOFFSET PASCAL FAR EXPORT_API FoSeekHf(HF hf, FILEOFFSET foOffset,
WORD wOrigin, PHRESULT phr)
{
assert(mv_gsfa);
assert(mv_gsfa_count);
if (phr)
phr->err=S_OK;
switch (wOrigin)
{
case wFSSeekSet:
mv_gsfa[(DWORD)hf].foCurrent=foOffset;
break;
case wFSSeekCur:
mv_gsfa[(DWORD)hf].foCurrent=FoAddFo(mv_gsfa[(DWORD)hf].foCurrent,foOffset);
break;
case wFSSeekEnd:
{
QSFB qsfb;
if (mv_gsfa[(DWORD)hf].hsfb == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return mv_gsfa[(DWORD)hf].foCurrent;
}
if ((qsfb = _GLOBALLOCK(mv_gsfa[(DWORD)hf].hsfb)) == NULL)
{ SetErrCode (phr, E_OUTOFMEMORY);
return mv_gsfa[(DWORD)hf].foCurrent;
}
assert(qsfb->hvf);
mv_gsfa[(DWORD)hf].foCurrent=FoAddFo(VFileGetSize(qsfb->hvf,phr),foOffset);
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
break;
}
default:
SetErrCode(phr,E_INVALIDARG);
}
return mv_gsfa[(DWORD)hf].foCurrent;
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func FILEOFFSET PASCAL FAR | FoTellHf |
* Returns position of file pointer. Replaces LTellHf. This function
* just looks up the file pointer value, so it is fast and can be
* called any time.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the file pointer for the given subfile.
*
***************************************************************************/
PUBLIC FILEOFFSET PASCAL FAR EXPORT_API FoTellHf(HF hf, PHRESULT phr)
{
assert(mv_gsfa);
assert(mv_gsfa_count);
return mv_gsfa[(DWORD_PTR)hf].foCurrent;
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func FILEOFFSET PASCAL FAR | FoSizeHf |
* Returns size of the subfile. Replaces LSizeHf.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the size of a subfile.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC FILEOFFSET PASCAL FAR EXPORT_API FoSizeHf(HF hf, PHRESULT phr)
{
QSFB qsfb;
FILEOFFSET foSize=foNil;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD)hf].hsfb)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return foNil;
}
assert(qsfb->hvf);
foSize = VFileGetSize(qsfb->hvf, phr);
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
return foSize;
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func FILEOFFSET PASCAL FAR | FoOffsetHf |
* Returns offset into the M20 of the subfile.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the size of a subfile.
*
***************************************************************************/
PUBLIC FILEOFFSET PASCAL FAR EXPORT_API FoOffsetHf(HF hf, PHRESULT phr)
{
QSFB qsfb;
FILEOFFSET foOffset=foNil;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD_PTR)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return foNil;
}
assert(qsfb->hvf);
foOffset = qsfb->foHeader;
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
return foOffset;
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func BOOL PASCAL FAR | FEofHf |
* Checks the file pointer for End Of File.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns TRUE if the file pointer is at or beyond the size of
* the file, else FALSE.
*
***************************************************************************/
PUBLIC BOOL PASCAL FAR EXPORT_API FEofHf(HF hf, PHRESULT phr)
{
QSFB qsfb;
FILEOFFSET foSize=foNil;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD_PTR)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return FALSE;
}
assert(qsfb->hvf);
foSize = VFileGetSize(qsfb->hvf, phr);
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
return (FoCompare(mv_gsfa[(DWORD_PTR)hf].foCurrent,foSize)>=0);
}
// Returns TRUE if size changed OK
/***************************************************************************
*
* @doc INTERNAL API
*
* @func BOOL PASCAL FAR | FChSizeHf |
* Change the size of the subfile
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @parm FILEOFFSET | foNewSize |
* New size of file
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns TRUE if file size changed OK, FALSE otherwise.
*
* @comm
* File pointer is not adjusted if size falls below current
* current file position.
*
***************************************************************************/
PUBLIC BOOL PASCAL FAR EXPORT_API FChSizeHf(HF hf, FILEOFFSET foSize, PHRESULT phr)
{
QSFB qsfb;
HRESULT rc;
QFSHR qfshr;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD_PTR)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return FALSE;
}
assert(qsfb->hvf);
if (qsfb->hfs == NULL || (qfshr = _GLOBALLOCK(qsfb->hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
goto exit1;
}
_ENTERCRITICALSECTION(&qfshr->cs);
rc=VFileSetEOF(qsfb->hvf, foSize);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(qsfb->hfs);
if (rc!=S_OK)
SetErrCode(phr, rc);
exit1:
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
return (rc==S_OK);
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func HRESULT PASCAL NEAR | HsfbRemove |
* Remove the subfile header block. Multiple HFs can reference a single
* subfile block, so the block should not be removed unless the lock
* count has been decremented to zero.
*
* @parm HSFB | hsfb |
* Handle to a valid subfile block
*
* @rdesc Returns S_OK if block removed OK.
*
* @comm
* hsfb is removed from linked list
*
***************************************************************************/
HRESULT PASCAL NEAR EXPORT_API HsfbRemove(HSFB hsfb)
{
QSFB qsfb;
QFSHR qfshr;
HRESULT rc=S_OK;
if (hsfb == NULL || (qsfb = _GLOBALLOCK(hsfb)) == NULL)
{
return E_INVALIDARG;
}
if (qsfb->hvf)
{
return E_NOPERMISSION;
}
if (!(qsfb->bOpenFlags&HFOPEN_SYSTEM))
{
HFS hfs;
HSFB hsfbNext;
hfs=qsfb->hfs;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
_GLOBALUNLOCK(hsfb);
return E_INVALIDARG;
}
_ENTERCRITICALSECTION(&qfshr->cs);
hsfbNext=qsfb->hsfbNext;
if (qfshr->hsfbFirst==hsfb)
{
qfshr->hsfbFirst=hsfbNext;
}
else
{
HSFB hsfbCursor = qfshr->hsfbFirst;
QSFB qsfbCursor;
while (hsfbCursor)
{ HSFB hsfbTemp;
qsfbCursor=(QSFB)_GLOBALLOCK(hsfbCursor);
if (qsfbCursor->hsfbNext==hsfb)
{
qsfbCursor->hsfbNext=hsfbNext;
_GLOBALUNLOCK(hsfbCursor);
break;
}
hsfbTemp=qsfbCursor->hsfbNext;
_GLOBALUNLOCK(hsfbCursor);
hsfbCursor=hsfbTemp;
}
// The block should always be in the list!
assert(hsfbCursor);
}
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
}
_GLOBALUNLOCK(hsfb);
_GLOBALFREE(hsfb);
return rc;
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func HSFB PASCAL NEAR | HsfbCloseHsfb |
* Close the actual file associated with this block. This should only
* be done once the reference count reaches zero.
*
* @parm HSFB hsfb
* Handle to a valid subfile.
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns the next subfile block in the linked list of subfile
* blocks.
*
***************************************************************************/
HSFB PASCAL NEAR EXPORT_API HsfbCloseHsfb(HSFB hsfbFirst, PHRESULT phr)
{
QSFB qsfb;
HRESULT errb;
HRESULT rc=S_OK;
HSFB hsfbNext=NULL;
if (hsfbFirst == NULL || (qsfb = _GLOBALLOCK(hsfbFirst)) == NULL)
{
SetErrCode(phr,E_OUTOFMEMORY);
return NULL;
}
if (qsfb->hvf==NULL)
{
SetErrCode(phr,E_INVALIDARG);
exit1:
_GLOBALUNLOCK(hsfbFirst);
return NULL;
}
hsfbNext=qsfb->hsfbNext;
// If Read Only, the close is easy
if (qsfb->bOpenFlags&HFOPEN_READ)
{
if ((rc=VFileClose(qsfb->hvf))!=S_OK)
{
VFileAbandon(qsfb->hvf);
if ((rc=VFileClose(qsfb->hvf))!=S_OK)
{
SetErrCode(phr,rc);
goto exit1;
}
}
qsfb->hvf=NULL;
}
else
{
FILEOFFSET foSize=VFileGetSize(qsfb->hvf,&errb);
HFS hfs;
QFSHR qfshr;
hfs=qsfb->hfs;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
SetErrCode(phr,E_OUTOFMEMORY);
goto exit1;
}
_ENTERCRITICALSECTION(&qfshr->cs);
// Closing a read/write file...
if (VFileGetFlags(qsfb->hvf,&errb)&VFF_FID)
{
FILEOFFSET foLeftOver;
// If file lives in FS,
if ((rc=VFileClose(qsfb->hvf))!=S_OK)
{
SetErrCode(phr,rc);
_GLOBALUNLOCK(hfs);
_LEAVECRITICALSECTION(&qfshr->cs);
goto exit1;
}
qsfb->hvf=NULL;
foLeftOver=FoSubFo(qsfb->sfh.foBlockSize,foSize);
assert(qfshr->hfl);
// Send left over to free list
if (!FoEquals(foLeftOver,foNil))
{
FreeListAdd(qfshr->hfl,FoAddDw(FoAddFo(qsfb->foHeader,foSize),SFHeaderSize(&qsfb->sfh)),
foLeftOver);
}
}
else
{
// If file lives in temp file
// Find free spot for file
if (qsfb->bOpenFlags&HFOPEN_READWRITE)
{
assert(qfshr->hfl);
if (!FoIsNil(qsfb->foHeader)) // File already lives in FS
{
if (FoCompare(foSize,qsfb->sfh.foBlockSize)<=0)
{
// It fits back into old slot!!!
FILEOFFSET foLeftOver=FoSubFo(qsfb->sfh.foBlockSize,foSize);
if (!FoIsNil(foLeftOver))
{
FreeListAdd(qfshr->hfl,
FoAddDw(FoAddFo(qsfb->foHeader,foSize),SFHeaderSize(&qsfb->sfh)),
foLeftOver);
}
}
else
{
FreeListAdd(qfshr->hfl,qsfb->foHeader,
FoAddDw(qsfb->sfh.foBlockSize,SFHeaderSize(&qsfb->sfh)));
qsfb->foHeader=GetFreeBlock(qfshr,FoAddDw(foSize,SFHeaderSize(&qsfb->sfh)),NULL);
}
}
else
{
qsfb->foHeader=GetFreeBlock(qfshr,FoAddDw(foSize,SFHeaderSize(&qsfb->sfh)),NULL);
}
VFileSetBase(qsfb->hvf,qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),foSize);
}
else
VFileAbandon(qsfb->hvf);
VFileClose(qsfb->hvf);
qsfb->hvf=NULL;
}
// Update structs
qsfb->sfh.foBlockSize=foSize;
// Update btree if necessary (always for now)
if (!(qsfb->bOpenFlags&HFOPEN_SYSTEM))
{
FILE_REC fr;
KEY key = NewKeyFromSz(qsfb->rgchKey);
qsfb->sfh.bFlags&=(~SFH_INVALID);
SetFrData(&fr,qsfb->foHeader,qsfb->sfh.foBlockSize,qsfb->sfh.bFlags);
//fr.lifBase=qsfb->foHeader.lOffset;
if ((rc = RcUpdateHbt(qfshr->hbt, key, &fr))!=S_OK)
{
// Can't update btree??? What now???
if ((rc = RcUpdateHbt(qfshr->hbt, key, &fr))!=S_OK)
{
// Place breakpoint above... this should never happen.
}
}
DisposeMemory((LPSTR)key);
}
else
{
qfshr->fsh.foDirectory=qsfb->foHeader;
qfshr->fsh.sfhSystem=qsfb->sfh;
}
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
}
_GLOBALUNLOCK(hsfbFirst);
return hsfbNext;
}
/***************************************************************************
*
* @doc PRIVATE
*
* @func HRESULT PASCAL NEAR | RcFlushHsfb |
* Flush the subfile block by ensuring the file it refers to is copied
* into the file system, and the btree entry is up to date with the
* current file size.
*
* @parm HF | hsfb |
* Handle to a valid subfile block.
*
* @rdesc Returns S_OK if subfile flushed OK.
*
***************************************************************************/
HRESULT PASCAL NEAR EXPORT_API RcFlushHsfb(HSFB hsfbFirst)
{
QSFB qsfb;
HRESULT errb;
HRESULT rc=S_OK;
if (hsfbFirst == NULL || (qsfb = _GLOBALLOCK(hsfbFirst)) == NULL)
{
rc=E_OUTOFMEMORY;
return rc;
}
if (qsfb->hvf==NULL)
{
rc=E_INVALIDARG;
exit1:
_GLOBALUNLOCK(hsfbFirst);
return rc;
}
// If Read Only, the flush is easy
if (qsfb->bOpenFlags&HFOPEN_READ)
{
goto exit1;
}
else
{
FILEOFFSET foSize=VFileGetSize(qsfb->hvf,&errb);
HFS hfs;
QFSHR qfshr;
hfs=qsfb->hfs;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
rc=E_OUTOFMEMORY;
goto exit1;
}
_ENTERCRITICALSECTION(&qfshr->cs);
// Copy r/w temp file to file system if needed
// Closing a read/write file...
if (VFileGetFlags(qsfb->hvf,&errb)&VFF_FID)
{
// Leave extra large block allocated to this file if there already
}
else
{
// If file lives in temp file
// Find free spot for file
assert(qfshr->hfl);
if (!FoIsNil(qsfb->foHeader)) // File already lives in FS
{
if (FoCompare(foSize,qsfb->sfh.foBlockSize)<=0)
{
// It fits back into old slot!!!
}
else
{
FILEOFFSET foExtraBytes;
FreeListAdd(qfshr->hfl,qsfb->foHeader,
FoAddDw(qsfb->sfh.foBlockSize,SFHeaderSize(&qsfb->sfh)));
qsfb->foHeader=GetFreeBlock(qfshr,FoAddDw(foSize,SFHeaderSize(&qsfb->sfh)),NULL);
foExtraBytes=FreeListGetBlockAt(qfshr->hfl,FoAddFo(qsfb->foHeader,foSize),NULL);
qsfb->sfh.foBlockSize=FoAddFo(foSize,foExtraBytes);
}
}
else
{
qsfb->foHeader=GetFreeBlock(qfshr,FoAddDw(foSize,SFHeaderSize(&qsfb->sfh)),NULL);
if (!FoIsNil(qsfb->foHeader))
qsfb->sfh.foBlockSize=foSize;
}
VFileSetBase(qsfb->hvf,qfshr->fid,FoAddDw(qsfb->foHeader,SFHeaderSize(&qsfb->sfh)),qsfb->sfh.foBlockSize);
}
// Update btree if necessary (always for now)
if (!(qsfb->bOpenFlags&HFOPEN_SYSTEM))
{
FILE_REC fr;
KEY key = NewKeyFromSz(qsfb->rgchKey);
// Update with proper file size instead of allocated size, in case we crash,
// then this is the true file size at this moment.
qsfb->sfh.bFlags&=(~SFH_INVALID);
SetFrData(&fr,qsfb->foHeader,foSize,qsfb->sfh.bFlags); // 0 flag now, since file is no longer invalid
rc = RcUpdateHbt(qfshr->hbt, key, &fr);
DisposeMemory((LPSTR)key);
}
else
{
qfshr->fsh.foDirectory=qsfb->foHeader;
qfshr->fsh.sfhSystem=qsfb->sfh;
// Update with proper file size instead of allocated size, in case we crash,
// then this is the true file size at this moment.
qfshr->fsh.sfhSystem.foBlockSize=foSize;
}
FidFlush(qfshr->fid);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
}
_GLOBALUNLOCK(hsfbFirst);
return rc;
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcAbandonHf |
* Abandon the creation of a new subfile.
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @rdesc Returns S_OK if file creation abandonded OK.
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API RcAbandonHf(HF hf)
{
QSFB qsfb;
HFS hfs;
QFSHR qfshr;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD_PTR)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb)) == NULL)
{
return E_OUTOFMEMORY;
}
hfs=qsfb->hfs;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
return E_OUTOFMEMORY;
}
_ENTERCRITICALSECTION(&qfshr->cs);
assert(qsfb->hvf);
qsfb->wLockCount--;
if (!qsfb->wLockCount)
{
// We should free the hsfb
if(qsfb->bOpenFlags&HFOPEN_READWRITE)
{
FILE_REC fr;
KEY key;
assert(qsfb->hvf);
VFileAbandon(qsfb->hvf); // Should abandon automatically do close too?
VFileClose(qsfb->hvf);
qsfb->hvf=NULL;
key = NewKeyFromSz(qsfb->rgchKey);
// Do error checking here to signal if Abandon fails???
// Remove from btree if we just now inserted it
RcLookupByKey(qfshr->hbt, key, NULL, &fr);
GetFrData(&fr);
if (fr.bFlags&SFH_INVALID)
{
RcDeleteHbt(qfshr->hbt, key);
}
DisposeMemory((LPSTR)key);
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
HsfbRemove(mv_gsfa[(DWORD_PTR)hf].hsfb);
}
else
{
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
_GLOBALFREE(mv_gsfa[(DWORD_PTR)hf].hsfb);
}
}
else
{
// subfile block still valid, since it has a lock count
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
}
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
// This hf is no longer valid
mv_gsfa[(DWORD_PTR)hf].hsfb=NULL;
#ifdef _DEBUGMVFS
DPF2("HfAbandon: hf=%d, %d\n", hf, 0);
DumpMV_GSFA();
#endif
return S_OK;
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcFlushHf |
* Flush the subfile by ensuring it is written in the file system
*
* @parm HF | hf |
* Handle to a valid subfile.
*
* @rdesc Returns S_OK if flushed OK.
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API RcFlushHf(HF hf)
{
QSFB qsfb;
HRESULT rc=S_OK;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD_PTR)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb)) == NULL)
{
return E_INVALIDARG;
}
if (qsfb->bOpenFlags&HFOPEN_READWRITE)
{
// Copy back to M20 file if necessary
// Update btree info
rc=RcFlushHsfb(mv_gsfa[(DWORD_PTR)hf].hsfb);
}
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
return (rc);
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcCloseEveryHf |
* Close every opened subfile. This should only be used in emergency
* cases to safely close all subfiles. Ideally, the calling application
* should close the subfiles as necessary, and never call this function.
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @rdesc Returns S_OK if all files closed OK.
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API RcCloseEveryHf(HFS hfs)
{
QFSHR qfshr;
HRESULT errb;
HRESULT rc = S_OK;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
return(E_INVALIDARG);
}
_ENTERCRITICALSECTION(&qfshr->cs);
while (qfshr->hsfbFirst)
{
HSFB hsfbNext;
errb=S_OK;
hsfbNext=HsfbCloseHsfb(qfshr->hsfbFirst, &errb);
if (errb==S_OK)
{
int q;
rc=HsfbRemove(qfshr->hsfbFirst); // destroys handle too
// now remove from our global array any left over references
for (q=1;q<giMaxSubFiles;q++)
if (mv_gsfa[q].hsfb==qfshr->hsfbFirst)
{
mv_gsfa[q].hsfb=NULL;
mv_gsfa[q].foCurrent=foNil;
}
}
qfshr->hsfbFirst=hsfbNext;
}
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
#ifdef _DEBUGMVFS
DPF2("RcCloseEveryHf: hfs=%d, %d\n", hfs, 0);
DumpMV_GSFA();
#endif
return S_OK;
}
#ifdef _DEBUGMVFS
void DumpMV_GSFA(void)
{
int q;
QSFB qsfb;
HANDLE h;
QFSHR qfshr;
if (mv_gsfa == NULL)
{
DPF2("mv_gsfa is EMPTY (%d, %d)\n",0,0);
return;
}
DPF2("mv_gsfa_count=%ld, giMaxSubfiles=%ld\n", mv_gsfa_count, giMaxSubFiles);
for (q=1;q<giMaxSubFiles;q++)
if ((h = mv_gsfa[q].hsfb) && (qsfb = _GLOBALLOCK(h)))
{
if (qfshr = _GLOBALLOCK(qsfb->hfs))
{
DPF4("mv_gsfa[%d]: qsfb->hfs=%ld ,qfshr->fid=%ld, wLock=%ld\n", \
q, qsfb->hfs, qfshr->fid, qsfb->wLockCount);
_GLOBALUNLOCK(qsfb->hfs);
}
_GLOBALUNLOCK(h);
}
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcFlushEveryHf |
* Flush every opened subfile. This should only be used in emergency
* cases to flush all subfiles.
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @rdesc Returns S_OK if all files flushed OK.
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API RcFlushEveryHf(HFS hfs)
{
QFSHR qfshr;
HSFB hsfbCursor;
HRESULT rc = S_OK;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
return(E_INVALIDARG);
}
_ENTERCRITICALSECTION(&qfshr->cs);
hsfbCursor=qfshr->hsfbFirst;
while (hsfbCursor)
{
HSFB hsfbNext=NULL;
QSFB qsfb;
if (!(qsfb = _GLOBALLOCK(hsfbCursor)))
{
rc=E_OUTOFMEMORY;
exit1:
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
return rc;
}
hsfbNext=qsfb->hsfbNext;
if ((rc=RcFlushHsfb(hsfbCursor))!=S_OK)
{
_GLOBALUNLOCK(hsfbCursor);
goto exit1;
}
_GLOBALUNLOCK(hsfbCursor);
hsfbCursor=hsfbNext;
}
goto exit1;
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcCloseHf |
* Close the subfile. All data is written back into the file system
* and the directory btree is updated with the file's size.
*
* @parm HF | hf |
* Handle to a valid subfile
*
* @rdesc Returns S_OK if all files closed OK.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC HRESULT PASCAL FAR EXPORT_API RcCloseHf(HF hf)
{
QSFB qsfb;
QFSHR qfshr;
FILEOFFSET foSize=foNil;
HRESULT rc=S_OK;
BOOL bFreeBlock=FALSE;
HFS hfs;
assert(mv_gsfa);
assert(mv_gsfa_count);
if (mv_gsfa[(DWORD)hf].hsfb == NULL || (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD)hf].hsfb)) == NULL)
{
return E_INVALIDARG;
}
hfs=qsfb->hfs;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
return E_INVALIDARG;
}
_ENTERCRITICALSECTION(&qfshr->cs);
assert(qsfb->hvf);
qsfb->wLockCount--;
if (!qsfb->wLockCount)
{
HRESULT errb;
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
errb.err=S_OK;
HsfbCloseHsfb(mv_gsfa[(DWORD)hf].hsfb, &errb);
rc=errb.err;
if (rc==S_OK)
{
rc=HsfbRemove(mv_gsfa[(DWORD)hf].hsfb); // destroys handle too
mv_gsfa[(DWORD)hf].hsfb=NULL;
}
}
else
{
_GLOBALUNLOCK(mv_gsfa[(DWORD)hf].hsfb);
mv_gsfa[(DWORD)hf].hsfb=NULL;
}
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
#ifdef _DEBUGMVFS
DPF2("RcCloseHf: hfs=%d, hf=%d\n", hfs, hf);
DumpMV_GSFA();
#endif
return (rc);
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcUnlinkFileHfs |
* Remove a subfile from the file system. The file should not be opened
* by any other processes when it is removed.
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @parm LPCSTR | szFileName |
* Name of file in file system to remove
*
* @rdesc Returns S_OK if file deleted OK.
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API RcUnlinkFileHfs(HFS hfs, LPCSTR sz)
{
#ifdef MOSMAP // {
// Disable function
return (ERR_NOTSUPPORTED);
#else // } {
QFSHR qfshr;
FILE_REC fr;
HRESULT errb;
KEY key;
HRESULT rc=S_OK;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
return SetErrCode (&errb, E_INVALIDARG);
}
key = NewKeyFromSz(sz);
if (qfshr->fsh.bFlags & FSH_READONLY)
{
rc=SetErrCode (&errb, E_NOPERMISSION);
exit0:
DisposeMemory((LPSTR)key);
_GLOBALUNLOCK(hfs);
return rc;
}
_ENTERCRITICALSECTION(&qfshr->cs);
// check for File In Use ???
/* look it up to get the file base offset */
if ((rc = RcLookupByKey (qfshr->hbt, key, NULL, &fr)) != S_OK)
{
exit1:
_LEAVECRITICALSECTION(&qfshr->cs);
goto exit0;
}
GetFrData(&fr);
if (fr.bFlags&SFH_LOCKED)
{ rc=SetErrCode(&errb, E_NOPERMISSION);
goto exit1;
}
assert(qfshr->hfl);
if ((rc = RcDeleteHbt (qfshr->hbt, key)) == S_OK)
{
/* put the file block on the free list */
//SFH sfh;
FreeListAdd(qfshr->hfl,fr.foStart,fr.foSize);
//if ((FoEquals(FoSeekFid(qfshr->fid, fr.fo, wSeekSet, &errb),fr.fo)) &&
// (LcbWriteFid(qfshr->fid, &sfh,sizeof(SFH),&errb)==sizeof(SFH)))
//{
// FreeListAdd(qfshr->hfl,fr.fo,(LONG)sfh.foBlockSize);
//}
}
DisposeMemory((LPSTR)key);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
return rc;
#endif //}
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | SetFileFlags |
* Set the Logging or Locking state of a file
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @parm LPCSTR | szFileName |
* Name of file in file system to set bits of
*
* @parm BYTE | bFlags |
* SFH_LOGGING or SFH_LOCKED
*
* @rdesc Returns S_OK if flags set OK
*
***************************************************************************/
PUBLIC HRESULT PASCAL FAR EXPORT_API SetFileFlags(HFS hfs, LPCSTR sz, BYTE bFlags)
{
#ifdef MOSMAP // {
// Disable function
return (ERR_NOTSUPPORTED);
#else // } {
QFSHR qfshr;
FILE_REC fr;
HRESULT errb;
KEY key;
HRESULT rc=S_OK;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
return SetErrCode (&errb, E_INVALIDARG);
}
key = NewKeyFromSz(sz);
if (qfshr->fsh.bFlags & FSH_READONLY)
{
rc=SetErrCode (&errb, E_NOPERMISSION);
exit0:
DisposeMemory((LPSTR)key);
_GLOBALUNLOCK(hfs);
return rc;
}
_ENTERCRITICALSECTION(&qfshr->cs);
// check for File In Use ???
/* look it up to get the file base offset */
if ((rc = RcLookupByKey (qfshr->hbt, key, NULL, &fr)) != S_OK)
{
_LEAVECRITICALSECTION(&qfshr->cs);
goto exit0;
}
GetFrData(&fr);
fr.bFlags=fr.bFlags&(~SFH_FILEFLAGS);
bFlags&=SFH_FILEFLAGS;
fr.bFlags|=bFlags;
SetFrData(&fr,fr.foStart,fr.foSize,fr.bFlags);
//fr.lifBase=qsfb->foHeader.lOffset;
if ((rc = RcUpdateHbt(qfshr->hbt, key, &fr))!=S_OK)
{
// Can't update btree??? What now???
}
DisposeMemory((LPSTR)key);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
return rc;
#endif //}
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func BYTE PASCAL FAR | GetFileFlags |
* Get the Logging or Locking state of a file
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @parm LPCSTR | szFileName |
* Name of file in file system to set bits of
*
* @parm PHRESULT | phr |
* Error return code
*
* @rdesc Returns zero or any combination of SFH_LOGGING and SFH_LOCKED
*
***************************************************************************/
PUBLIC BYTE PASCAL FAR EXPORT_API GetFileFlags(HFS hfs, LPCSTR sz, PHRESULT phr)
{
#ifdef MOSMAP // {
// Disable function
return (ERR_NOTSUPPORTED);
#else // } {
QFSHR qfshr;
FILE_REC fr;
KEY key;
HRESULT rc;
*phr=S_OK;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return 0;
}
key = NewKeyFromSz(sz);
if (qfshr->fsh.bFlags & FSH_READONLY)
{
SetErrCode (phr, E_NOPERMISSION);
exit0:
DisposeMemory((LPSTR)key);
_GLOBALUNLOCK(hfs);
return 0;
}
_ENTERCRITICALSECTION(&qfshr->cs);
// check for File In Use ???
/* look it up to get the file base offset */
if ((rc = RcLookupByKey (qfshr->hbt, key, NULL, &fr)) != S_OK)
{
_LEAVECRITICALSECTION(&qfshr->cs);
SetErrCode(phr,rc);
goto exit0;
}
GetFrData(&fr);
fr.bFlags=fr.bFlags&(~SFH_FILEFLAGS);
DisposeMemory((LPSTR)key);
_LEAVECRITICALSECTION(&qfshr->cs);
_GLOBALUNLOCK(hfs);
return fr.bFlags;
#endif //}
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func BOOL PASCAL FAR | FAccessHfs |
* Check whether a sub file has specific attributes.
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @parm LPCSTR | szFileName |
* Name of subfile to check
*
* @parm BYTE | bFlags |
* Any one of the following attributes:
* @flag FACCESS_EXISTS | Does the file exist?
* @flag FACCESS_READWRITE | Can we open the file for read/write?
* @flag FACCESS_READ | Can we open the file for reading?
* @flag FACCESS_LIVESINFS | Does the file live in the file system now?
* @flag FACCESS_LIVESINTEMP | Does the file live in a temporary file on th HD now?
*
* @parm PHRESULT | phr |
* Error return
*
* @rdesc Returns TRUE if the file has the given attribute. Error code
* will not be S_OK if an error occurred.
*
***************************************************************************/
#ifndef ITWRAP
PUBLIC BOOL PASCAL FAR EXPORT_API FAccessHfs( HFS hfs, LPCSTR szName, BYTE bFlags, PHRESULT phr)
{
QFSHR qfshr;
FILE_REC fr;
KEY key;
BOOL bSuccess=FALSE;
HRESULT rc;
//SetErrCode (phr, S_OK);
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
SetErrCode (phr, E_INVALIDARG);
return bSuccess;
}
key = NewKeyFromSz(szName);
_ENTERCRITICALSECTION(&qfshr->cs);
rc = RcLookupByKey (qfshr->hbt, key, NULL, &fr);
_LEAVECRITICALSECTION(&qfshr->cs);
if (rc==S_OK)
{
if (bFlags&FACCESS_EXISTS)
bSuccess=TRUE;
else
{
HSFB hsfbCursor;
// Look up subfile block and check for permissions
hsfbCursor = qfshr->hsfbFirst;
while (hsfbCursor)
{
HSFB hsfbNext;
QSFB qsfb;
if (!(qsfb = _GLOBALLOCK(hsfbCursor)))
{
SetErrCode(phr,E_OUTOFMEMORY);
goto exit1;
}
hsfbNext=qsfb->hsfbNext;
if (FoEquals(qsfb->foHeader,fr.foStart))
{
// Cannot open for read/write...
if ((!(qsfb->bOpenFlags&HFOPEN_READWRITE)) && (bFlags&FACCESS_READ))
{
// Can open for read
bSuccess=TRUE;
}
else if (bFlags&(FACCESS_LIVESINFS|FACCESS_LIVESINTEMP))
{
// find out where this opened file lives
DWORD dwVFileFlags = VFileGetFlags(qsfb->hvf,NULL);
if (((dwVFileFlags&VFF_TEMP) && (bFlags&FACCESS_LIVESINTEMP)) ||
((dwVFileFlags&VFF_FID) && (bFlags&FACCESS_LIVESINFS)))
bSuccess=TRUE;
else
{
if (phr)
phr->err=E_NOPERMISSION; // reason for no access
}
}
else
{ if (phr)
phr->err=E_NOPERMISSION;
}
_GLOBALUNLOCK(hsfbCursor);
goto exit1;
}
_GLOBALUNLOCK(hsfbCursor);
hsfbCursor=hsfbNext;
}
// File is not currently opened
if (bFlags&(FACCESS_READ|FACCESS_READWRITE))
bSuccess=TRUE;
else
{
if (phr)
phr->err=E_NOPERMISSION;
}
}
}
else
{
if (phr)
phr->err=E_NOTEXIST;
}
exit1:
DisposeMemory((LPSTR)key);
_GLOBALUNLOCK(hfs);
return bSuccess;
}
#endif
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HRESULT PASCAL FAR | RcRenameFileHfs |
* Rename a subfile in the file system. The subfile must not currently
* be in use.
*
* @parm HFS | hfs |
* Handle to a valid file system
*
* @parm LPCSTR | szOld |
* Current file name
*
* @parm LPCSTR | szNew |
* New file name
*
* @rdesc Returns S_OK if the file was renamed OK.
* E_NOPERMISSION if file is in use, or file system is read-only.
* rcExists if file named szOld already exists in FS
* rcNoExists if file named szNew doesn't exist in FS
*
***************************************************************************/
PUBLIC HRESULT PASCAL PASCAL EXPORT_API RcRenameFileHfs(HFS hfs, LPCSTR szOld, LPCSTR szNew)
{
#ifdef MOSMAP // {
// Disable function
return ERR_NOTSUPPORTED;
#else // } {
QFSHR qfshr;
FILE_REC fr;
HRESULT rc;
HSFB hsfbCursor;
KEY keyOld,keyNew;
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
{
return(E_INVALIDARG);
}
if (qfshr->fsh.bFlags & FSH_READONLY)
{
rc=E_NOPERMISSION;
exit0:
_GLOBALUNLOCK(hfs);
return rc;
}
keyOld = NewKeyFromSz(szOld);
keyNew = NewKeyFromSz(szNew);
_ENTERCRITICALSECTION(&qfshr->cs);
if ((rc = RcLookupByKey (qfshr->hbt, keyOld, NULL, &fr))
!= S_OK)
{
goto exit1;
}
// Make sure file is not already opened by anyone
hsfbCursor = qfshr->hsfbFirst;
while (hsfbCursor)
{
HSFB hsfbNext;
QSFB qsfb;
if (!(qsfb = _GLOBALLOCK(hsfbCursor)))
{
rc=E_OUTOFMEMORY;
RcDeleteHbt( qfshr->hbt, keyNew);
goto exit1;
}
hsfbNext=qsfb->hsfbNext;
if (FoEquals(qsfb->foHeader,fr.foStart))
{
rc = E_NOPERMISSION;
_GLOBALUNLOCK(hsfbCursor);
goto exit1;
}
_GLOBALUNLOCK(hsfbCursor);
hsfbCursor=hsfbNext;
}
if ((rc = RcInsertHbt( qfshr->hbt, keyNew, &fr)) != S_OK )
{
goto exit1;
}
if ((rc = RcDeleteHbt(qfshr->hbt, keyOld) != S_OK))
{
// can't delete the old, so just delete the new and hope for
// the best!
if ((rc = RcDeleteHbt( qfshr->hbt, keyNew)) == S_OK)
rc = E_FAIL;
}
exit1:
DisposeMemory((LPSTR)keyOld);
DisposeMemory((LPSTR)keyNew);
_LEAVECRITICALSECTION(&qfshr->cs);
goto exit0;
#endif // } MOSMAP
}
/***************************************************************************
*
* @doc INTERNAL API
*
* @func BOOL PASCAL FAR | FHfValid |
* Check for validity of a subfile handle
*
* @parm HF | hf |
* Handle to a subfile
*
* @rdesc Returns TRUE if the subfile is valid.
*
***************************************************************************/
#ifndef ITWRAP
BOOL PASCAL FAR EXPORT_API FHfValid( HF hf)
{
return ((mv_gsfa) && (mv_gsfa_count) && ((WORD)hf<giMaxSubFiles) && (hf) && (mv_gsfa[(DWORD)hf].hsfb))?TRUE:FALSE;
}
#endif // !ITWRAP
/***************************************************************************
*
* @doc INTERNAL API
*
* @func HFS PASCAL FAR | HfsGetFromHf |
* Get the file system handle of a given subfile.
*
* @parm HF | hf |
* Handle to a subfile
*
* @rdesc Returns the subfile's parent file system handle, or NULL.
*
***************************************************************************/
HFS PASCAL FAR EXPORT_API HfsGetFromHf( HF hf )
{
HFS hfs=NULL;
QSFB qsfb;
assert(mv_gsfa);
assert(mv_gsfa_count);
if ((mv_gsfa[(DWORD_PTR)hf].hsfb) && (qsfb = _GLOBALLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb)))
{
hfs=qsfb->hfs;
_GLOBALUNLOCK(mv_gsfa[(DWORD_PTR)hf].hsfb);
}
return hfs;
}