1023 lines
27 KiB
C
1023 lines
27 KiB
C
/*****************************************************************************
|
|
* *
|
|
* FILESYS.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1995. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* All main File System commands (opening, closing) *
|
|
* any given offset, or in a temp file. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Current Owner: davej *
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Created 07/17/95 - davej
|
|
* 3/05/97 erinfox Change errors to HRESULTS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
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>
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Defines *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Prototypes *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* Private Functions *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
// FACCESS_READWRITE
|
|
// FACCESS_READ
|
|
BOOL PASCAL FAR FHfsAccess(HFS hfs, BYTE bFlags)
|
|
{
|
|
QFSHR qfshr;
|
|
BOOL bHasAccess=FALSE;
|
|
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ((qfshr->fsh.bFlags&FSH_READWRITE) && (bFlags&FACCESS_READWRITE))
|
|
bHasAccess=TRUE;
|
|
if (bFlags&FACCESS_READ)
|
|
bHasAccess=TRUE;
|
|
|
|
_GLOBALUNLOCK(hfs);
|
|
return bHasAccess;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc PRIVATE
|
|
*
|
|
* @func HFS PASCAL NEAR | HfsOpenFileSysFmInternal |
|
|
* Open or Create a file system
|
|
*
|
|
* @parm FM | fmFileName |
|
|
* File name of file system to open or create (FM is identical to LPSTR)
|
|
*
|
|
* @parm BYTE | bFlags |
|
|
* Combinations of the FSH_ flags
|
|
* @flag FSH_READWRITE | (0x01) FS will be updated
|
|
* @flag FSH_READONLY | (0x02) FS will be read only, no updating
|
|
* @flag FSH_CREATE | (0x04) Create a new FS
|
|
* @flag FSH_M14 | (0x08) Not used really, since we return
|
|
* @flag FSH_FASTUPDATE | (0x10) System Btree is not copied to temp file
|
|
* unless absolutely necessary
|
|
* @flag FSH_DISKBTREE | (0x20) System Btree is always copied to disk if
|
|
* possible for speed. Btree may be very very
|
|
* large, and this is NOT recommended for on-line
|
|
*
|
|
* @parm FS_PARAMS FAR * | lpfsp |
|
|
* File system initialization parameters (currently btree block size)
|
|
*
|
|
* @parm PHRESULT | lpErrb |
|
|
* Error return
|
|
*
|
|
* @rdesc Returns a valid HFS or NULL if an error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
HFS PASCAL NEAR HfsOpenFileSysFmInternal(FM fm,
|
|
BYTE bFlags, FS_PARAMS FAR *qfsp, PHRESULT phr)
|
|
{
|
|
#ifdef MOSMAP // {
|
|
// Disable function
|
|
if (bFlags&(FSH_CREATE|FSH_READWRITE))
|
|
{
|
|
SetErrCode (phr, ERR_NOTSUPPORTED);
|
|
return NULL;
|
|
}
|
|
#else // } {
|
|
HFS hfs;
|
|
QFSHR qfshr;
|
|
BTREE_PARAMS btp;
|
|
|
|
/* make file system header */
|
|
|
|
if (( hfs = _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE| GMEM_MOVEABLE,
|
|
(LONG)sizeof( FSHR) ) ) == NULL )
|
|
{
|
|
SetErrCode (phr, E_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
qfshr = _GLOBALLOCK(hfs);
|
|
|
|
_INITIALIZECRITICALSECTION(&qfshr->cs);
|
|
|
|
// Copy File Moniker
|
|
if ((qfshr->fm = FmCopyFm(fm, phr)) == fmNil)
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
exit0:
|
|
_DELETECRITICALSECTION(&qfshr->cs);
|
|
_GLOBALUNLOCK(hfs);
|
|
_GLOBALFREE(hfs);
|
|
return 0;
|
|
}
|
|
|
|
// Open or Create fid
|
|
if (bFlags&FSH_CREATE)
|
|
qfshr->fid = FidCreateFm(qfshr->fm, wReadWrite, wReadWrite, phr);
|
|
else
|
|
qfshr->fid = FidOpenFm(qfshr->fm,
|
|
(WORD)((bFlags & FSH_READONLY)?(wReadOnly | wShareRead) : (wReadWrite | wShareNone)), phr);
|
|
if (qfshr->fid == fidNil)
|
|
{
|
|
exit1:
|
|
//FreeListDestroy(qfshr->hfl);
|
|
SetErrCode(phr,E_NOPERMISSION);
|
|
DisposeFm(qfshr->fm);
|
|
goto exit0;
|
|
}
|
|
|
|
qfshr->hsfbFirst=NULL;
|
|
qfshr->hsfbSystem=NULL;
|
|
|
|
// Initialize global SubFile header blocks array - shared by everyone :-)
|
|
// So we take multi-threading precautions
|
|
if (!_INTERLOCKEDINCREMENT(&mv_gsfa_count))
|
|
{
|
|
HANDLE hSfa;
|
|
|
|
mv_gsfa_count=1L;
|
|
mv_gsfa=NULL;
|
|
_INITIALIZECRITICALSECTION(&mv_gsfa_cs);
|
|
|
|
if (((hSfa=_GLOBALALLOC(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_SHARE,sizeof(SF)*MAXSUBFILES))==NULL) ||
|
|
((mv_gsfa=(SF FAR *)_GLOBALLOCK(hSfa))==NULL))
|
|
{
|
|
if (hSfa)
|
|
_GLOBALFREE(hSfa);
|
|
SetErrCode (phr, E_OUTOFMEMORY);
|
|
exit2:
|
|
if (!(_INTERLOCKEDDECREMENT(&mv_gsfa_count)))
|
|
{
|
|
if (mv_gsfa)
|
|
{
|
|
_GLOBALUNLOCK(GlobalHandle(mv_gsfa));
|
|
_GLOBALFREE(GlobalHandle(mv_gsfa));
|
|
mv_gsfa=NULL;
|
|
}
|
|
_DELETECRITICALSECTION(&mv_gsfa_cs);
|
|
mv_gsfa_count=-1L;
|
|
}
|
|
goto exit1;
|
|
}
|
|
mv_gsfa[0].hsfb=(HSFB)-1;
|
|
}
|
|
|
|
if (bFlags&(FSH_READWRITE|FSH_DISKBTREE))
|
|
{
|
|
// If we are ambitious, we can try lower scratch sizes, but if we are that low
|
|
// on memory, chances are we are just going to fail a few steps from here.
|
|
HANDLE hBuf;
|
|
if (!(hBuf=_GLOBALALLOC(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_SHARE,(DWORD)SCRATCHBUFSIZE)))
|
|
{
|
|
SetErrCode(phr, E_OUTOFMEMORY);
|
|
goto exit2;
|
|
}
|
|
|
|
if (!(qfshr->sb.lpvBuffer=_GLOBALLOCK(hBuf)))
|
|
{
|
|
_GLOBALFREE(hBuf);
|
|
goto exit2;
|
|
}
|
|
qfshr->sb.lcbBuffer=(LONG)SCRATCHBUFSIZE;
|
|
_INITIALIZECRITICALSECTION(&qfshr->sb.cs);
|
|
}
|
|
|
|
if (!(bFlags&FSH_CREATE))
|
|
{
|
|
BYTE bFlagsBtree=0L;
|
|
|
|
if ((LcbReadFid(qfshr->fid, &qfshr->fsh,(LONG)sizeof(FSH),
|
|
phr)) != (LONG)sizeof(FSH))
|
|
{
|
|
SetErrCode (phr, E_FILEINVALID);
|
|
exit3:
|
|
if (bFlags&(FSH_READWRITE|FSH_DISKBTREE))
|
|
_DELETECRITICALSECTION(&qfshr->sb.cs);
|
|
if (qfshr->sb.lpvBuffer)
|
|
{
|
|
if (!(bFlags&(FSH_READWRITE|FSH_DISKBTREE)))
|
|
_DELETECRITICALSECTION(&qfshr->sb.cs);
|
|
_GLOBALUNLOCK(GlobalHandle(qfshr->sb.lpvBuffer));
|
|
_GLOBALFREE(GlobalHandle(qfshr->sb.lpvBuffer));
|
|
}
|
|
RcCloseFid(qfshr->fid);
|
|
if (bFlags&FSH_CREATE)
|
|
RcUnlinkFm(qfshr->fm);
|
|
goto exit2;
|
|
}
|
|
/* Handle MAC swapping */
|
|
|
|
if (qfshr->fsh.wMagic == SWAPWORD(wFileSysMagic))
|
|
{
|
|
qfshr->fsh.wMagic = SWAPWORD(qfshr->fsh.wMagic);
|
|
qfshr->fsh.foDirectory.dwOffset = SWAPLONG(qfshr->fsh.foDirectory.dwOffset);
|
|
qfshr->fsh.foDirectory.dwHigh = SWAPLONG(qfshr->fsh.foDirectory.dwHigh);
|
|
qfshr->fsh.foFreeList.dwOffset = SWAPLONG(qfshr->fsh.foFreeList.dwOffset);
|
|
qfshr->fsh.foFreeList.dwHigh = SWAPLONG(qfshr->fsh.foFreeList.dwHigh);
|
|
qfshr->fsh.foEof.dwOffset = SWAPLONG(qfshr->fsh.foEof.dwOffset);
|
|
qfshr->fsh.foEof.dwHigh = SWAPLONG(qfshr->fsh.foEof.dwHigh);
|
|
}
|
|
qfshr->fsh.bFlags=(qfshr->fsh.bFlags&(~(FSH_READWRITE|FSH_READONLY)))|bFlags;
|
|
|
|
|
|
if (qfshr->fsh.wMagic != wFileSysMagic)
|
|
{
|
|
SetErrCode (phr, E_FILEINVALID);
|
|
goto exit3;
|
|
}
|
|
if (qfshr->fsh.bVersion != bFileSysVersion)
|
|
{
|
|
if (qfshr->fsh.bVersion == bFileSysVersionOld)
|
|
{
|
|
qfshr->fsh.bFlags|=FSH_M14;
|
|
SetErrCode(phr, E_BADVERSION); // Try another error specially for M14?
|
|
goto exit3;
|
|
}
|
|
else
|
|
{
|
|
SetErrCode(phr, E_BADVERSION);
|
|
goto exit3;
|
|
}
|
|
}
|
|
// Open Free List if r/w mode
|
|
if (bFlags&FSH_READWRITE)
|
|
{
|
|
if ((!FoEquals(FoSeekFid(qfshr->fid,qfshr->fsh.foFreeList,wSeekSet,
|
|
phr),qfshr->fsh.foFreeList)) ||
|
|
((qfshr->hfl=FreeListInitFromFid( qfshr->fid, phr ))==NULL))
|
|
{
|
|
goto exit3;
|
|
}
|
|
}
|
|
|
|
// Open Btree making temp file for btree
|
|
//if ((qfshr->hbt = HbtOpenBtreeSz(NULL, hfs,
|
|
// (BYTE)((qfshr->fsh.bFlags&FSH_READONLY?HFOPEN_READ:HFOPEN_READWRITE)|HFOPEN_SYSTEM),
|
|
// NULL, phr)) == NULL)
|
|
|
|
// Open Btree, temp only gets made if necessary
|
|
|
|
bFlagsBtree|=HFOPEN_SYSTEM;
|
|
if (qfshr->fsh.bFlags&FSH_READONLY)
|
|
{
|
|
bFlagsBtree|=HFOPEN_READ;
|
|
if (qfshr->fsh.bFlags&FSH_DISKBTREE)
|
|
bFlagsBtree|=HFOPEN_FORCETEMP;
|
|
}
|
|
else
|
|
{
|
|
bFlagsBtree|=HFOPEN_READWRITE;
|
|
if (qfshr->fsh.bFlags&FSH_FASTUPDATE)
|
|
bFlagsBtree|=HFOPEN_NOTEMP;
|
|
}
|
|
|
|
//if ((qfshr->hbt = HbtOpenBtreeSz(NULL, hfs,
|
|
// (BYTE)((qfshr->fsh.bFlags&FSH_READONLY?HFOPEN_READ:
|
|
// ((qfshr->fsh.bFlags&FSH_FASTUPDATE)?HFOPEN_NOTEMP:0)|HFOPEN_READWRITE)|
|
|
// HFOPEN_SYSTEM),
|
|
// NULL, phr)) == NULL)
|
|
|
|
if ((qfshr->hbt = HbtOpenBtreeSz(NULL, hfs, bFlagsBtree, phr))==NULL)
|
|
{
|
|
exit4:
|
|
if (qfshr->hfl)
|
|
FreeListDestroy(qfshr->hfl);
|
|
goto exit3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WORD wFreeListSize = wDefaultFreeListSize;
|
|
if ((qfsp) && (qfsp->wFreeListSize))
|
|
wFreeListSize = qfsp->wFreeListSize;
|
|
|
|
qfshr->fsh.wMagic = wFileSysMagic;
|
|
qfshr->fsh.bVersion = bFileSysVersion;
|
|
qfshr->fsh.bFlags = FSH_READWRITE;
|
|
qfshr->fsh.foFreeList.dwOffset = sizeof(FSH); // Free list directly after header
|
|
qfshr->fsh.foFreeList.dwHigh = 0L;
|
|
qfshr->fsh.foDirectory = foNil; // Filled in with system file start
|
|
if (bFlags&FSH_READWRITE)
|
|
{ if ((qfshr->hfl=FreeListInit(wFreeListSize, phr))==NULL)
|
|
{
|
|
goto exit3;
|
|
}
|
|
}
|
|
qfshr->fsh.foEof.dwOffset = sizeof(FSH)+FreeListSize(qfshr->hfl,phr); // Free starts here
|
|
|
|
qfshr->fsh.foEof.dwHigh = 0L;
|
|
|
|
/* build btree directory */
|
|
btp.hfs = hfs;
|
|
btp.bFlags = HFOPEN_READWRITE|HFOPEN_SYSTEM;
|
|
if (qfsp != NULL)
|
|
{
|
|
btp.cbBlock = qfsp->cbBlock;
|
|
}
|
|
else
|
|
{
|
|
btp.cbBlock = cbBtreeBlockDefault;
|
|
}
|
|
lstrcpy(btp.rgchFormat, "VOO1"); // "VY", KT_VSTI, FMT_VNUM_FO, FMT_VNUM_LCB, file byte // 'VOL1' works too
|
|
|
|
qfshr->hbt = HbtCreateBtreeSz(NULL, &btp, phr);
|
|
if (qfshr->hbt == NULL)
|
|
{
|
|
goto exit4;
|
|
}
|
|
}
|
|
|
|
// NO harm writing out file system file so far (???)
|
|
//LSeekFid(qfshr->fid, 0L, wSeekSet, phr);
|
|
//LcbWriteFid(qfshr->fid, &qfshr->fsh, sizeof(FSH), phr);
|
|
//Lcb ... also no need to write it out yet!
|
|
|
|
/* return handle to file system */
|
|
_GLOBALUNLOCK(hfs);
|
|
return hfs;
|
|
|
|
#endif //}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HFS PASCAL FAR | HfsCreateFileSysFm |
|
|
* Creates a new file system
|
|
*
|
|
* @parm FM | fmFileName |
|
|
* File name of file system to open or create (FM is same as LPSTR)
|
|
*
|
|
* @parm FS_PARAMS FAR * | lpfsp |
|
|
* File system initialization parameters (currently btree block size)
|
|
*
|
|
* @parm PHRESULT | lpErrb |
|
|
* Error return
|
|
*
|
|
* @rdesc Returns a valid HFS or NULL if an error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HFS PASCAL FAR EXPORT_API HfsCreateFileSysFm(FM fm,
|
|
FS_PARAMS FAR *qfsp, PHRESULT phr)
|
|
{
|
|
return HfsOpenFileSysFmInternal(fm,FSH_CREATE|FSH_READWRITE,qfsp,phr);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HFS PASCAL FAR | HfsOpenFm |
|
|
* Open an existing file system
|
|
*
|
|
* @parm FM | fmFileName |
|
|
* File name of file system to open (FM is same as LPSTR)
|
|
*
|
|
* @parm BYTE | bFlags |
|
|
* Any of the FSH_ flags
|
|
* @flag FSH_READWRITE | (BYTE)0x01 FS will be updated
|
|
* @flag FSH_READONLY | (BYTE)0x02 FS will be read only, no updating
|
|
* @flag FSH_M14 | (BYTE)0x08 Not used really, since we return
|
|
* @flag FSH_FASTUPDATE | (BYTE)0x10 System Btree is not copied to temp file
|
|
* unless absolutely necessary
|
|
* @flag FSH_DISKBTREE | (BYTE)0x20 System Btree is always copied to disk if
|
|
* possible for speed. Btree may be very very
|
|
* large, and this is NOT recommended for on-line
|
|
*
|
|
* @parm PHRESULT | lpErrb |
|
|
* Error return
|
|
*
|
|
* @rdesc Returns a valid HFS or NULL if an error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HFS PASCAL FAR EXPORT_API HfsOpenFm(FM fm, BYTE bFlags,
|
|
PHRESULT phr)
|
|
{
|
|
if (bFlags&FSH_CREATE)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return NULL;
|
|
}
|
|
return HfsOpenFileSysFmInternal(fm,bFlags,NULL,phr);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HRESULT PASCAL FAR | RcCloseHfs |
|
|
* Close an opened File System
|
|
*
|
|
* @parm HFS | hfs |
|
|
* Handle of opened file system
|
|
*
|
|
* @rdesc Returns S_OK if closed OK, else an error
|
|
*
|
|
* @comm
|
|
* state IN:
|
|
* state OUT:
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
HRESULT PASCAL FAR RcCloseHfs(HFS hfs)
|
|
{
|
|
QFSHR qfshr;
|
|
HRESULT errb;
|
|
|
|
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
|
|
{
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
if (!mv_gsfa_count)
|
|
return E_ASSERT;
|
|
|
|
_ENTERCRITICALSECTION(&qfshr->cs);
|
|
|
|
// We need to:
|
|
// (1) close all subfiles if any open
|
|
// (2) close the btree
|
|
// (3) r/w write free list
|
|
// (4) r/w write header
|
|
|
|
RcCloseEveryHf(hfs);
|
|
|
|
//while (qfshr->hsfbFirst)
|
|
//{
|
|
// qfshr->hsfbFirst=HsfbCloseHsfb(qfshr->hsfbFirst, &errb);
|
|
// if (errb.err!=S_OK)
|
|
// break;
|
|
//}
|
|
|
|
if ((errb = RcCloseOrFlushHbt(qfshr->hbt, TRUE)) != S_OK)
|
|
{
|
|
/* out of disk space, internal error, or out of file handles. */
|
|
if (errb != E_HANDLE)
|
|
{
|
|
/* attempt to invalidate FS by clobbering magic number */
|
|
LSeekFid(qfshr->fid, 0L, wSeekSet, NULL);
|
|
qfshr->fsh.wMagic = 0;
|
|
LcbWriteFid(qfshr->fid, &qfshr->fsh, (LONG)sizeof(FSH), NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (qfshr->fsh.bFlags & FSH_READWRITE)
|
|
{
|
|
FoSeekFid(qfshr->fid, qfshr->fsh.foFreeList, wSeekSet, NULL);
|
|
FreeListWriteToFid(qfshr->hfl, qfshr->fid, NULL);
|
|
FreeListDestroy(qfshr->hfl);
|
|
|
|
if (LSeekFid(qfshr->fid, 0L, wSeekSet, &errb) == 0L)
|
|
{
|
|
LcbWriteFid(qfshr->fid, &qfshr->fsh, (LONG)sizeof(FSH), &errb);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove our access to the global subfile handle object
|
|
if (!(_INTERLOCKEDDECREMENT(&mv_gsfa_count)))
|
|
{
|
|
// Last person using subfile array deletes it too!
|
|
// Can we use the GlobalHandle function OK in all environments?
|
|
_GLOBALUNLOCK(GlobalHandle(mv_gsfa));
|
|
_GLOBALFREE(GlobalHandle(mv_gsfa));
|
|
mv_gsfa=NULL;
|
|
mv_gsfa_count=-1L;
|
|
_DELETECRITICALSECTION(&mv_gsfa_cs);
|
|
}
|
|
|
|
RcCloseFid(qfshr->fid);
|
|
|
|
_LEAVECRITICALSECTION(&qfshr->cs);
|
|
_DELETECRITICALSECTION(&qfshr->cs);
|
|
if (qfshr->sb.lpvBuffer)
|
|
{
|
|
_DELETECRITICALSECTION(&qfshr->sb.cs);
|
|
_GLOBALUNLOCK(GlobalHandle(qfshr->sb.lpvBuffer));
|
|
_GLOBALFREE(GlobalHandle(qfshr->sb.lpvBuffer));
|
|
}
|
|
DisposeFm(qfshr->fm);
|
|
_GLOBALUNLOCK(hfs);
|
|
_GLOBALFREE(hfs);
|
|
|
|
return errb;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HRESULT PASCAL FAR | RcFlushHfs |
|
|
* Write all temporary info in file system to FS file
|
|
*
|
|
* @parm HFS | hfs |
|
|
* Handle of opened file system
|
|
*
|
|
* @rdesc Returns S_OK if closed OK, else an error
|
|
*
|
|
* @comm
|
|
* state IN:
|
|
* state OUT:
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
HRESULT PASCAL FAR RcFlushHfs(HFS hfs)
|
|
{
|
|
QFSHR qfshr;
|
|
HRESULT errb;
|
|
//HSFB hsfbCursor;
|
|
HRESULT rc=S_OK;
|
|
|
|
|
|
if (hfs == NULL || (qfshr = _GLOBALLOCK(hfs)) == NULL)
|
|
{
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
_ENTERCRITICALSECTION(&qfshr->cs);
|
|
|
|
// We need to:
|
|
// (1) close all subfiles if any open
|
|
// (2) close the btree
|
|
// (3) r/w write free list
|
|
// (4) r/w write header
|
|
|
|
RcFlushEveryHf(hfs);
|
|
|
|
/*
|
|
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;
|
|
}
|
|
*/
|
|
|
|
if ((rc = RcCloseOrFlushHbt(qfshr->hbt, FALSE)) != S_OK)
|
|
{
|
|
/* out of disk space, internal error, or out of file handles. */
|
|
if (rc != E_HANDLE)
|
|
{
|
|
/* attempt to invalidate FS by clobbering magic number */
|
|
LSeekFid(qfshr->fid, 0L, wSeekSet, NULL);
|
|
qfshr->fsh.wMagic = 0;
|
|
LcbWriteFid(qfshr->fid, &qfshr->fsh, (LONG)sizeof(FSH), NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (qfshr->fsh.bFlags & FSH_READWRITE)
|
|
{
|
|
FoSeekFid(qfshr->fid, qfshr->fsh.foFreeList, wSeekSet, NULL);
|
|
FreeListWriteToFid(qfshr->hfl, qfshr->fid, NULL);
|
|
|
|
if (LSeekFid(qfshr->fid, 0L, wSeekSet, &errb) == 0L)
|
|
{
|
|
LcbWriteFid(qfshr->fid, &qfshr->fsh, (LONG)sizeof(FSH), &errb);
|
|
}
|
|
|
|
FidFlush(qfshr->fid);
|
|
}
|
|
}
|
|
|
|
_LEAVECRITICALSECTION(&qfshr->cs);
|
|
_GLOBALUNLOCK(hfs);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HRESULT PASCAL FAR | RcDestroyFileSysFm |
|
|
* Deletes a closed file system (removes it from the disk)
|
|
*
|
|
* @parm FM | fmFileName |
|
|
* Name of file to destroy (FM is same as LPSTR)
|
|
*
|
|
* @rdesc Returns S_OK if deleted
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API RcDestroyFileSysFm(FM fm)
|
|
{
|
|
#ifdef MOSMAP // {
|
|
// Disable function
|
|
return ERR_NOTSUPPORTED;
|
|
#else // } {
|
|
HRESULT errb;
|
|
FSH fsh;
|
|
FID fid = FidOpenFm(fm, wReadOnly, &errb);
|
|
|
|
|
|
if (fid == fidNil)
|
|
return errb;
|
|
|
|
if (LcbReadFid (fid, &fsh, (LONG)sizeof(FSH), &errb) != (LONG)sizeof(FSH))
|
|
{
|
|
RcCloseFid(fid);
|
|
return E_FILEINVALID;
|
|
}
|
|
|
|
if (fsh.wMagic != wFileSysMagic)
|
|
{
|
|
RcCloseFid(fid);
|
|
return E_FILEINVALID;
|
|
}
|
|
|
|
/* REVIEW: unlink all tmp files for open files? assert count == 0? */
|
|
|
|
RcCloseFid(fid); /* I'm not checking this return code out of boredom */
|
|
|
|
return (RcUnlinkFm( fm) );
|
|
#endif //}
|
|
}
|
|
|
|
#define MAGIC_FSFILEFIND 0x22334455
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HRESULT PASCAL FAR | RcFindFirstFile |
|
|
* Finds the first file equal to or after the given filename. This function
|
|
* will always find a filename unless the given search filename is past all
|
|
* files in the file system.
|
|
*
|
|
* @parm LPCSTR | szFileName |
|
|
* Name of file to look for. OK to use partial filenames, since the
|
|
* first file equal to or after the given filename in the directory
|
|
* will be found.
|
|
*
|
|
* @parm LPFSFILEFIND |lpfsfilefind|
|
|
* This structure will be filled with information like file length,
|
|
* permission attributes, and file name. It will also be passed to
|
|
* RcFindNextFile to get the next filename.
|
|
*
|
|
* @rdesc Returns S_OK if successful.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API RcFindFirstFile(HFS hfs, LPCSTR szFilename, FSFILEFIND * pfind)
|
|
{
|
|
QFSHR qfshr;
|
|
HRESULT rc = S_OK;
|
|
KEY key;
|
|
BTPOS btpos;
|
|
FILE_REC fr;
|
|
|
|
if ((szFilename==NULL) || (pfind==NULL) || (hfs == NULL) || ((qfshr = _GLOBALLOCK(hfs)) == NULL))
|
|
return E_INVALIDARG;
|
|
|
|
key = NewKeyFromSz(szFilename);
|
|
|
|
_ENTERCRITICALSECTION(&qfshr->cs);
|
|
|
|
RcLookupByKeyAux(qfshr->hbt, key, &btpos, &fr, FALSE);
|
|
if (btpos.bk == bkNil)
|
|
{ rc=E_NOTEXIST;
|
|
goto exit1;
|
|
}
|
|
|
|
GetFrData(&fr);
|
|
pfind->btpos=btpos;
|
|
pfind->foSize=fr.foSize;
|
|
pfind->foStart=fr.foStart;
|
|
pfind->bFlags=fr.bFlags;
|
|
pfind->hfs=hfs;
|
|
pfind->magic=MAGIC_FSFILEFIND;
|
|
|
|
RcLookupByPos( qfshr->hbt, &btpos, (KEY)pfind->szFilename, 256, &fr );
|
|
//GetFrData(&fr);
|
|
|
|
{ DWORD dwLen;
|
|
int iOffset;
|
|
LPSTR szCursor;
|
|
|
|
dwLen=FoFromSz(pfind->szFilename).dwOffset;
|
|
iOffset=LenSzFo(pfind->szFilename);
|
|
szCursor=pfind->szFilename;
|
|
while (dwLen--)
|
|
{ *szCursor=*(szCursor+iOffset);
|
|
szCursor++;
|
|
}
|
|
*szCursor=0x00;
|
|
}
|
|
exit1:
|
|
_LEAVECRITICALSECTION(&qfshr->cs);
|
|
DisposeMemory((LPSTR)key);
|
|
_GLOBALUNLOCK(hfs);
|
|
return rc;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func HRESULT PASCAL FAR | RcFindNextFile |
|
|
* Finds the next file in the file system directory.
|
|
*
|
|
* @parm LPFSFILEFIND |lpfsfilefind|
|
|
* This structure will be filled with information like file length,
|
|
* permission attributes, and file name. It must have already been filled
|
|
* by RcFindFirstFile before calling this function.
|
|
*
|
|
* @rdesc Returns S_OK if successful
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API RcFindNextFile(FSFILEFIND * pfind)
|
|
{
|
|
QFSHR qfshr;
|
|
HRESULT rc = S_OK;
|
|
BTPOS btNewPos;
|
|
FILE_REC fr;
|
|
|
|
if ((!pfind) || (pfind->magic!=MAGIC_FSFILEFIND))
|
|
return E_INVALIDARG;
|
|
|
|
if ((pfind->hfs == NULL) || ((qfshr = _GLOBALLOCK(pfind->hfs)) == NULL))
|
|
return E_INVALIDARG;
|
|
|
|
_ENTERCRITICALSECTION(&qfshr->cs);
|
|
|
|
if (RcNextPos( qfshr->hbt, &pfind->btpos, &btNewPos )==E_NOTEXIST)
|
|
{
|
|
rc=E_NOTEXIST;
|
|
goto exit1;
|
|
}
|
|
|
|
pfind->btpos=btNewPos;
|
|
|
|
RcLookupByPos( qfshr->hbt, &pfind->btpos, (KEY)pfind->szFilename, 256, &fr );
|
|
GetFrData(&fr);
|
|
|
|
pfind->foSize=fr.foSize;
|
|
pfind->bFlags=fr.bFlags;
|
|
|
|
{ LPSTR szCursor;
|
|
DWORD dwLen;
|
|
int iOffset;
|
|
|
|
dwLen=FoFromSz(pfind->szFilename).dwOffset;
|
|
iOffset=LenSzFo(pfind->szFilename);
|
|
szCursor=pfind->szFilename;
|
|
while (dwLen--)
|
|
{ *szCursor=*(szCursor+iOffset);
|
|
szCursor++;
|
|
}
|
|
*szCursor=0x00;
|
|
}
|
|
|
|
exit1:
|
|
_LEAVECRITICALSECTION(&qfshr->cs);
|
|
_GLOBALUNLOCK(pfind->hfs);
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc PRIVATE
|
|
*
|
|
* @func HFREELIST PASCAL FAR | FreeListInitFromFid |
|
|
* Initialize a freelist structure from a fid image. Fid image is
|
|
* always in Intel byte ordering format.
|
|
*
|
|
* @parm FID | fid |
|
|
* File id
|
|
*
|
|
* @parm PHRESULT | lpErrb |
|
|
* Error code return - S_OK or E_OUTOFMEMORY
|
|
*
|
|
* @rdesc Returns handle to a FREELIST, otherwise NULL if error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HFREELIST PASCAL FAR EXPORT_API FreeListInitFromFid( FID fid, PHRESULT lpErrb )
|
|
{
|
|
FREELISTHDR freehdr;
|
|
|
|
QFREELIST qFreeList;
|
|
HFREELIST hFreeList = NULL;
|
|
WORD wMaxBlocks;
|
|
WORD wNumBlocks;
|
|
DWORD dwLostBytes;
|
|
|
|
assert(fid!=fidNil);
|
|
|
|
if (LcbReadFid(fid, &freehdr, sizeof(FREELISTHDR), lpErrb)!=sizeof(FREELISTHDR))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
wMaxBlocks = freehdr.wMaxBlocks;
|
|
wNumBlocks = freehdr.wNumBlocks;
|
|
dwLostBytes = freehdr.dwLostBytes;
|
|
|
|
//wMaxBlocks = qFreeListMem->flh.wMaxBlocks;
|
|
//wNumBlocks = qFreeListMem->flh.wNumBlocks;
|
|
|
|
// Mac-ify
|
|
wMaxBlocks = SWAPWORD(wMaxBlocks);
|
|
wNumBlocks = SWAPWORD(wNumBlocks);
|
|
dwLostBytes = SWAPLONG(dwLostBytes);
|
|
|
|
assert( wMaxBlocks );
|
|
|
|
if (!(hFreeList=_GLOBALALLOC(GMEM_ZEROINIT| GMEM_MOVEABLE,
|
|
sizeof(FREEITEM)*wMaxBlocks+sizeof(FREELISTHDR))))
|
|
{
|
|
SetErrCode(lpErrb,E_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(qFreeList=_GLOBALLOCK(hFreeList)))
|
|
{
|
|
SetErrCode(lpErrb,E_OUTOFMEMORY);
|
|
goto exit1;
|
|
}
|
|
|
|
qFreeList->flh.wMaxBlocks=wMaxBlocks;
|
|
qFreeList->flh.wNumBlocks=wNumBlocks;
|
|
qFreeList->flh.dwLostBytes=dwLostBytes;
|
|
|
|
LcbReadFid( fid, qFreeList->afreeitem, sizeof(FREEITEM) * wMaxBlocks, lpErrb);
|
|
|
|
#ifdef _BIG_E
|
|
{
|
|
QFREEITEM qCurrent = qFreeList->afreeitem;
|
|
WORD wBlock;
|
|
|
|
for (wBlock=0;wBlock<wNumBlocks;wBlock++)
|
|
{
|
|
qCurrent->foStart.dwOffset = SWAPLONG(qCurrent->foStart.dwOffset);
|
|
qCurrent->foStart.dwHigh = SWAPLONG(qCurrent->foStart.dwHigh);
|
|
qCurrent->foBlock.dwOffset = SWAPLONG(qCurrent->foBlock.dwOffset);
|
|
qCurrent->foBlock.dwHigh = SWAPLONG(qCurrent->foBlock.dwHigh);
|
|
}
|
|
}
|
|
#endif // _BIG_E
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
return hFreeList;
|
|
|
|
exit1:
|
|
_GLOBALFREE(hFreeList);
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc PRIVATE
|
|
*
|
|
* @func HRESULT PASCAL FAR | FreeListWriteToFid |
|
|
* Write to fid with freelist data. Call FreeListSize first
|
|
* to make sure the space is large enough. Memory image will always
|
|
* be in Intel byte ordering format.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* List to retrieve
|
|
*
|
|
* @parm FID | fid |
|
|
* FID to contain free list data
|
|
*
|
|
* @parm PHRESULT | lpErrb |
|
|
* Error return
|
|
*
|
|
* @rdesc Number of bytes written
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC LONG PASCAL FAR EXPORT_API FreeListWriteToFid( HFREELIST hFreeList, FID fid, PHRESULT lpErrb )
|
|
{
|
|
QFREELIST qFreeList;
|
|
WORD wMaxBlocks;
|
|
WORD wNumBlocks;
|
|
DWORD dwLostBytes;
|
|
LONG lcbSize;
|
|
LONG lcbWritten=0L;
|
|
|
|
assert(fid!=fidNil);
|
|
assert(hFreeList);
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{ SetErrCode(lpErrb, E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
wMaxBlocks=qFreeList->flh.wMaxBlocks;
|
|
wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
dwLostBytes=qFreeList->flh.dwLostBytes;
|
|
|
|
lcbSize = sizeof(FREELISTHDR) + wMaxBlocks * sizeof(FREEITEM);
|
|
|
|
#ifdef _BIG_E
|
|
{
|
|
QFREEITEM qCurrent;
|
|
WORD wBlock;
|
|
HANDLE hMem;
|
|
QFREELIST qFreeListMem;
|
|
|
|
if (!(hMem = _GLOBALALLOC(GMEM_MOVEABLE, lcbSize)))
|
|
{
|
|
SetErrCode(lpErrb, E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
qFreeListMem = (QFREELIST)_GLOBALLOCK(hMem);
|
|
qCurrent = qFreeListMem->afreeitem;
|
|
|
|
qFreeListMem->flh.wNumBlocks = SWAPWORD( qFreeList->flh.wNumBlocks );
|
|
qFreeListMem->flh.wMaxBlocks = SWAPWORD( qFreeList->flh.wMaxBlocks );
|
|
qFreeListMem->flh.dwLostBytes = SWAPLONG( qFreeList->flh.dwLostBytes );
|
|
|
|
|
|
for (wBlock=0;wBlock<wNumBlocks;wBlock++)
|
|
{
|
|
qCurrent->foStart.dwOffset = SWAPLONG(qCurrent->foStart.dwOffset);
|
|
qCurrent->foStart.dwHigh = SWAPLONG(qCurrent->foStart.dwHigh);
|
|
qCurrent->foBlock.dwOffset = SWAPLONG(qCurrent->foBlock.dwOffset);
|
|
qCurrent->foBlock.dwHigh = SWAPLONG(qCurrent->foBlock.dwHigh);
|
|
}
|
|
|
|
lcbWritten=LcbWriteFid(fid, qFreeListMem, lcbSize, lpErrb);
|
|
|
|
_GLOBALUNLOCK(hMem);
|
|
_GLOBALFREE(hMem);
|
|
}
|
|
#else
|
|
lcbWritten=LcbWriteFid(fid, qFreeList, lcbSize, lpErrb);
|
|
#endif // _BIG_E
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return lcbWritten;
|
|
|
|
}
|
|
|