mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
581 lines
15 KiB
581 lines
15 KiB
/*****************************************************************************
|
|
* *
|
|
* FSOPEN.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1990. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* File System Manager functions to open and close a file or file system. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Testing Notes *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Current Owner: JohnSc *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Revision History: Created 03/12/90 by JohnSc
|
|
*
|
|
* 08/10/90 t-AlexC Introduced FMs
|
|
* 10/29/90 RobertBu Added RcFlushHf() and RcCloseHf() as real functions
|
|
* so that they could be exported to DLLs.
|
|
* 12/11/90 JohnSc Removed FPlungeQfshr() in RcCloseOrFlushHfs() to
|
|
* avoid unnecessary open of readonly FS on close;
|
|
* removed tabs; autodocified comments
|
|
* 08-Feb-1991 JohnSc Bug 848: FM shit can fail
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "help.h"
|
|
#include "inc\fspriv.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: HfsOpenFm( fm, bFlags )
|
|
-
|
|
* Purpose: Open a file system
|
|
*
|
|
* ASSUMES
|
|
* args IN: fm - descriptor of file system to open
|
|
* bFlags - fFSOpenReadOnly or fFSOpenReadWrite
|
|
*
|
|
* PROMISES
|
|
* returns: handle to file system if opened OK, else NULL
|
|
*
|
|
* Bugs: don't have mode now (a file system is opened r/w)
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HFS STDCALL HfsOpenFm(FM fm, BYTE bFlags)
|
|
{
|
|
HFS hfs;
|
|
QFSHR qfshr;
|
|
HBT hbt;
|
|
LONG lcb;
|
|
#ifndef _X86_
|
|
LONG lcbFSHDisk;
|
|
#endif
|
|
|
|
// make header
|
|
|
|
if ((hfs = GhAlloc(GPTR, sizeof(FSHR))) == NULL) {
|
|
SetFSErrorRc(rcOutOfMemory);
|
|
return NULL;
|
|
}
|
|
qfshr = PtrFromGh(hfs);
|
|
|
|
qfshr->fm = FmCopyFm(fm);
|
|
qfshr->fid = HFILE_ERROR;
|
|
|
|
if (!qfshr->fm) {
|
|
SetFSErrorRc(RcGetIOError());
|
|
goto error_return;
|
|
}
|
|
|
|
qfshr->fsh.bFlags = (BYTE) ((bFlags & fFSOpenReadOnly)
|
|
? fFSOpenReadOnly : fFSOpenReadWrite);
|
|
|
|
if (!FPlungeQfshr(qfshr))
|
|
#ifdef _X86_
|
|
goto error_return;
|
|
#else
|
|
goto error_return_nosdff;
|
|
#endif
|
|
|
|
#ifdef _X86_
|
|
lcb = LcbReadFid(qfshr->fid, &qfshr->fsh, (LONG) sizeof(FSH));
|
|
#else
|
|
/* Phase order prob - have not read header, have not registered file: */
|
|
//lcbDisk = (LONG)LcbStructSizeSDFF( qfshr->fsh.sdff_file_id, SE_FH );
|
|
lcbFSHDisk = DISK_SIZEOF_FSH();
|
|
lcb = LcbReadFid( qfshr->fid, &qfshr->fsh, lcbFSHDisk );
|
|
|
|
qfshr->fsh.sdff_file_id = IRegisterFileSDFF(
|
|
qfshr->fsh.bFlags & fFSBigEndian ?
|
|
SDFF_FILEFLAGS_BIGENDIAN : SDFF_FILEFLAGS_LITTLEENDIAN, NULL );
|
|
|
|
/* Now map that structure: */
|
|
if ( (LONG)SDFF_ERROR == LcbMapSDFF( qfshr->fsh.sdff_file_id,
|
|
SE_FSH, qfshr, qfshr ) ) {
|
|
/* REVIEW: this has no effect in retail builds */
|
|
AssertF( FALSE);
|
|
}
|
|
#endif
|
|
|
|
// restore the fFSOpenReadOnly bit
|
|
|
|
if (bFlags & fFSOpenReadOnly)
|
|
qfshr->fsh.bFlags |= fFSOpenReadOnly;
|
|
|
|
#ifdef _X86_
|
|
if (lcb != (LONG) sizeof(FSH)
|
|
||
|
|
qfshr->fsh.wMagic != wFileSysMagic
|
|
||
|
|
qfshr->fsh.lifDirectory < sizeof(FSH)
|
|
||
|
|
qfshr->fsh.lifDirectory > qfshr->fsh.lifEof
|
|
||
|
|
(qfshr->fsh.lifFirstFree < sizeof(FSH)
|
|
&&
|
|
qfshr->fsh.lifFirstFree != lifNil)
|
|
||
|
|
qfshr->fsh.lifFirstFree > qfshr->fsh.lifEof) {
|
|
#else
|
|
if ( lcb != lcbFSHDisk
|
|
||
|
|
qfshr->fsh.wMagic != wFileSysMagic
|
|
||
|
|
qfshr->fsh.lifDirectory < lcbFSHDisk
|
|
||
|
|
qfshr->fsh.lifDirectory > qfshr->fsh.lifEof
|
|
||
|
|
( qfshr->fsh.lifFirstFree < lcbFSHDisk
|
|
&&
|
|
qfshr->fsh.lifFirstFree != lifNil )
|
|
||
|
|
qfshr->fsh.lifFirstFree > qfshr->fsh.lifEof ) {
|
|
#endif
|
|
|
|
if (qfshr->fsh.wMagic == ADVISOR_FS) {
|
|
SetFSErrorRc(rcAdvisorFile);
|
|
goto error_return;
|
|
}
|
|
|
|
if (RcGetIOError() == rcSuccess)
|
|
SetFSErrorRc(rcInvalid);
|
|
else
|
|
SetFSErrorRc(RcGetIOError());
|
|
goto error_return;
|
|
}
|
|
|
|
if (qfshr->fsh.bVersion != FILESYSVERSION) {
|
|
SetFSErrorRc(rcBadVersion);
|
|
goto error_return;
|
|
}
|
|
|
|
// open btree directory
|
|
|
|
hbt = HbtOpenBtreeSz( NULL,
|
|
hfs,
|
|
(BYTE)( qfshr->fsh.bFlags | fFSIsDirectory ) );
|
|
if (hbt == NULL) {
|
|
SetFSErrorRc(RcGetBtreeError());
|
|
goto error_return;
|
|
}
|
|
qfshr->hbt = hbt;
|
|
|
|
SetFSErrorRc(rcSuccess);
|
|
return hfs;
|
|
|
|
error_return:
|
|
#ifndef _X86_
|
|
IDiscardFileSDFF( qfshr->fsh.sdff_file_id);
|
|
error_return_nosdff:
|
|
#endif
|
|
if (qfshr->fid != HFILE_ERROR) {
|
|
RcCloseFid(qfshr->fid);
|
|
qfshr->fid = HFILE_ERROR;
|
|
}
|
|
RemoveFM(&qfshr->fm);
|
|
|
|
FreeGh(hfs);
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcCloseOrFlushHfs( hfs, fClose )
|
|
-
|
|
* Purpose: Close or sync the header and directory of an open file system.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hfs - handle to an open file system
|
|
* fClose - TRUE to close the file system;
|
|
* FALSE to write through
|
|
* PROMISES
|
|
* returns: standard return code
|
|
* globals OUT:rcFSError
|
|
*
|
|
* Note: If closing the FS, all FS files must have been closed or
|
|
* changes made will be lost.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcCloseOrFlushHfs(HFS hfs, BOOL fClose)
|
|
{
|
|
QFSHR qfshr;
|
|
BOOL fIsDir;
|
|
|
|
ASSERT(hfs != NULL);
|
|
qfshr = PtrFromGh(hfs);
|
|
|
|
/*
|
|
We don't call FPlungeQfshr() here because if we need to open the
|
|
file, it will be opened in the btree call.
|
|
In fixing a bug (help3.5 164) I added this FPlungeQfshr() call,
|
|
but I now think the bug was due to inherent FS limitations in
|
|
dealing with a FS open multiple times.
|
|
*/
|
|
|
|
if (SetFSErrorRc(RcCloseOrFlushHbt(qfshr->hbt, fClose)) != rcSuccess) {
|
|
ASSERT(qfshr->fid != HFILE_ERROR); // see comment above
|
|
|
|
// out of disk space, internal error, or out of file handles.
|
|
|
|
if (rcNoFileHandles != RcGetBtreeError() && !fClose) {
|
|
#ifndef _X86_
|
|
QV qvQuickBuff = QvQuickBuffSDFF( sizeof( FSH) );
|
|
#endif
|
|
|
|
// attempt to invalidate FS by clobbering magic number
|
|
|
|
LSeekFid(qfshr->fid, 0L, SEEK_SET);
|
|
qfshr->fsh.wMagic = 0;
|
|
#ifdef _X86_
|
|
LcbWriteFid(qfshr->fid, &qfshr->fsh, (LONG) sizeof(FSH));
|
|
#else
|
|
LcbReverseMapSDFF( qfshr->fsh.sdff_file_id, SE_FSH, qvQuickBuff,
|
|
&qfshr->fsh );
|
|
LcbWriteFid(qfshr->fid, qvQuickBuff,
|
|
LcbStructSizeSDFF( qfshr->fsh.sdff_file_id, SE_FSH) );
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
if (qfshr->fsh.bFlags & fFSDirty) {
|
|
ASSERT(qfshr->fid != HFILE_ERROR); // see comment above
|
|
|
|
ASSERT(!(qfshr->fsh.bFlags & (fFSOpenReadOnly | fFSReadOnly)));
|
|
|
|
// save the directory flag, clear before writing, and restore
|
|
|
|
fIsDir = qfshr->fsh.bFlags & fFSIsDirectory;
|
|
|
|
qfshr->fsh.bFlags &= ~(fFSDirty | fFSIsDirectory);
|
|
|
|
// write out file system header
|
|
|
|
if (LSeekFid(qfshr->fid, 0L, SEEK_SET) != 0L) {
|
|
if (RcGetIOError() == rcSuccess)
|
|
SetFSErrorRc(rcInvalid);
|
|
else
|
|
SetFSErrorRc(RcGetIOError());
|
|
}
|
|
#ifdef _X86_
|
|
else if (LcbWriteFid(qfshr->fid, &qfshr->fsh, (LONG) sizeof(FSH))
|
|
!=
|
|
(LONG) sizeof(FSH)) {
|
|
if (RcGetIOError() == rcSuccess)
|
|
SetFSErrorRc(rcInvalid);
|
|
else
|
|
SetFSErrorRc(RcGetIOError());
|
|
}
|
|
#else
|
|
else {
|
|
LONG lcbFSHDisk = LcbStructSizeSDFF( qfshr->fsh.sdff_file_id, SE_FSH);
|
|
QV qvQuickBuff = QvQuickBuffSDFF( (int)lcbFSHDisk );
|
|
LcbReverseMapSDFF( qfshr->fsh.sdff_file_id, SE_FSH, qvQuickBuff,
|
|
&qfshr->fsh );
|
|
if ( LcbWriteFid( qfshr->fid, qvQuickBuff, lcbFSHDisk )
|
|
!=
|
|
lcbFSHDisk ) {
|
|
if (RcGetIOError() == rcSuccess)
|
|
SetFSErrorRc(rcInvalid);
|
|
else
|
|
SetFSErrorRc(RcGetIOError());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
qfshr->fsh.bFlags |= fIsDir;
|
|
|
|
/* REVIEW: should we keep track of open files and make sure */
|
|
/* REVIEW: they are all closed, or close them here? */
|
|
}
|
|
}
|
|
|
|
if (fClose) {
|
|
if (qfshr->fid != HFILE_ERROR) {
|
|
RcCloseFid(qfshr->fid);
|
|
if (rcFSError == rcSuccess)
|
|
rcFSError = RcGetIOError();
|
|
}
|
|
|
|
DisposeFm(qfshr->fm); // Should we really do this? Guess so.
|
|
#ifndef _X86_
|
|
IDiscardFileSDFF( qfshr->fsh.sdff_file_id );
|
|
#endif
|
|
FreeGh(hfs);
|
|
}
|
|
|
|
return rcFSError;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RC RcFlushHfs( hfs, bFlags )
|
|
-
|
|
* Purpose: Ssync up the FS header and directory. Also optionally
|
|
* close the DOS file handle associated with the file system
|
|
* and/or free the directory btree cache.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hfs
|
|
* bFlags - byte of flags for various actions to take
|
|
* fFSCloseFile - close the native file FS lives in
|
|
* fFSFreeBtreeCache - free the btree cache
|
|
* PROMISES
|
|
* returns: rc
|
|
* args OUT: hfs cache is flushed and/or file is closed
|
|
*
|
|
* Note: This is NOT sufficient to allow the same FS to be opened
|
|
* twice if anyone is writing. In-memory data can get out
|
|
* of sync with the disk image, causing problems.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcFlushHfs(HFS hfs, BYTE bFlags)
|
|
{
|
|
QFSHR qfshr;
|
|
RC rcT;
|
|
|
|
ASSERT(hfs != NULL);
|
|
qfshr = PtrFromGh(hfs);
|
|
|
|
rcT = RcCloseOrFlushHfs(hfs, FALSE);
|
|
|
|
if (bFlags & fFSFreeBtreeCache)
|
|
SetFSErrorRc(RcFreeCacheHbt(qfshr->hbt));
|
|
else
|
|
SetFSErrorRc(rcSuccess);
|
|
|
|
if (bFlags & fFSCloseFile) {
|
|
if (qfshr->fid != HFILE_ERROR) {
|
|
SetFSErrorRc(RcCloseFid(qfshr->fid));
|
|
qfshr->fid = HFILE_ERROR;
|
|
}
|
|
}
|
|
|
|
return rcFSError == rcSuccess ? rcT : rcFSError;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: HfOpenHfs( hfs, sz, bFlags )
|
|
-
|
|
* Purpose: open a file in a file system
|
|
*
|
|
* ASSUMES
|
|
* args IN: hfs - handle to file system
|
|
* sz - name (key) of file to open
|
|
* bFlags - fFSOpenReadOnly, fFSIsDirectory, or combination
|
|
*
|
|
* PROMISES
|
|
* returns: handle to open file or NULL on failure
|
|
* +++
|
|
*
|
|
* Notes: strlen( NULL ) and strcpy( s, NULL ) don't work as I'd like.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HF STDCALL HfOpenHfs(HFS hfs, LPCSTR sz, BYTE bFlags)
|
|
{
|
|
QFSHR qfshr;
|
|
FILE_REC fr;
|
|
HF hf;
|
|
QRWFO qrwfo;
|
|
FH fh;
|
|
#ifndef _X86_
|
|
LONG lcbStructSize;
|
|
QV qvQuickBuff;
|
|
#endif
|
|
|
|
ASSERT(hfs != NULL);
|
|
qfshr = PtrFromGh(hfs);
|
|
|
|
if ((qfshr->fsh.bFlags & fFSOpenReadOnly) &&
|
|
!(bFlags & fFSOpenReadOnly)) {
|
|
SetFSErrorRc(rcNoPermission);
|
|
return NULL;
|
|
}
|
|
|
|
if (!FPlungeQfshr(qfshr))
|
|
return NULL;
|
|
|
|
if (bFlags & fFSIsDirectory) {
|
|
|
|
// check if directory is already open
|
|
|
|
if (qfshr->fsh.bFlags & fFSIsDirectory) {
|
|
SetFSErrorRc(rcBadArg);
|
|
return NULL;
|
|
}
|
|
qfshr->fsh.bFlags |= fFSIsDirectory;
|
|
fr.lifBase = qfshr->fsh.lifDirectory;
|
|
}
|
|
#ifdef _X86_
|
|
else if (SetFSErrorRc(RcLookupByKey(qfshr->hbt, (KEY) sz, NULL, &fr))
|
|
!= rcSuccess)
|
|
return NULL;
|
|
#else
|
|
else { if (SetFSErrorRc(RcLookupByKey(qfshr->hbt, (KEY) sz, NULL, &fr))
|
|
!= rcSuccess) {
|
|
return NULL;
|
|
}
|
|
LcbMapSDFF( qfshr->fsh.sdff_file_id, SE_FILE_REC, &fr, &fr);
|
|
}
|
|
#endif
|
|
|
|
// sanity check
|
|
|
|
#ifdef _X86_
|
|
if (fr.lifBase < sizeof(FSH) || fr.lifBase > qfshr->fsh.lifEof) {
|
|
SetFSErrorRc(rcInvalid);
|
|
return NULL;
|
|
}
|
|
#else
|
|
if (fr.lifBase < LcbStructSizeSDFF( qfshr->fsh.sdff_file_id, SE_FSH)
|
|
|| fr.lifBase > qfshr->fsh.lifEof) {
|
|
SetFSErrorRc(rcInvalid);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
// read the file header
|
|
#ifndef _X86_
|
|
lcbStructSize = LcbStructSizeSDFF( qfshr->fsh.sdff_file_id, SE_FH);
|
|
qvQuickBuff= QvQuickBuffSDFF( sizeof(FH));
|
|
#endif
|
|
|
|
if (LSeekFid(qfshr->fid, fr.lifBase, SEEK_SET) != fr.lifBase
|
|
||
|
|
#ifdef _X86_
|
|
LcbReadFid(qfshr->fid, &fh, sizeof(FH)) != sizeof(FH)) {
|
|
#else
|
|
LcbReadFid(qfshr->fid, qvQuickBuff, lcbStructSize) != lcbStructSize) {
|
|
#endif
|
|
if (RcGetIOError() == rcSuccess)
|
|
SetFSErrorRc(rcInvalid);
|
|
else
|
|
SetFSErrorRc(RcGetIOError());
|
|
|
|
return NULL;
|
|
}
|
|
#ifndef _X86_
|
|
// Map the file Header:
|
|
LcbMapSDFF( qfshr->fsh.sdff_file_id, SE_FH, &fh, qvQuickBuff);
|
|
#endif
|
|
|
|
// sanity check
|
|
|
|
if (fh.lcbFile < 0L
|
|
||
|
|
#ifdef _X86_
|
|
fh.lcbFile + (LONG) sizeof(FH) > fh.lcbBlock
|
|
#else
|
|
fh.lcbFile + lcbStructSize > fh.lcbBlock
|
|
#endif
|
|
||
|
|
fr.lifBase + fh.lcbBlock > qfshr->fsh.lifEof) {
|
|
SetFSErrorRc(rcInvalid);
|
|
return NULL;
|
|
}
|
|
|
|
// check mode against fh.bPerms for legality
|
|
|
|
if ((fh.bPerms & fFSReadOnly) && !(bFlags & fFSOpenReadOnly)) {
|
|
SetFSErrorRc(rcNoPermission);
|
|
return NULL;
|
|
}
|
|
|
|
// build file struct
|
|
|
|
hf = GhAlloc(GPTR,
|
|
(LONG) sizeof(RWFO) + (sz == NULL ? 0 : lstrlen(sz)));
|
|
|
|
if (hf == NULL) {
|
|
SetFSErrorRc( rcOutOfMemory );
|
|
return NULL;
|
|
}
|
|
|
|
qrwfo = PtrFromGh(hf);
|
|
|
|
qrwfo->hfs = hfs;
|
|
qrwfo->lifBase = fr.lifBase;
|
|
qrwfo->lifCurrent = 0L;
|
|
qrwfo->lcbFile = fh.lcbFile;
|
|
qrwfo->bFlags = bFlags & (BYTE) ~(fFSDirty | fFSNoBlock);
|
|
|
|
// fidT and fmT are undefined until (bFlags & fDirty)
|
|
|
|
if (sz != NULL)
|
|
lstrcpy(qrwfo->rgchKey, sz);
|
|
|
|
SetFSErrorRc(rcSuccess);
|
|
return hf;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcCloseOrFlushHf( hf, fClose, lcbOffset )
|
|
-
|
|
* Purpose: close or flush an open file in a file system
|
|
*
|
|
* ASSUMES
|
|
* args IN: hf - file handle
|
|
* fClose - TRUE to close; FALSE to just flush
|
|
* lcbOffset - offset for CDROM alignment (align at this
|
|
* offset into the file) (only used if
|
|
* fFSOptCdRom flag is set for the file)
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess on successful closing
|
|
* failure: If we fail on a flush, the handle is still valid
|
|
* but hosed? yes. This is so further file ops will fail but
|
|
* not assert.
|
|
* +++
|
|
*
|
|
* Method: If the file is dirty, copy the scratch file back to the
|
|
* FS file. If this is the first time the file has been closed,
|
|
* we enter the name into the FS directory. If this file is
|
|
* the FS directory, store the location in a special place
|
|
* instead. Write the FS directory and header to disk.
|
|
* Do other various hairy stuff.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcCloseOrFlushHf(HF hf, BOOL fClose, LONG lcbOffset)
|
|
{
|
|
QRWFO qrwfo;
|
|
BOOL fError = FALSE;
|
|
|
|
ASSERT(hf);
|
|
qrwfo = PtrFromGh(hf);
|
|
|
|
if (qrwfo->bFlags & fFSDirty)
|
|
fError = !FCloseOrFlushDirtyQrwfo(qrwfo, fClose, lcbOffset);
|
|
else
|
|
SetFSErrorRc(rcSuccess);
|
|
|
|
if (fClose || fError) {
|
|
FreeGh(hf);
|
|
}
|
|
else {
|
|
qrwfo->bFlags &= ~(fFSNoBlock | fFSDirty);
|
|
}
|
|
|
|
return rcFSError;
|
|
}
|
|
/* EOF */
|