|
|
/*****************************************************************************
* * * 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; }
|