|
|
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
Fcb.h
Abstract:
This module defines File Control Block data structures, by which we mean:
1) File Control Blocks (FCB) 2) File Object Extensions (FOXB) 3) Net Roots (NET_ROOT) 4) ServerSide Open Context (SRV_OPEN) 5) Server Call Context (SRV_CALL) 6) View of Net Roots (V_NET_ROOT)
The more complete description follows the prototypes.
Author:
Joe Linn [JoeLinn] 19-aug-1994
Revision History:
Balan Sethu Raman [SethuR] 1-Aug-96
--*/
#ifndef _FCB_STRUCTS_DEFINED_
#define _FCB_STRUCTS_DEFINED_
#include "fcbtable.h"
#include "buffring.h"
typedef NODE_TYPE_CODE TYPE_OF_OPEN;
struct _FCB_INIT_PACKET; typedef struct _FCB_INIT_PACKET *PFCB_INIT_PACKET;
/* -----------------------------------------------------------
There are six important data structures in the wrapper that are shared with the various mini redirectors. These data structures come in two flavours -- the mini redirector flavour which contains only those fields that can be manipulated by the mini redirector and the RDBSS flavour defined here. The mini redirector flavour carries the prefix MRX_.
The six data structures are SRV_CALL,NET_ROOT,V_NET_ROOT,FCB,SRV_OPEN and FOBX respectively.
The global view of these structures is the following (information on each of the data structures follows the locking description )
L O C K I N G <-------
There are two levels of lookup tables used: a global table for srvcalls and netroots and a table-per-netroot for fcbs. This allows directory operations on different netroots to be almost completely noninterfering (once the connections are established). Directory operations on the same netroot do intefere slightly. The following table describes what locks you need:
OPERATION DATATYPE LOCK REQUIRED
create/finalize srvcall/(v)netroot exclusive on netnametablelock ref/deref/lookup srvcall/(v)netroot shared on netnametablelock (at least)
create/finalize fcb/srvopen/fobx exclusive on netroot->fcbtablelock ref/deref/lookup fcb/srvopen/fobx shared on netroot->fcbtablelock
Note that manipulations on srvopens and fobxs require the same lock as fcbs....this is simply a memory saving idea. It would be straightforward to add another resource at the fcb level to remove this; a set of sharted resources could be used to decrease the probability of collision to an acceptably low level.
R E F C O U N T S <---------------
Each of the structures is reference counted. The counts are the following:
refcount(srvcall) = number of netroots pointing to srvcall + DYNAMIC refcount(netroot) = number of fcbs pointing to netroot + DYNAMIC refcount(fcb) = number of fcbs pointing to netroot + DYNAMIC refcount(srvopen) = number of fcbs pointing to netroot + DYNAMIC refcount(fobx) = DYNAMIC
In each case, dynamic refers to the number of callers that have referenced the structure without dereferencing it. The static part of the refcount is maintained by the routines themselves; for example, CreateNetRoot increments the refcount for the associated srvcall. Reference and Successful Lookups increment the reference counts; dereference decrements the count. Creates set the reference counts to 1,
If you require both locks (like FinalizeNetFcb), you take the fcblock first AND THEN the global table lock. obviously, you release in the opposite order.
----------------------------------*/
//
// SRV_CALL
//
// A global list of the SRV_CALL structures is maintained in the global
// data. Each SrvCall structure has stuff that is unique to a srv_call.
// Now, the rx doesn't know what this stuff is except for
//
// 0) signature and refcount
// a) a name and associated table stuff
// b) a list of associated NET_ROOTs
// c) a set of timing parameters that control how often the subrx wants
// to be called by the rx in different circumstances (i.e. idle timouts)
// d) the minirdr id
// .
// .
// z) whatever additional storage is request by the minirdr (or creator of the block).
//
// In fact, the Unicode name of the structure is carried in the structure itself
// at the end. The extra space begins at the end of the known stuff so that a
// mini redirector can just refer to his extra space using the context fields
// These flags are not visible to the mini redirectors.
#define SRVCALL_FLAG_NO_CONNECTION_ALLOWED (0x10000)
#define SRVCALL_FLAG_NO_WRITES_ALLOWED (0x20000)
#define SRVCALL_FLAG_NO_DELETES_ALLOWED (0x40000)
#ifdef __cplusplus
typedef struct _SRV_CALL : public MRX_SRV_CALL { #else // !__cplusplus
typedef struct _SRV_CALL {
//
// The portion of SRV_CALL visible to mini redirectors.
//
union { MRX_SRV_CALL; struct { MRX_NORMAL_NODE_HEADER spacer; }; }; #endif // __cplusplus
//
// The finalization of a SRV_CALL instance consists of two parts,
// destroying the association with all NET_ROOTS etc and freeing the
// memory. There can be a delay between these two and this field
// prevents thefirst step from being duplicated.
//
BOOLEAN UpperFinalizationDone;
//
// Name and Prefixtable entry for name lookups
//
RX_PREFIX_ENTRY PrefixEntry;
//
// Current condition of the SRV_CALL, i.e., good/bad/in transition
//
RX_BLOCK_CONDITION Condition;
ULONG SerialNumberForEnum;
//
// Number of delayed close files
//
LONG NumberOfCloseDelayedFiles;
//
// List of Contexts which are waiting for the SRV_CALL transitioning
// to be completed before resumption of processing. This typically
// happens when concurrent requests are directed at a server. One of
// these requests initiates the construction while the other requests
// are queued.
//
LIST_ENTRY TransitionWaitList;
//
// List Entry to thread together all the SRV_CALL instances marked
// for garbage collection/scavenging.
//
LIST_ENTRY ScavengerFinalizationList;
//
// Synchronization context for coordinating the purge operations on the
// files opened at this server.
//
PURGE_SYNCHRONIZATION_CONTEXT PurgeSyncronizationContext;
//
// The Buffering manager for coordinating/processing the buffering state
// change requests of the files opened at the server.
//
RX_BUFFERING_MANAGER BufferingManager; } SRV_CALL, *PSRV_CALL;
//
// A NET_ROOT contains
// 0) signature and refcount
// a) a name and associated table stuff
// b) backpointer to the SRV_CALL structure
// c) size information for the various substructures
// d) a lookuptable of FCB structures
// .
// .
// z) whatever additional storage is request by the minirdr (or creator of the block).
//
// A NET_ROOT is what the rx wants to deal with.....not a server.
// Accordingly, the rx calls down to open a netroot and the subrx is
// responsible for opening a server and calling up to put the right
// structures.
//
#define NETROOT_FLAG_ENCLOSED_ALLOCATED ( 0x00010000 )
#define NETROOT_FLAG_DEVICE_NETROOT ( 0x00020000 )
#define NETROOT_FLAG_FINALIZATION_IN_PROGRESS ( 0x00040000 )
#define NETROOT_FLAG_NAME_ALREADY_REMOVED ( 0x00080000 )
#define NETROOT_INIT_KEY (0)
#ifdef __cplusplus
typedef struct _NET_ROOT : public MRX_NET_ROOT { #else // !__cplusplus
typedef struct _NET_ROOT {
//
// The porion of NET_ROOT instance visible to mini redirectors.
//
union { MRX_NET_ROOT; struct { MRX_NORMAL_NODE_HEADER spacer; PSRV_CALL SrvCall; }; }; #endif // __cplusplus
//
// The finalization of a NET_ROOT instance consists of two parts,
// destroying the association with all V_NET_ROOTS etc and freeing the
// memory. There can be a delay between these two and this field
// prevents thefirst step from being duplicated.
//
BOOLEAN UpperFinalizationDone;
//
// Current condition of the NET_ROOT, i.e., good/bad/in transition
//
RX_BLOCK_CONDITION Condition;
//
// List of Contexts which are waiting for the NET_ROOT transitioning
// to be completed before resumption of processing. This typically
// happens when concurrent requests are directed at a server. One of
// these requests initiates the construction while the other requests
// are queued.
//
LIST_ENTRY TransitionWaitList;
//
// List Entry to thread together all the NET_ROOT instances marked
// for garbage collection/scavenging.
//
LIST_ENTRY ScavengerFinalizationList;
//
// Synchronization context for coordinating the purge operations on the
// files opened for this NET_ROOt
//
PURGE_SYNCHRONIZATION_CONTEXT PurgeSyncronizationContext;
//
// The default V_NET_ROOT instance to be used on this NET_ROOT
//
PV_NET_ROOT DefaultVNetRoot;
//
// list of V_NET_ROOTs associated with the NET_ROOT
//
LIST_ENTRY VirtualNetRoots;
//
// the count of V_NET_ROOT instances associated with the NET_ROOT
//
ULONG NumberOfVirtualNetRoots;
ULONG SerialNumberForEnum;
//
// NET_ROOT name and prefix table entry
//
RX_PREFIX_ENTRY PrefixEntry;
//
// the FCB's associated with this NET_ROOT
//
RX_FCB_TABLE FcbTable; } NET_ROOT, *PNET_ROOT;
//
// A V_NETROOT contains
// 0) signature and refcount
// a) ptr to netroot and links.
// b) name info for table lookup (prefix)
// c) name for a prefix to be added to whatever name you see. this is for simulating a netroot
// mapped not at the root of the actual netroot.
//
#ifdef __cplusplus
typedef struct _V_NET_ROOT : public MRX_V_NET_ROOT { #else // !__cplusplus
typedef struct _V_NET_ROOT {
//
// the portion of V_NET_ROOT visible to mini redirectors
//
union { MRX_V_NET_ROOT; struct { MRX_NORMAL_NODE_HEADER spacer; PNET_ROOT NetRoot; }; }; #endif // __cplusplus
//
// The finalization of a V_NET_ROOT instance consists of two parts,
// destroying the association with all FCBs etc and freeing the
// memory. There can be a delay between these two and this field
// prevents thefirst step from being duplicated.
//
BOOLEAN UpperFinalizationDone;
BOOLEAN ConnectionFinalizationDone;
//
// Current condition of the V_NET_ROOT, i.e., good/bad/in transition
//
RX_BLOCK_CONDITION Condition;
//
// Additional reference for the Delete FSCTL. This field is long as
// opposed to a BOOLEAN eventhough it can have only one of two values
// 0 or 1. This enables the usage of interlocked instructions
//
LONG AdditionalReferenceForDeleteFsctlTaken;
//
// Prefix table entry and V_NET_ROOT name ( prefix table entry is inserted
// in the RxNetNameTable)
//
RX_PREFIX_ENTRY PrefixEntry;
//
// this name is prepended to all fcbs (not currently used)
//
UNICODE_STRING NamePrefix;
//
// amount of bytes required to get past the netroot
//
ULONG PrefixOffsetInBytes;
//
// List entry to wire the V_NET_ROOT instance into a list of V_NET_ROOTS
// maintained in the NET_ROOT
//
LIST_ENTRY NetRootListEntry;
ULONG SerialNumberForEnum;
//
// List of Contexts which are waiting for the NET_ROOT transitioning
// to be completed before resumption of processing. This typically
// happens when concurrent requests are directed at a server. One of
// these requests initiates the construction while the other requests
// are queued.
//
LIST_ENTRY TransitionWaitList;
//
// List Entry to thread together all the V_NET_ROOT instances marked
// for garbage collection/scavenging.
//
LIST_ENTRY ScavengerFinalizationList; } V_NET_ROOT, *PV_NET_ROOT;
#define FILESIZE_LOCK_DISABLED(x)
//
// An FCB contains
// 0) FSRTL_COMMON_HEADER
// 1) a reference count
// a) a name and associated table stuff
// b) backpointer to the NET_ROOT structure
// c) a list of SRV_OPEN structures
// d) device object
// e) dispatch table (not yet)
// .
// .
// z) whatever additional storage is request by the minirdr (or creator of the block).
//
// The FCB is pointed to by the FsContext Field in the file object. The
// rule is that all the guys sharing an FCB are talking about the same
// file. (unfortuantely, SMB servers are implemented today in such a way
// that names are aliased so that two different names could be the same
// actual file.....sigh!) The Fcb is the focal point of file
// operations...since operations on the same FCB are actually on the same
// file, synchronization is based on the Fcb rather than some higher level
// (the levels described so far are lower, i.e. farther from the user).
// Again, we will provide for colocation of FCB/SRV_OPEN/FOBX to improve
// paging behaviour. We don't colocate the FCB and NET_ROOT because the
// NET_ROOTs are not paged but FCBs usually are (i.e. unless they are
// paging files).
//
// The Fcb record corresponds to every open file and directory and is is split up into
// two portions a non paged part, i.e., an instance allocated in non paged pool and
// a paged part. The former is the NON_PAGED_FCB and the later is referred to as FCB.
// The FCB conatins a pointer to the corresponding NON_PAGED_FCB part. A backpointer
// is maintained from the NON_PAGED_FCB to the FCB for debugging purposes in debug builds
//
typedef struct _NON_PAGED_FCB {
//
// Struct type and size for debugging/tracking
//
NODE_TYPE_CODE NodeTypeCode; NODE_BYTE_SIZE NodeByteSize;
//
// The following field contains a record of special pointers used by
// MM and Cache to manipluate section objects. Note that the values
// are set outside of the file system. However the file system on an
// open/create will set the file object's SectionObject field to point
// to this field
//
SECTION_OBJECT_POINTERS SectionObjectPointers;
//
// This resource is used in the common fsrtl routines....allocated here for
// space locality.
//
ERESOURCE HeaderResource;
//
// This resource is also used in the common fsrtl routines....allocated here for
// space locality.
//
ERESOURCE PagingIoResource;
#ifdef USE_FILESIZE_LOCK
//
// This mutex protect the filesize during read/write
//
FAST_MUTEX FileSizeLock;
#endif
//
// The list of contexts whose processing has been suspended pending the state
// transition of the FCB.
//
LIST_ENTRY TransitionWaitList;
//
// This context is non-zero only if the file currently has asynchronous
// non-cached valid data length extending writes. It allows
// synchronization between pending writes and other operations.
//
ULONG OutstandingAsyncWrites;
//
// This event is set when OutstandingAsyncWrites transitions to zero.
//
PKEVENT OutstandingAsyncEvent;
KEVENT TheActualEvent;
//
// The mechanism for the mini redirectors to store additional information
//
PVOID MiniRdrContext[2];
//
// This is the mutex that is inserted into the FCB_ADVANCED_HEADER
// FastMutex field
//
FAST_MUTEX AdvancedFcbHeaderMutex;
#if DBG
PFCB FcbBackPointer; #endif
} NON_PAGED_FCB, *PNON_PAGED_FCB;
typedef enum _FCB_CONDITION { FcbGood = 1, FcbBad, FcbNeedsToBeVerified } FCB_CONDITION;
//
// A enumerated type distinguishing the varios contexts under which the FCB resource
// is accquired.
//
typedef enum _RX_FCBTRACKER_CASES { RX_FCBTRACKER_CASE_NORMAL, RX_FCBTRACKER_CASE_NULLCONTEXT, RX_FCBTRACKER_CASE_CBS_CONTEXT, RX_FCBTRACKER_CASE_CBS_WAIT_CONTEXT, RX_FCBTRACKER_CASE_MAXIMUM } RX_FCBTRACKER_CASES;
#ifdef __cplusplus
typedef struct _FCB : public MRX_FCB { #else // !__cplusplus
typedef struct _FCB { //
// Entries are reference counted. ordinarily this would be at the beginning but
// in the case of FCB's it will follows the common header and fixed part
//
union { MRX_FCB; struct { FSRTL_ADVANCED_FCB_HEADER spacer; PNET_ROOT NetRoot; }; }; #endif // !__cplusplus
//
// VNetroot for this FCB, if any
//
PV_NET_ROOT VNetRoot;
//
// Structure for fields that must be in non-paged pool.
//
PNON_PAGED_FCB NonPaged;
//
// List Entry to thread together all the FCB instances marked
// for garbage collection/scavenging.
//
LIST_ENTRY ScavengerFinalizationList;
//
// The resource accquisition mechanism gives preference to buffering state change
// processing over other requests. Therefor when a buffering state change is
// indicated all subsequent requests are shunted off to wait on a buffering state
// change completion event. This enables the actual buffering state change processing
// to complete in a timely fashion.
//
PKEVENT pBufferingStateChangeCompletedEvent;
//
// Number of contexts awaiting buffering state change processing completion
//
LONG NumberOfBufferingStateChangeWaiters;
//
// the name in the table is always a suffix of the name as viewed by the mini
// redirector. the string in the prefix entry is the name in the table....
// the "alreadyprefixedname: points to the whole name.
//
RX_FCB_TABLE_ENTRY FcbTableEntry;
//
// the name alongwith the MRX_NET_ROOT prefix, i.e. fully qualified name
//
UNICODE_STRING PrivateAlreadyPrefixedName;
//
// Indicates that the V_NET_ROOT related processing on finalization is complete
//
BOOLEAN UpperFinalizationDone;
//
// the present state of the FCB, good/bad/in transition
//
RX_BLOCK_CONDITION Condition;
//
// Pointer to the private dispatch table, if any.
//
PRX_FSD_DISPATCH_VECTOR PrivateDispatchVector;
//
// the device object that owns this fcb
//
PRDBSS_DEVICE_OBJECT RxDeviceObject;
PMINIRDR_DISPATCH MRxDispatch;
//
// private fast dispatch table, if any. This allows lwio to add it's own hooks
//
PFAST_IO_DISPATCH MRxFastIoDispatch;
//
// Whenever a FCB instance is created a correpsonding SRV_OPEN and FOBX instance
// is also created. More than one SRV_OPEN can be associated with a given FCB and
// more than one FOBX is associated with a given SRV_OPEN. In a majority of the
// cases the number of SRV_OPENs associated with an FCB is one and the number of
// FOBX associated with a given SRV_OPEN is 1. In order to improve the spatial
// locality and the paging behaviour in such cases the allocation for the
// FCB also involves an allocation for the SRV_OPEN and FOBX.
//
//
// set initially to the internally allocated srv_open
//
PSRV_OPEN InternalSrvOpen;
//
// set to internal fobx until allocated
//
PFOBX InternalFobx;
//
// the shared access for each time this file/directory is opened.
//
SHARE_ACCESS ShareAccess; SHARE_ACCESS ShareAccessPerSrvOpens;
//
// this information is returned when the file is opened. ..might as well
// cache it so that so that tandard info query can be handled on the client
// side
//
ULONG NumberOfLinks;
//
// Cache these entries..... speeds up RxFastQueryBasicInfo().
//
LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER LastChangeTime;
//
// this thread is one who has acquired the FCB for CreateSection. it is used
// to deduce whether certain operations (notably queryfileinfo) have preacquired
// the resources and will, therefore, run without blocking.
//
PETHREAD CreateSectionThread;
//
// used to check by mini redirs in order to decide whether to update the FCB
//
ULONG ulFileSizeVersion;
//
// The following union is cased off of the node type code for the fcb.
// There is a seperate case for the directory versus file fcbs.
//
union {
//
// A File Control Block (Fcb)
//
struct { //
// The following field will be used by the oplock module
// to maintain current oplock information. BUT we dont do it
// yet
//
// OPLOCK Oplock;
//
// The following field is used by the filelock module
// to maintain current byte range locking information.
//
FILE_LOCK FileLock;
//
// This pointer is used to detect writes that eminated in the
// cache manager's lazywriter. It prevents lazy writer threads,
// who already have the Fcb shared, from trying to acquire it
// exclusive, and thus causing a deadlock.
//
PVOID LazyWriteThread;
//
// do this wierdly so that I can call stuff be the inner or outer names
//
union { #ifndef __cplusplus
LOWIO_PER_FCB_INFO; #endif // __cplusplus
LOWIO_PER_FCB_INFO LowIoPerFcbInfo; };
#ifdef USE_FILESIZE_LOCK
PFAST_MUTEX FileSizeLock; #endif
} Fcb;
} Specific;
//
// The following field is used to verify that the Ea's for a file
// have not changed between calls to query for Ea's. It is compared
// with a similar field in a Fobx.
//
// IMPORTANT!! **** DO NOT MOVE THIS FIELD ****
//
// The slack space in the union above is computed from
// the field offset of the EaModificationCount.
//
ULONG EaModificationCount;
#if DBG
PNON_PAGED_FCB CopyOfNonPaged; // copy of NonPaged so we can zap the real pointer and still find it
#endif
#ifdef RDBSS_TRACKER
ULONG FcbAcquires[RX_FCBTRACKER_CASE_MAXIMUM]; // there are four types
ULONG FcbReleases[RX_FCBTRACKER_CASE_MAXIMUM]; #else
#error tracker must be defined
#endif
PCHAR PagingIoResourceFile; ULONG PagingIoResourceLine;
} FCB, *PFCB;
//
// Here are the Fcb state fields.
//
#define FCB_STATE_SRVOPEN_USED ( 0x80000000 )
#define FCB_STATE_FOBX_USED ( 0x40000000 )
#define FCB_STATE_ADDEDBACKSLASH ( 0x20000000 )
#define FCB_STATE_NAME_ALREADY_REMOVED ( 0x10000000 )
#define FCB_STATE_WRITECACHEING_ENABLED ( 0x08000000 )
#define FCB_STATE_WRITEBUFFERING_ENABLED ( 0x04000000 )
#define FCB_STATE_READCACHEING_ENABLED ( 0x02000000 )
#define FCB_STATE_READBUFFERING_ENABLED ( 0x01000000 )
#define FCB_STATE_OPENSHARING_ENABLED ( 0x00800000 )
#define FCB_STATE_COLLAPSING_ENABLED ( 0x00400000 )
#define FCB_STATE_LOCK_BUFFERING_ENABLED ( 0x00200000 )
#define FCB_STATE_FILESIZECACHEING_ENABLED ( 0x00100000 )
#define FCB_STATE_FILETIMECACHEING_ENABLED ( 0x00080000 )
#define FCB_STATE_TIME_AND_SIZE_ALREADY_SET ( 0x00040000 )
#define FCB_STATE_SPECIAL_PATH ( 0x00020000 )
#define FCB_STATE_FILE_IS_SHADOWED ( 0x00010000 )
#define FCB_STATE_FILE_IS_DISK_COMPRESSED ( 0x00008000 )
#define FCB_STATE_FILE_IS_BUF_COMPRESSED ( 0x00004000 )
#define FCB_STATE_BUFFERSTATE_CHANGING ( 0x00002000 )
#define FCB_STATE_FAKEFCB ( 0x00001000 )
#define FCB_STATE_DELAY_CLOSE ( 0x00000800 )
#define FCB_STATE_READAHEAD_DEFERRED ( 0x00000100 )
#define FCB_STATE_ORPHANED ( 0x00000080 )
#define FCB_STATE_BUFFERING_STATE_CHANGE_PENDING ( 0x00000040 )
#define FCB_STATE_TEMPORARY ( 0x00000020 )
#define FCB_STATE_DISABLE_LOCAL_BUFFERING ( 0x00000010 )
#define FCB_STATE_LWIO_ENABLED ( 0x00000008 )
#define FCB_STATE_PAGING_FILE ( 0x00000004 )
#define FCB_STATE_TRUNCATE_ON_CLOSE ( 0x00000002 )
#define FCB_STATE_DELETE_ON_CLOSE ( 0x00000001 )
#define FCB_STATE_BUFFERING_STATE_MASK \
(( FCB_STATE_WRITECACHEING_ENABLED \ | FCB_STATE_WRITEBUFFERING_ENABLED \ | FCB_STATE_READCACHEING_ENABLED \ | FCB_STATE_READBUFFERING_ENABLED \ | FCB_STATE_OPENSHARING_ENABLED \ | FCB_STATE_COLLAPSING_ENABLED \ | FCB_STATE_LOCK_BUFFERING_ENABLED \ | FCB_STATE_FILESIZECACHEING_ENABLED \ | FCB_STATE_FILETIMECACHEING_ENABLED )) //
// This is the MAX recursive resource limit.
//
#define MAX_FCB_ASYNC_ACQUIRE (0xf000)
typedef struct _FCB_INIT_PACKET { PULONG pAttributes; // in the fcb this is DirentRxFlags;
PULONG pNumLinks; // in the fcb this is NumberOfLinks;
PLARGE_INTEGER pCreationTime; // these fields are the same as for the Fcb
PLARGE_INTEGER pLastAccessTime; PLARGE_INTEGER pLastWriteTime; PLARGE_INTEGER pLastChangeTime; PLARGE_INTEGER pAllocationSize; // common header fields
PLARGE_INTEGER pFileSize; PLARGE_INTEGER pValidDataLength; } FCB_INIT_PACKET;
//
// A SRV_OPEN contains
// 0) signature and refcount
// a) backpointer to the FCB
// b) backpointer to the NET_ROOT //maybe
// c) a list of FOXB structures
// d) access rights and collapsability status
// .
// .
// z) whatever additional storage is request by the minirdr (or creator of the block).
//
// The SRV_OPEN points to a structure describing a spevific open on the
// server; multiple file objects and fileobject extensions (FOBXs) can
// share the same srvopen if the access rights are correct. For example,
// this would be where the FID is stored for SMBs. A list of these hangs
// from the FCB. Similarly, all fileobject extensionss that share the same
// serverside open are listed together here. Also here is information
// about whether a new open of this FCB can share this serverside open
// context; obviously the guys that pass the test on the list.
//
//
// The SRVOPEN flags are split into two groups, i.e., visible to mini rdrs and invisible to mini rdrs.
// The visible ones are defined above and the definitions for the invisible ones can be found
// in fcb.h. The convention that has been adopted is that the lower 16 flags will be visible
// to the mini rdr and the upper 16 flags will be reserved for the wrapper. This needs to be
// enforced in defining new flags.
//
#define SRVOPEN_FLAG_ENCLOSED_ALLOCATED (0x10000)
#define SRVOPEN_FLAG_FOBX_USED (0x20000)
#define SRVOPEN_FLAG_SHAREACCESS_UPDATED (0x40000)
#ifdef __cplusplus
typedef struct _SRV_OPEN : public MRX_SRV_OPEN { #else // !__cplusplus
typedef struct _SRV_OPEN {
//
// the portion of SRV_OPEN visible to all the mini redirectors.
//
union { MRX_SRV_OPEN; struct { MRX_NORMAL_NODE_HEADER spacer; PFCB Fcb; // the Fcb for this srv_open
}; }; #endif // !__cplusplus
BOOLEAN UpperFinalizationDone;
//
// the current condition of the SRV_OPEN, good/bad/in transition
//
RX_BLOCK_CONDITION Condition;
//
// Buffering state manager token
//
LONG BufferingToken;
//
// List Entry to thread together all the FCB instances marked
// for garbage collection/scavenging.
//
LIST_ENTRY ScavengerFinalizationList;
//
// The list of contexts whose processing has been suspended pending the state
// transition of the SRV_OPEN.
//
LIST_ENTRY TransitionWaitList;
//
// List Head for the list of FOBXs associated with this SRV_OPEN
//
LIST_ENTRY FobxList;
//
// The colocated instance of FOBX that is allocated whenever a SRV_OPEN
// instance is allocated.
//
PFOBX InternalFobx;
//
// the data structure for maintaining the mapping between the key
// associated with the SRV_OPEN instance by the mini redirector and
// the SRV_OPEN instance
//
union { LIST_ENTRY SrvOpenKeyList; ULONG SequenceNumber; }; NTSTATUS OpenStatus; } SRV_OPEN, *PSRV_OPEN;
#define RxWriteCacheingAllowed(Fcb,SrvOpen) \
(FlagOn( (Fcb)->FcbState, FCB_STATE_WRITECACHEING_ENABLED ) && \ !FlagOn( (SrvOpen)->Flags, SRVOPEN_FLAG_DONTUSE_WRITE_CACHEING ))
#define SRVOPEN_INIT_KEY (0)
//
// A FOBX contains
// 0) signature and refcount
// a) backpointer to the FCB
// b) backpointer to the SRV_OPEN
// c) context information about this open
// ...
// z) whatever additional storage is request by the minirdr (or creator of the block).
//
// The FOBX points to the "fileobject extension", i.e. all the stuff that
// is per fileobject is not stored there because the IO system provides
// fixed size filesystem objects (not a dig BTW, that's just the decision).
// The FOBX for any file object is referenced by the FsContext2 field in
// the fileobject. Even tho the FOBX is ordinarily a terminus in the
// structure, it is currently refcounted anyway.
// The FOBX flags are split into two groups, i.e., visible to mini rdrs and invisible to mini rdrs.
// The visible ones are defined above and the definitions for the invisible ones can be found
// in fcb.h. The convention that has been adopted is that the lower 16 flags will be visible
// to the mini rdr and the upper 16 flags will be reserved for the wrapper. This needs to be
// enforced in defining new flags.
//
#define FOBX_FLAG_MATCH_ALL (0x10000)
//
// This tells us whether we allocated buffers to hold search templates.
//
#define FOBX_FLAG_FREE_UNICODE (0x20000)
//
// These flags prevents cleanup from updating the modify time, etc.
//
#define FOBX_FLAG_USER_SET_LAST_WRITE (0x40000)
#define FOBX_FLAG_USER_SET_LAST_ACCESS (0x80000)
#define FOBX_FLAG_USER_SET_CREATION (0x100000)
#define FOBX_FLAG_USER_SET_LAST_CHANGE (0x200000)
//
// This bit says the file object associated with this Fobx was opened for
// read only access.
//
#define FOBX_FLAG_READ_ONLY (0x400000)
//
// the delete on close flag is used to track a file object that was opened with delete-on-close;
// when this object is closed, we copy the bit to the fcb and make it global
//
#define FOBX_FLAG_DELETE_ON_CLOSE (0x800000)
//
// this bits is used by minirdrs that do not have NT semantics. for example, the smbmini has
// to close a file before it can try a rename or delete. after the operation, we prevent people from
// getting back in.
//
#define FOBX_FLAG_SRVOPEN_CLOSED (0x1000000)
//
// this bit is used to tell whether the original name was a UNC name so that
// we can return the name the same way
//
#define FOBX_FLAG_UNC_NAME (0x2000000)
//
// this flag tells if this fobx is allocated as part of a larger structure
//
#define FOBX_FLAG_ENCLOSED_ALLOCATED (0x4000000)
//
// this flag specfies if the FOBX was included in the count of dormant
// files against the server.
//
#define FOBX_FLAG_MARKED_AS_DORMANT (0x8000000)
//
// this flag notes down the fact that some writes have been issued on this FOBX
// this is used to issue flushes on close
//
#define FOBX_FLAG_WRITES_ISSUED (0x10000000)
#ifdef __cplusplus
typedef struct _FOBX : public MRX_FOBX { #else // !__cplusplus
typedef struct _FOBX { //
// the portion of FOBX visible to the mini redirectors
//
union { MRX_FOBX; struct { MRX_NORMAL_NODE_HEADER spacer; PSRV_OPEN SrvOpen; }; }; #endif // __cplusplus
//
// a serial number....it wraps but not often
//
ULONG FobxSerialNumber;
//
// list entry to wire the FOBX to the list of FOBXs maintained in
// the associated SRV_OPEN
//
LIST_ENTRY FobxQLinks;
//
// list entry to gather all the FOBX instance marked for garbage collection
// scavenging
//
LIST_ENTRY ScavengerFinalizationList;
//
// list entry to thread together all the FOBXs which have a pending close
// operation.
//
LIST_ENTRY ClosePendingList;
BOOLEAN UpperFinalizationDone; BOOLEAN ContainsWildCards; BOOLEAN fOpenCountDecremented;
//
// Parameters depending on the type of file opened, pipe/file etc.
//
union {
struct {
union { #ifndef __cplusplus
MRX_PIPE_HANDLE_INFORMATION; #endif // __cplusplus
MRX_PIPE_HANDLE_INFORMATION PipeHandleInformation; };
LARGE_INTEGER CollectDataTime; ULONG CollectDataSize; THROTTLING_STATE ThrottlingState; // for peek and read om msgmodepipes
//
// these serialization Qs must be together
// and read must be the first
//
LIST_ENTRY ReadSerializationQueue; LIST_ENTRY WriteSerializationQueue; } NamedPipe;
struct { RXVBO PredictedReadOffset; RXVBO PredictedWriteOffset; THROTTLING_STATE LockThrottlingState; // for locks
LARGE_INTEGER LastLockOffset; LARGE_INTEGER LastLockRange; } DiskFile; } Specific; } FOBX, *PFOBX;
#define FOBX_NUMBER_OF_SERIALIZATION_QUEUES 2
//
// The RDBSS wrapper relies upon ref. counting to mark the instances of
// various data structures. The following macros implement a debugging
// mechanism to track/log the reference counts associated with various
// data structures. A fine grained control to monitor each data structure
// separately is provided. Each of these can be further controlled to either
// print the tracking info or log it.
//
#define RDBSS_REF_TRACK_SRVCALL (0x00000001)
#define RDBSS_REF_TRACK_NETROOT (0x00000002)
#define RDBSS_REF_TRACK_VNETROOT (0x00000004)
#define RDBSS_REF_TRACK_NETFOBX (0x00000008)
#define RDBSS_REF_TRACK_NETFCB (0x00000010)
#define RDBSS_REF_TRACK_SRVOPEN (0x00000020)
#define RX_LOG_REF_TRACKING (0x80000000)
#define RX_PRINT_REF_TRACKING (0x40000000)
//
// The reference count tracking mechanism is activated by setting the following
// variable to the appropriate value defined above.
//
extern ULONG RdbssReferenceTracingValue;
//
// Macros for tracking the line number and the file of each reference and
// derefernce on the data structure. on Non DBG builds they are defined as
// NOTHING. For each data structure the appropriate reference/dereference
// macro is defined, These should be used instead of raw manipulation of
// the reference counts.
//
#ifdef DBG
VOID RxpTrackReference ( ULONG TraceType, PCHAR FileName, ULONG Line, PVOID Instance );
BOOLEAN RxpTrackDereference ( ULONG TraceType, PCHAR FileName, ULONG Line, PVOID Instance );
#else
#define RxpTrackReference(Type,File,Line,Instance) NOTHING
#define RxpTrackDereference(Type,File,Line,Instance) NOTHING
#endif
#define REF_TRACING_ON(TraceMask) (TraceMask & RdbssReferenceTracingValue)
#define PRINT_REF_COUNT(TYPE,Count) \
if (REF_TRACING_ON( RDBSS_REF_TRACK_ ## TYPE ) && \ (RdbssReferenceTracingValue & RX_PRINT_REF_TRACKING)) { \ DbgPrint("%ld\n",Count); \ }
#define RxReferenceSrvCallAtDpc(SrvCall) \
RxpTrackReference( RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall ); \ ASSERT( SrvCall->NodeReferenceCount > 1 ); \ InterlockedIncrement( &SrvCall->NodeReferenceCount )
#define RxReferenceSrvCall(SrvCall) \
RxpTrackReference( RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall ); \ RxReference( SrvCall )
#define RxDereferenceSrvCall(SrvCall,LockHoldingState) \
RxpTrackDereference( RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall ); \ RxDereference(SrvCall, LockHoldingState )
#define RxReferenceNetRoot(NetRoot) \
RxpTrackReference( RDBSS_REF_TRACK_NETROOT, __FILE__, __LINE__, NetRoot ); \ RxReference( NetRoot )
#define RxDereferenceNetRoot( NetRoot, LockHoldingState ) \
RxpTrackDereference( RDBSS_REF_TRACK_NETROOT, __FILE__, __LINE__, NetRoot );\ RxDereference( NetRoot, LockHoldingState )
#define RxReferenceVNetRoot(VNetRoot) \
RxpTrackReference( RDBSS_REF_TRACK_VNETROOT, __FILE__, __LINE__, VNetRoot );\ RxReference( VNetRoot )
#define RxDereferenceVNetRoot( VNetRoot, LockHoldingState ) \
RxpTrackDereference( RDBSS_REF_TRACK_VNETROOT, __FILE__, __LINE__, VNetRoot ); \ RxDereference( VNetRoot, LockHoldingState )
#define RxReferenceNetFobx(Fobx) \
RxpTrackReference( RDBSS_REF_TRACK_NETFOBX, __FILE__, __LINE__, Fobx ); \ RxReference( Fobx )
#define RxDereferenceNetFobx(Fobx,LockHoldingState) \
RxpTrackDereference( RDBSS_REF_TRACK_NETFOBX, __FILE__, __LINE__, Fobx ); \ RxDereference( Fobx, LockHoldingState )
#define RxReferenceSrvOpen(SrvOpen) \
RxpTrackReference( RDBSS_REF_TRACK_SRVOPEN, __FILE__, __LINE__, SrvOpen ); \ RxReference( SrvOpen )
#define RxDereferenceSrvOpen( SrvOpen, LockHoldingState ) \
RxpTrackDereference( RDBSS_REF_TRACK_SRVOPEN, __FILE__, __LINE__, SrvOpen); \ RxDereference( SrvOpen, LockHoldingState )
#define RxReferenceNetFcb(Fcb) \
RxpTrackReference( RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb ); \ RxpReferenceNetFcb( Fcb )
//
// the following macros manipulate the reference count and also return the
// status of the final derefence or finalize call. This results in the usage
// of the , operator.
//
#define RxDereferenceNetFcb(Fcb) ( \
((LONG)RxpTrackDereference( RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb )), \ RxpDereferenceNetFcb( Fcb ))
#define RxDereferenceAndFinalizeNetFcb(Fcb,RxContext,RecursiveFinalize,ForceFinalize) ( \
RxpTrackDereference( RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb ), \ RxpDereferenceAndFinalizeNetFcb( Fcb, RxContext, RecursiveFinalize, ForceFinalize )) \
//
// Check for structure alignment errors
//
VOID RxCheckFcbStructuresForAlignment( VOID );
//
// SRV_CALL related routines.
//
PSRV_CALL RxCreateSrvCall ( IN PRX_CONTEXT RxContext, IN PUNICODE_STRING Name, IN PUNICODE_STRING InnerNamePrefix OPTIONAL, IN PRX_CONNECTION_ID RxConnectionId );
#define RxWaitForStableSrvCall(SRVCALL,RXCONTEXT) { \
RxDbgTrace( 0, Dbg, ("RxWaitForStableSrvCall -- %lx\n",(SRVCALL)) ); \ RxWaitForStableCondition( &(SRVCALL)->Condition, &(SRVCALL)->TransitionWaitList, (RXCONTEXT), NULL); \ }
#define RxWaitForStableSrvCall_Async(SRVCALL,RXCONTEXT,PNTSTATUS) { \
RxDbgTrace( 0, Dbg, ("RxWaitForStableSrvCall -- %lx\n",(SRVCALL)) ); \ RxWaitForStableCondition( &(SRVCALL)->Condition, &(SRVCALL)->TransitionWaitList, (RXCONTEXT), (PNTSTATUS) ); \ }
#define RxTransitionSrvCall(SRVCALL,CONDITION) \
RxDbgTrace( 0, Dbg, ("RxTransitionSrvCall -- %lx Condition -- %ld\n",(SRVCALL),(CONDITION)) ); \ RxUpdateCondition( (CONDITION), &(SRVCALL)->Condition, &(SRVCALL)->TransitionWaitList )
BOOLEAN RxFinalizeSrvCall ( OUT PSRV_CALL ThisSrvCall, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize );
//
// NET_ROOT related routines.
//
PNET_ROOT RxCreateNetRoot ( IN PSRV_CALL SrvCall, IN PUNICODE_STRING Name, IN ULONG NetRootFlags, IN PRX_CONNECTION_ID OPTIONAL RxConnectionId );
VOID RxFinishNetRootInitialization ( IN OUT PNET_ROOT ThisNetRoot, IN PMINIRDR_DISPATCH Dispatch, IN PUNICODE_STRING InnerNamePrefix, IN ULONG FcbSize, IN ULONG SrvOpenSize, IN ULONG FobxSize, IN ULONG NetRootFlags );
#define RxWaitForStableNetRoot(NETROOT,RXCONTEXT) \
RxDbgTrace(0, Dbg, ("RxWaitForStableNetRoot -- %lx\n",(NETROOT))); \ RxWaitForStableCondition(&(NETROOT)->Condition,&(NETROOT)->TransitionWaitList,(RXCONTEXT),NULL)
#define RxTransitionNetRoot(NETROOT,CONDITION) \
RxDbgTrace(0, Dbg, ("RxTransitionNetRoot -- %lx Condition -- %ld\n",(NETROOT),(CONDITION))); \ RxUpdateCondition((CONDITION),&(NETROOT)->Condition,&(NETROOT)->TransitionWaitList)
BOOLEAN RxFinalizeNetRoot ( OUT PNET_ROOT ThisNetRoot, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize );
//
// V_NET_ROOT related routines
//
NTSTATUS RxInitializeVNetRootParameters ( PRX_CONTEXT RxContext, OUT LUID *LogonId, OUT PULONG SessionId, OUT PUNICODE_STRING *UserNamePtr, OUT PUNICODE_STRING *UserDomainNamePtr, OUT PUNICODE_STRING *PasswordPtr, OUT PULONG Flags );
VOID RxUninitializeVNetRootParameters ( IN PUNICODE_STRING UserName, IN PUNICODE_STRING UserDomainName, IN PUNICODE_STRING Password, OUT PULONG Flags );
PV_NET_ROOT RxCreateVNetRoot ( IN PRX_CONTEXT RxContext, IN PNET_ROOT NetRoot, IN PUNICODE_STRING CanonicalName, IN PUNICODE_STRING LocalNetRootName, IN PUNICODE_STRING FilePath, IN PRX_CONNECTION_ID RxConnectionId );
BOOLEAN RxFinalizeVNetRoot ( OUT PV_NET_ROOT ThisVNetRoot, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize );
#define RxWaitForStableVNetRoot(VNETROOT,RXCONTEXT) \
RxDbgTrace( 0, Dbg, ("RxWaitForStableVNetRoot -- %lx\n",(VNETROOT)) ); \ RxWaitForStableCondition( &(VNETROOT)->Condition, &(VNETROOT)->TransitionWaitList, (RXCONTEXT), NULL )
#define RxTransitionVNetRoot(VNETROOT,CONDITION) \
RxDbgTrace( 0, Dbg, ("RxTransitionVNetRoot -- %lx Condition -- %ld\n", (VNETROOT), (CONDITION)) ); \ RxUpdateCondition( (CONDITION), &(VNETROOT)->Condition, &(VNETROOT)->TransitionWaitList )
#ifdef USE_FILESIZE_LOCK
//
// FCB related routines.
//
#define RxAcquireFileSizeLock(PFCB) { \
ExAcquireFastMutex( (PFCB)->Specific.Fcb.FileSizeLock ); \ } #define RxReleaseFileSizeLock(PFCB) { \
ExReleaseFastMutex((PFCB)->Specific.Fcb.FileSizeLock); \ }
#endif
VOID RxSetFileSizeWithLock ( IN OUT PFCB Fcb, IN PLONGLONG FileSize );
VOID RxGetFileSizeWithLock ( IN PFCB Fcb, OUT PLONGLONG FileSize );
PFCB RxCreateNetFcb ( OUT PRX_CONTEXT RxContext, IN PV_NET_ROOT VNetRoot, IN PUNICODE_STRING Name );
#define RxWaitForStableNetFcb(FCB,RXCONTEXT) \
RxDbgTrace(0, Dbg, ("RxWaitForStableNetFcb -- %lx\n",(FCB))); \ RxWaitForStableCondition( &(FCB)->Condition, &(FCB)->NonPaged->TransitionWaitList, (RXCONTEXT), NULL )
#define RxTransitionNetFcb(FCB,CONDITION) \
RxDbgTrace(0, Dbg, ("RxTransitionNetFcb -- %lx Condition -- %ld\n",(FCB),(CONDITION))); \ RxUpdateCondition( (CONDITION), &(FCB)->Condition, &(FCB)->NonPaged->TransitionWaitList )
#define RxFormInitPacket(IP,I1,I1a,I2,I3,I4a,I4b,I5,I6,I7) (\
IP.pAttributes = I1, \ IP.pNumLinks = I1a, \ IP.pCreationTime = I2, \ IP.pLastAccessTime = I3, \ IP.pLastWriteTime = I4a, \ IP.pLastChangeTime = I4b, \ IP.pAllocationSize = I5, \ IP.pFileSize = I6, \ IP.pValidDataLength = I7, \ &IP)
#if DBG
#define ASSERT_CORRECT_FCB_STRUCTURE_DBG_ONLY(___thisfcb) {\
ASSERT( ___thisfcb->NonPaged == ___thisfcb->CopyOfNonPaged ); \ ASSERT( ___thisfcb->NonPaged->FcbBackPointer == ___thisfcb ); \ } #else
#define ASSERT_CORRECT_FCB_STRUCTURE_DBG_ONLY(___thisfcb)
#endif
#define ASSERT_CORRECT_FCB_STRUCTURE(THIS_FCB__) { \
ASSERT( NodeTypeIsFcb(THIS_FCB__)); \ ASSERT( THIS_FCB__->NonPaged != NULL ); \ ASSERT( NodeType(THIS_FCB__->NonPaged) == RDBSS_NTC_NONPAGED_FCB); \ ASSERT_CORRECT_FCB_STRUCTURE_DBG_ONLY(THIS_FCB__) \ }
RX_FILE_TYPE RxInferFileType ( IN PRX_CONTEXT RxContext );
VOID RxFinishFcbInitialization ( IN OUT PMRX_FCB Fcb, IN RX_FILE_TYPE FileType, IN PFCB_INIT_PACKET InitPacket OPTIONAL );
#define RxWaitForStableSrvOpen(SRVOPEN,RXCONTEXT) \
RxDbgTrace( 0, Dbg, ("RxWaitForStableFcb -- %lx\n",(SRVOPEN)) ); \ RxWaitForStableCondition( &(SRVOPEN)->Condition, &(SRVOPEN)->TransitionWaitList, (RXCONTEXT), NULL )
#define RxTransitionSrvOpen(SRVOPEN,CONDITION) \
RxDbgTrace( 0, Dbg, ("RxTransitionSrvOpen -- %lx Condition -- %ld\n",(SRVOPEN),(CONDITION)) ); \ RxUpdateCondition( (CONDITION), &(SRVOPEN)->Condition, &(SRVOPEN)->TransitionWaitList )
VOID RxRemoveNameNetFcb ( OUT PFCB ThisFcb );
VOID RxpReferenceNetFcb ( PFCB Fcb );
LONG RxpDereferenceNetFcb ( PFCB Fcb );
BOOLEAN RxpDereferenceAndFinalizeNetFcb ( OUT PFCB ThisFcb, IN PRX_CONTEXT RxContext, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize );
#if DBG
extern BOOLEAN RxLoudFcbOpsOnExes; BOOLEAN RxLoudFcbMsg( PUCHAR msg, PUNICODE_STRING Name ); #else
#define RxLoudFcbMsg(a,b) (FALSE)
#endif
//
// SRV_OPEN related methods
//
PSRV_OPEN RxCreateSrvOpen ( IN PV_NET_ROOT VNetRoot, IN OUT PFCB Fcb );
VOID RxTransitionSrvOpenState ( OUT PSRV_OPEN ThisSrvOpen, IN RX_BLOCK_CONDITION Condition );
BOOLEAN RxFinalizeSrvOpen ( OUT PSRV_OPEN ThisSrvOpen, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize );
#if 0
#else
INLINE PUNICODE_STRING GET_ALREADY_PREFIXED_NAME ( PMRX_SRV_OPEN SrvOpen, PMRX_FCB Fcb) { PFCB ThisFcb = (PFCB)Fcb;
#if DBG
if (SrvOpen != NULL ) { ASSERT( NodeType( SrvOpen ) == RDBSS_NTC_SRVOPEN ); ASSERT( ThisFcb != NULL ); ASSERT( NodeTypeIsFcb( Fcb) ); ASSERT( SrvOpen->pFcb == Fcb ); ASSERT( SrvOpen->pAlreadyPrefixedName == &ThisFcb->PrivateAlreadyPrefixedName ); } #endif
return( &ThisFcb->PrivateAlreadyPrefixedName); } #endif
#define GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(Rxcontext) \
(GET_ALREADY_PREFIXED_NAME( (Rxcontext)->pRelevantSrvOpen, (Rxcontext)->pFcb ))
//
// FOBX related routines
//
PMRX_FOBX RxCreateNetFobx ( OUT PRX_CONTEXT RxContext, IN PMRX_SRV_OPEN MrxSrvOpen );
BOOLEAN RxFinalizeNetFobx ( OUT PFOBX ThisFobx, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize );
#endif // _FCB_STRUCTS_DEFINED_
|