|
|
/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
replutil.h
Abstract:
Header file for utility routines for the NT File Replication Service.
Author:
David A. Orbits (davidor) 3-Mar-1997 Created
Environment:
User Mode Service
Revision History:
--*/ #ifndef _REPLUTIL_INCLUDED_
#define _REPLUTIL_INCLUDED_
#endif
#ifdef __cplusplus
extern "C" { #endif
#include <frserror.h>
#include <config.h>
#define BACKSLASH_CHAR TEXT('\\')
#define COLON_CHAR TEXT(':')
#define DOT_CHAR TEXT('.')
#define UNICODE_STAR (L'*')
#define UNICODE_QMARK (L'?')
#define UNICODE_SPACE 0x0020
#define UNICODE_TAB 0x0009
#define TIME_STRING_LENGTH 32
//
// The maximum length of a volume label. This is defined in ntos\inc\io.h
// but since this is the only def needed from io.h it is copied here. sigh!
//
#define MAXIMUM_VOLUME_LABEL_LENGTH (32 * sizeof(WCHAR)) // 32 characters
#define GUID_CHAR_LEN 40
#define OBJECT_ID_LENGTH sizeof(GUID)
#define FILE_ID_LENGTH sizeof(ULONGLONG)
#define GUIDS_EQUAL(_a_, _b_) (memcmp((_a_), (_b_), sizeof(GUID)) == 0)
#define COPY_GUID(_a_, _b_) CopyMemory((_a_), (_b_), sizeof(GUID))
#define IS_GUID_ZERO(_g_) ((*((PULONG)(_g_)+0) | \
*((PULONG)(_g_)+1) | \ *((PULONG)(_g_)+2) | \ *((PULONG)(_g_)+3) ) == 0)
#define COPY_TIME(_a_, _b_) CopyMemory((_a_), (_b_), sizeof(FILETIME))
#define IS_TIME_ZERO(_g_) ((*((PULONG)(&(_g_))+0) | *((PULONG)(&(_g_))+1) ) == 0)
//
// A few macros for working with MD5 checksums.
//
#define IS_MD5_CHKSUM_ZERO(_x_) \
(((*(((PULONG) (_x_))+0)) | (*(((PULONG) (_x_))+1)) | \ (*(((PULONG) (_x_))+2)) | (*(((PULONG) (_x_))+3)) ) == (ULONG) 0)
#define MD5_EQUAL(_a_, _b_) (memcmp((_a_), (_b_), MD5DIGESTLEN) == 0)
//
// Is a handle valid?
// Some functions set the handle to NULL and some to
// INVALID_HANDLE_VALUE (-1). This define handles both
// cases.
//
#define HANDLE_IS_VALID(_Handle) ((_Handle) && ((_Handle) != INVALID_HANDLE_VALUE))
//
// Only close valid handles and then set the handle invalid.
// FRS_CLOSE(handle);
//
#define FRS_CLOSE(_Handle) \
if (HANDLE_IS_VALID(_Handle)) { \ CloseHandle(_Handle); \ (_Handle) = INVALID_HANDLE_VALUE; \ }
DWORD FrsResetAttributesForReplication( PWCHAR Name, HANDLE Handle );
LONG FrsIsParent( IN PWCHAR Directory, IN PWCHAR Path );
LPTSTR FrsSupInitPath( OUT LPTSTR OutPath, IN LPTSTR InPath, IN ULONG MaxOutPath );
ULONG FrsForceDeleteFile( PTCHAR DestName );
HANDLE FrsCreateEvent( IN BOOL ManualReset, IN BOOL InitialState );
HANDLE FrsCreateWaitableTimer( IN BOOL ManualReset );
ULONG FrsUuidCreate( OUT GUID *Guid );
VOID FrsNowAsFileTime( IN PLONGLONG Now );
VOID FileTimeToString( IN FILETIME *FileTime, OUT PCHAR Buffer // buffer must be at least 32 bytes long.
);
VOID FileTimeToStringClockTime( IN FILETIME *FileTime, OUT PCHAR Buffer // buffer must be at least 9 bytes long.
);
VOID GuidToStr( IN GUID *pGuid, OUT PCHAR s );
VOID GuidToStrW( IN GUID *pGuid, OUT PWCHAR ws );
BOOL StrWToGuid( IN PWCHAR ws, OUT GUID *pGuid );
VOID StrToGuid( IN PCHAR s, OUT GUID *pGuid );
NTSTATUS SetupOnePrivilege ( ULONG Privilege, PUCHAR PrivilegeName );
PWCHAR FrsGetResourceStr( LONG Id );
//
// Convenient DesiredAccess
//
#define READ_ATTRIB_ACCESS (FILE_READ_ATTRIBUTES | SYNCHRONIZE)
#define WRITE_ATTRIB_ACCESS (FILE_WRITE_ATTRIBUTES | SYNCHRONIZE)
#define READ_ACCESS (GENERIC_READ | GENERIC_EXECUTE | SYNCHRONIZE)
#define ATTR_ACCESS (READ_ACCESS | FILE_WRITE_ATTRIBUTES)
#define WRITE_ACCESS (GENERIC_WRITE | GENERIC_EXECUTE | SYNCHRONIZE)
#define RESTORE_ACCESS (READ_ACCESS | \
WRITE_ACCESS | \ WRITE_DAC | \ WRITE_OWNER)
#define OPLOCK_ACCESS (FILE_READ_ATTRIBUTES)
//
// Convenient CreateOptions
//
#define OPEN_OPTIONS (FILE_OPEN_FOR_BACKUP_INTENT | \
FILE_SEQUENTIAL_ONLY | \ FILE_OPEN_NO_RECALL | \ FILE_OPEN_REPARSE_POINT | \ FILE_SYNCHRONOUS_IO_NONALERT) #define ID_OPTIONS (OPEN_OPTIONS | FILE_OPEN_BY_FILE_ID)
#define OPEN_OPLOCK_OPTIONS (FILE_RESERVE_OPFILTER | FILE_OPEN_REPARSE_POINT)
#define ID_OPLOCK_OPTIONS (FILE_OPEN_FOR_BACKUP_INTENT | \
FILE_RESERVE_OPFILTER | \ FILE_OPEN_REPARSE_POINT | \ FILE_OPEN_BY_FILE_ID)
//
// convenient ShareMode
//
#define SHARE_ALL (FILE_SHARE_READ | \
FILE_SHARE_WRITE | \ FILE_SHARE_DELETE) #define SHARE_NONE (0)
//
// File attributes that prevent installation and prevent
// hammering the object id.
//
#define NOREPL_ATTRIBUTES (FILE_ATTRIBUTE_READONLY | \
FILE_ATTRIBUTE_SYSTEM | \ FILE_ATTRIBUTE_HIDDEN)
DWORD FrsOpenSourceFileW( OUT PHANDLE Handle, IN LPCWSTR lpFileName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions );
DWORD FrsOpenSourceFile2W( OUT PHANDLE Handle, IN LPCWSTR lpFileName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions, IN ULONG ShareMode );
DWORD FrsCheckReparse( IN PWCHAR Name, IN PVOID Id, IN DWORD IdLen, IN HANDLE VolumeHandle );
DWORD FrsDeleteReparsePoint( IN HANDLE Handle );
DWORD FrsChaseSymbolicLink( IN PWCHAR SymLink, OUT PWCHAR *OutPrintName, OUT PWCHAR *OutSubstituteName );
DWORD FrsTraverseReparsePoints( IN PWCHAR SuppliedPath, OUT PWCHAR *RealPath );
DWORD FrsOpenSourceFileById( OUT PHANDLE Handle, OUT PFILE_NETWORK_OPEN_INFORMATION FileOpenInfo, OUT OVERLAPPED *OverLap, IN HANDLE VolumeHandle, IN PVOID ObjectId, IN ULONG Length, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions, IN ULONG ShareMode, IN ULONG CreateDispostion );
PWCHAR FrsGetFullPathByHandle( IN PWCHAR Name, IN HANDLE Handle );
PWCHAR FrsGetRelativePathByHandle( IN PWCHAR Name, IN HANDLE Handle );
DWORD FrsCreateFileRelativeById( OUT PHANDLE Handle, IN HANDLE VolumeHandle, IN PVOID ParentObjectId, IN ULONG OidLength, IN ULONG FileCreateAttributes, IN PWCHAR BaseFileName, IN USHORT FileNameLength, IN PLARGE_INTEGER AllocationSize, IN ULONG CreateDisposition, IN ACCESS_MASK DesiredAccess );
DWORD FrsOpenFileRelativeByName( IN HANDLE VolumeHandle, IN PULONGLONG FileReferenceNumber, IN PWCHAR FileName, IN GUID *ParentGuid, IN GUID *FileGuid, OUT HANDLE *Handle );
typedef struct _QHASH_TABLE_ QHASH_TABLE, *PQHASH_TABLE; DWORD FrsDeleteFileRelativeByName( IN HANDLE VolumeHandle, IN GUID *ParentGuid, IN PWCHAR FileName, IN PQHASH_TABLE FrsWriteFilter );
DWORD FrsDeleteFileObjectId( IN HANDLE Handle, IN LPCWSTR FileName );
DWORD FrsGetOrSetFileObjectId( IN HANDLE Handle, IN LPCWSTR FileName, IN BOOL CallerSupplied, OUT PFILE_OBJECTID_BUFFER ObjectIdBuffer );
DWORD FrsReadFileUsnData( IN HANDLE Handle, OUT USN *UsnBuffer );
DWORD FrsMarkHandle( IN HANDLE VolumeHandle, IN HANDLE Handle );
DWORD FrsReadFileParentFid( IN HANDLE Handle, OUT ULONGLONG *ParentFid );
DWORD FrsDeletePath( IN PWCHAR Path, IN DWORD DirectoryFlags );
DWORD FrsRestrictAccessToFileOrDirectory( PWCHAR Name, HANDLE Handle, BOOL NoInherit );
VOID FrsAddToMultiString( IN PWCHAR AddStr, IN OUT DWORD *IOSize, IN OUT DWORD *IOIdx, IN OUT PWCHAR *IOStr );
VOID FrsCatToMultiString( IN PWCHAR CatStr, IN OUT DWORD *IOSize, IN OUT DWORD *IOIdx, IN OUT PWCHAR *IOStr );
BOOL FrsSearchArgv( IN LONG ArgC, IN PWCHAR *ArgV, IN PWCHAR ArgKey, OUT PWCHAR *ArgValue );
BOOL FrsSearchArgvDWord( IN LONG ArgC, IN PWCHAR *ArgV, IN PWCHAR ArgKey, OUT PDWORD ArgValue );
BOOL FrsDissectCommaList ( IN UNICODE_STRING RawArg, OUT PUNICODE_STRING FirstArg, OUT PUNICODE_STRING RemainingArg );
BOOL FrsCheckNameFilter( IN PUNICODE_STRING Name, IN PLIST_ENTRY FilterListHead );
VOID FrsEmptyNameFilter( IN PLIST_ENTRY FilterListHead );
VOID FrsLoadNameFilter( IN PUNICODE_STRING FilterString, IN PLIST_ENTRY FilterListHead );
ULONG FrsParseIntegerCommaList( IN PWCHAR ArgString, IN ULONG MaxResults, OUT PLONG Results, OUT PULONG NumberResults, OUT PULONG Offset );
//
// Unicode Name support routines, implemented in Name.c
//
// The routines here are used to manipulate unicode names
// The code is copied here from FsRtl because it calls the pool allocator.
//
//
// The following macro is used to determine if a character is wild.
//
#define FrsIsUnicodeCharacterWild(C) ( \
(((C) == UNICODE_STAR) || ((C) == UNICODE_QMARK)) \ )
VOID FrsDissectName ( IN UNICODE_STRING Path, OUT PUNICODE_STRING FirstName, OUT PUNICODE_STRING RemainingName );
BOOLEAN FrsDoesNameContainWildCards ( IN PUNICODE_STRING Name );
BOOLEAN FrsAreNamesEqual ( IN PUNICODE_STRING ConstantNameA, IN PUNICODE_STRING ConstantNameB, IN BOOLEAN IgnoreCase, IN PCWCH UpcaseTable OPTIONAL );
BOOLEAN FrsIsNameInExpression ( IN PUNICODE_STRING Expression, IN PUNICODE_STRING Name, IN BOOLEAN IgnoreCase, IN PWCH UpcaseTable OPTIONAL );
//
// The following is taken from clusrtl.h
//
//
// Routine Description:
//
// Initializes the FRS run time library.
//
// Arguments:
//
// RunningAsService - TRUE if the process is running as an NT service.
// FALSE if running as a console app.
//
// Return Value:
//
// ERROR_SUCCESS if the function succeeds.
// A Win32 error code otherwise.
//
DWORD FrsRtlInitialize( IN BOOL RunningAsService );
//
// Routine Description:
//
// Cleans up the FRS run time library.
//
// Arguments:
//
// RunningAsService - TRUE if the process is running as an NT service.
// FALSE if running as a console app.
//
// Return Value:
//
// None.
//
VOID FrsRtlCleanup( VOID );
//
// PLIST_ENTRY
// GetListHead(
// PLIST_ENTRY ListHead
// );
//
#define GetListHead(ListHead) ((ListHead)->Flink)
//
// PLIST_ENTRY
// GetListTail(
// PLIST_ENTRY ListHead
// );
//
#define GetListTail(ListHead) ((ListHead)->Blink)
//
// PLIST_ENTRY
// GetListNext(
// PLIST_ENTRY Entry
// );
//
#define GetListNext(Entry) ((Entry)->Flink)
//
// VOID
// FrsRemoveEntryList(
// PLIST_ENTRY Entry
// );
//
// *NOTE* The Flink/Blink of the removed entry are set to NULL to cause an
// Access violation if a thread is following a list and an element is removed.
// UNFORTUNATELY there is still code that depends on this, perhaps through
// remove head/tail. Sigh. For now leave as is.
//
#define FrsRemoveEntryList(Entry) {\
PLIST_ENTRY _EX_Blink;\ PLIST_ENTRY _EX_Flink;\ PLIST_ENTRY _EX_Entry;\ _EX_Entry = (Entry);\ _EX_Flink = _EX_Entry->Flink;\ _EX_Blink = _EX_Entry->Blink;\ _EX_Blink->Flink = _EX_Flink;\ _EX_Flink->Blink = _EX_Blink;\ _EX_Entry->Flink = _EX_Entry->Blink = _EX_Entry;\ }
//
// VOID
// RemoveEntryListB(
// PLIST_ENTRY Entry
// );
//
// The BillyF version of remove entry list. The Flink/Blink of the removed
// entry are set to the entry address.
//
#define RemoveEntryListB(Entry) {\
PLIST_ENTRY _EX_Blink;\ PLIST_ENTRY _EX_Flink;\ PLIST_ENTRY _EX_Entry;\ _EX_Entry = (Entry);\ _EX_Flink = _EX_Entry->Flink;\ _EX_Blink = _EX_Entry->Blink;\ _EX_Blink->Flink = _EX_Flink;\ _EX_Flink->Blink = _EX_Blink;\ _EX_Entry->Flink = _EX_Entry->Blink = _EX_Entry;\ }
//
// Traverse a singlely linked NULL terminated list.
// Pass in the address of the list head, the type of the containing record,
// the offset to the link entry in the conatining record, and code for
// the loop body. pE is the iterator and is of type specified.
// Within the loop body the macros InsertSingleListEntry() and
// RemoveSingleListEntry() can be used to do the obvious things.
//
#define ForEachSingleListEntry( _HEAD_, _TYPE_, _OFFSET_, _STMT_ ) \
{ \ PSINGLE_LIST_ENTRY __Entry, __NextEntry, __PrevEntry; \ _TYPE_ *pE; \ \ __Entry = (_HEAD_); \ __NextEntry = (_HEAD_)->Next; \ \ while (__PrevEntry = __Entry, __Entry = __NextEntry, __Entry != NULL) { \ \ __NextEntry = __Entry->Next; \ pE = CONTAINING_RECORD(__Entry, _TYPE_, _OFFSET_); \ \ { _STMT_ } \ \ } \ \ } //
// The following three macros are only valid within the loop body above.
// Insert an entry before the current entry with pointer pE.
//
#define InsertSingleListEntry( _Item_, _xOFFSET_ ) \
(_Item_)->_xOFFSET_.Next = __Entry; \ __PrevEntry->Next = (PSINGLE_LIST_ENTRY) &((_Item_)->_xOFFSET_);
//
// Note that after you remove an entry the value for __Entry is set back to
// the __PrevEntry so when the loop continues __PrevEntry doesn't change
// since current entry had been removed.
//
#define RemoveSingleListEntry( _UNUSED_ ) \
__PrevEntry->Next = __NextEntry; \ __Entry->Next = NULL; \ __Entry = __PrevEntry;
//
// Return ptr to the previous node. Only valid inside above FOR loop.
// Useful when deleting the current entry.
//
#define PreviousSingleListEntry( _TYPE_, _OFFSET_) \
CONTAINING_RECORD(__PrevEntry, _TYPE_, _OFFSET_)
//
// General-purpose queue package. Taken from cluster\clusrtl.c
// *** WARNING *** To make the macros work properly for both lists and queues
// the first five items in FRS_LIST and FRS_QUEUE MUST match.
//
typedef struct _FRS_QUEUE FRS_QUEUE, *PFRS_QUEUE; struct _FRS_QUEUE { LIST_ENTRY ListHead; CRITICAL_SECTION Lock; DWORD Count; PFRS_QUEUE Control; DWORD ControlCount;
HANDLE Event; HANDLE RunDown; ULONG InitTime; LIST_ENTRY Full; LIST_ENTRY Empty; LIST_ENTRY Idled; BOOL IsRunDown; BOOL IsIdled; };
VOID FrsInitializeQueue( IN PFRS_QUEUE Queue, IN PFRS_QUEUE Control );
VOID FrsRtlDeleteQueue( IN PFRS_QUEUE Queue );
PLIST_ENTRY FrsRtlRemoveHeadQueue( IN PFRS_QUEUE Queue );
VOID FrsRtlUnIdledQueue( IN PFRS_QUEUE IdledQueue );
VOID FrsRtlUnIdledQueueLock( IN PFRS_QUEUE IdledQueue );
VOID FrsRtlIdleQueue( IN PFRS_QUEUE Queue );
VOID FrsRtlIdleQueueLock( IN PFRS_QUEUE Queue );
PLIST_ENTRY FrsRtlRemoveHeadQueueTimeoutIdled( IN PFRS_QUEUE Queue, IN DWORD dwMilliseconds, OUT PFRS_QUEUE *IdledQueue );
PLIST_ENTRY FrsRtlRemoveHeadQueueTimeout( IN PFRS_QUEUE Queue, IN DWORD dwMilliseconds );
VOID FrsRtlRemoveEntryQueue( IN PFRS_QUEUE Queue, IN PLIST_ENTRY Entry );
DWORD FrsRtlWaitForQueueFull( IN PFRS_QUEUE Queue, IN DWORD dwMilliseconds );
DWORD FrsRtlInsertTailQueue( IN PFRS_QUEUE Queue, IN PLIST_ENTRY Item );
DWORD FrsRtlInsertHeadQueue( IN PFRS_QUEUE Queue, IN PLIST_ENTRY Item );
VOID FrsRtlRunDownQueue( IN PFRS_QUEUE Queue, OUT PLIST_ENTRY ListHead );
#define FrsRtlAcquireQueueLock(_pQueue_) \
EnterCriticalSection(&(((_pQueue_)->Control)->Lock))
#define FrsRtlReleaseQueueLock(_pQueue_) \
LeaveCriticalSection(&(((_pQueue_)->Control)->Lock))
#define FrsRtlCountQueue(_pQueue_) \
(((_pQueue_)->Control)->ControlCount)
#define FrsRtlCountSubQueue(_pQueue_) \
((_pQueue_)->Count)
#define FrsRtlNoIdledQueues(_pQueue_) \
(IsListEmpty(&(((_pQueue_)->Control)->Idled)))
//
// The Lock suffix on the routines below means the user already has the
// queue lock.
//
VOID FrsRtlRemoveEntryQueueLock( IN PFRS_QUEUE Queue, IN PLIST_ENTRY Entry );
DWORD FrsRtlInsertTailQueueLock( IN PFRS_QUEUE Queue, IN PLIST_ENTRY Item );
DWORD FrsRtlInsertHeadQueueLock( IN PFRS_QUEUE Queue, IN PLIST_ENTRY Item );
//
// COMMAND SERVER
// A command server is a dynamic pool of threads and a controlled queue
// The default queue is set up as a controlled queue. Other
// controlled queues can be added in a server specific manner.
// A command server exports an initialize, abort, and none or more
// submit routines. The parameters and names of these functions is
// server specific. The consumers of a server's interface are intimate
// with the server.
//
typedef struct _COMMAND_SERVER COMMAND_SERVER, *PCOMMAND_SERVER; struct _COMMAND_SERVER { DWORD MaxThreads; // Max # of threads
DWORD FrsThreads; // current # of frs threads
DWORD Waiters; // current # of frs threads waiting
PWCHAR Name; // Thread's name
HANDLE Idle; // No active threads; no queue entries
DWORD (*Main)(PVOID); // Thread's entry point
FRS_QUEUE Control; // controlling queue
FRS_QUEUE Queue; // queue
};
//
// Interlocked list.
// *** WARNING *** To make the macros work properly for both lists and queues
// the first five items in FRS_LIST and FRS_QUEUE MUST match.
//
typedef struct _FRS_LIST FRS_LIST, *PFRS_LIST; struct _FRS_LIST { LIST_ENTRY ListHead; CRITICAL_SECTION Lock; DWORD Count; PFRS_LIST Control; DWORD ControlCount; };
DWORD FrsRtlInitializeList( PFRS_LIST List );
VOID FrsRtlDeleteList( PFRS_LIST List );
PLIST_ENTRY FrsRtlRemoveHeadList( IN PFRS_LIST List );
VOID FrsRtlInsertHeadList( IN PFRS_LIST List, IN PLIST_ENTRY Entry );
PLIST_ENTRY FrsRtlRemoveTailList( IN PFRS_LIST List );
VOID FrsRtlInsertTailList( IN PFRS_LIST List, IN PLIST_ENTRY Entry );
VOID FrsRtlRemoveEntryList( IN PFRS_LIST List, IN PLIST_ENTRY Entry );
#define FrsRtlAcquireListLock(_pList_) EnterCriticalSection(&(((_pList_)->Control)->Lock))
#define FrsRtlReleaseListLock(_pList_) LeaveCriticalSection(&(((_pList_)->Control)->Lock))
#define FrsRtlCountList(_pList_) (((_pList_)->Control)->ControlCount)
VOID FrsRtlRemoveEntryListLock( IN PFRS_LIST List, IN PLIST_ENTRY Entry );
VOID FrsRtlInsertTailListLock( IN PFRS_LIST List, IN PLIST_ENTRY Entry );
VOID FrsRtlInsertHeadListLock( IN PFRS_LIST List, IN PLIST_ENTRY Entry );
//VOID
//FrsRtlInsertBeforeEntryListLock(
// IN PFRS_LIST List,
// IN PLIST_ENTRY BeforeEntry
// IN PLIST_ENTRY NewEntry
// )
//
// Inserts newEntry before the BeforeEntry on the interlocked list (List).
// This is used to keep the list elements in ascending order by KeyValue.
//
// Assumes caller already has the list lock.
//
#define FrsRtlInsertBeforeEntryListLock( _List, _BeforeEntry, _NewEntry ) \
InsertTailList((_BeforeEntry), (_NewEntry)); \ (_List)->Count += 1; \ ((_List)->Control)->ControlCount += 1; \
//
// Walk thru an interlocked queue or list (_QUEUE_) with elements of type
// _TYPE_ and execute {_STMT_} for each one. The list entry in _TYPE_ is
// at _OFFSET_. Use pE in the statement body as a pointer to the entry.
// The entry may be removed from within the loop since we capture the
// link to the next entry before executing the loop body. You may also use
// 'continue' within the loop body because the assignment of nextentry to entry
// is in a comma expression inside the while test.
//
#define ForEachListEntry( _QUEUE_, _TYPE_, _OFFSET_, _STMT_ ) \
{ \ PLIST_ENTRY __Entry, __NextEntry; \ BOOL __Hold__=FALSE; \ _TYPE_ *pE; \ \ FrsRtlAcquireQueueLock(_QUEUE_); \ __NextEntry = GetListHead(&((_QUEUE_)->ListHead)); \ \ while (__Entry = __NextEntry, __Entry != &((_QUEUE_)->ListHead)) { \ \ __NextEntry = GetListNext(__Entry); \ pE = CONTAINING_RECORD(__Entry, _TYPE_, _OFFSET_); \ \ { _STMT_ } \ \ } \ \ if (!__Hold__) FrsRtlReleaseQueueLock(_QUEUE_); \ \ }
#define AquireListLock( _QUEUE_ ) FrsRtlAcquireListLock(_QUEUE_)
#define ReleaseListLock( _QUEUE_ ) FrsRtlReleaseListLock(_QUEUE_)
#define BreakAndHoldLock __Hold__ = TRUE; break
//
// Just like the above except the caller already has the list lock.
//
#define ForEachListEntryLock( _QUEUE_, _TYPE_, _OFFSET_, _STMT_ ) \
{ \ PLIST_ENTRY __Entry, __NextEntry; \ _TYPE_ *pE; \ \ __NextEntry = GetListHead(&((_QUEUE_)->ListHead)); \ \ while (__Entry = __NextEntry, __Entry != &((_QUEUE_)->ListHead)) { \ \ __NextEntry = GetListNext(__Entry); \ pE = CONTAINING_RECORD(__Entry, _TYPE_, _OFFSET_); \ \ { _STMT_ } \ \ } \ \ }
//
// Just like the above except pass in the address of the list head
// instead of using QUEUE->ListHEad.
//
#define ForEachSimpleListEntry( _HEAD_, _TYPE_, _OFFSET_, _STMT_ ) \
{ \ PLIST_ENTRY __Entry, __NextEntry; \ _TYPE_ *pE; \ \ __NextEntry = GetListHead(_HEAD_); \ \ while (__Entry = __NextEntry, __Entry != (_HEAD_)) { \ \ __NextEntry = GetListNext(__Entry); \ pE = CONTAINING_RECORD(__Entry, _TYPE_, _OFFSET_); \ \ { _STMT_ } \ \ } \ \ }
//VOID
//FrsRtlInsertQueueOrdered(
// IN PFRS_QUEUE List,
// IN PLIST_ENTRY NewEntry,
// IN <Entry-Data-Type>,
// IN <LIST_ENTRY-offset-name>,
// IN <Orderkey-Offset-name>,
// IN EventHandle or NULL
// )
//
// Inserts NewEntry on an ordered queue of <Entry-Data-Type> elements.
// The offset to the LIST_ENTRY in each element is <LIST_ENTRY-offset-name>
// The offset to the Ordering key (.eg. a ULONG) is <Orderkey-Offset-name>
// It acquires the List Lock.
// The list elements are kept in ascending order by KeyValue.
// If a new element is placed at the head of the queue and the EventHandle
// is non-NULL, the event is signalled.
//
//
#define FrsRtlInsertQueueOrdered( \
_QUEUE_, _NEWENTRY_, _TYPE_, _OFFSET_, _BY_, _EVENT_, _STATUS_) \ { \ BOOL __InsertDone = FALSE; \ BOOL __FirstOnQueue = TRUE; \ _STATUS_ = ERROR_SUCCESS; \ \ FrsRtlAcquireQueueLock(_QUEUE_); \ \ ForEachListEntryLock(_QUEUE_, _TYPE_, _OFFSET_, \ \ /* pE is loop iterator of type _TYPE_ */ \ \ if ((_NEWENTRY_)->_BY_ < pE->_BY_) { \ FrsRtlInsertBeforeEntryListLock( _QUEUE_, \ &pE->_OFFSET_, \ &((_NEWENTRY_)->_OFFSET_)); \ __InsertDone = TRUE; \ break; \ } \ \ __FirstOnQueue = FALSE; \ ); \ \ /* Handle new head or new tail case. If the queue was previously */ \ /* the insert will set the event. */ \ \ if (!__InsertDone) { \ if (__FirstOnQueue) { \ _STATUS_ = FrsRtlInsertHeadQueueLock(_QUEUE_, &((_NEWENTRY_)->_OFFSET_)); \ } else { \ _STATUS_ = FrsRtlInsertTailQueueLock(_QUEUE_, &((_NEWENTRY_)->_OFFSET_)); \ } \ } \ \ /* If this command became the new first one on the queue and the */ \ /* queue wasn't previously empty we have to set the event here to */ \ /* get the thread to readjust its wait time. */ \ \ if (__FirstOnQueue && \ (FrsRtlCountQueue(_QUEUE_) != 1)) { \ if (HANDLE_IS_VALID(_EVENT_)) { \ SetEvent(_EVENT_); \ } \ } \ \ FrsRtlReleaseQueueLock(_QUEUE_); \ \ }
//VOID
//FrsRtlInsertListOrdered(
// IN PFRS_LIST List,
// IN PLIST_ENTRY NewEntry,
// IN <Entry-Data-Type>,
// IN <LIST_ENTRY-offset-name>,
// IN <Orderkey-Offset-name>,
// IN EventHandle or NULL
// )
//
// Inserts NewEntry on an ordered list of <Entry-Data-Type> elements.
// The offset to the LIST_ENTRY in each element is <LIST_ENTRY-offset-name>
// The offset to the Ordering key (.eg. a ULONG) is <Orderkey-Offset-name>
// It acquires the List Lock.
// The list elements are kept in ascending order by KeyValue.
// If a new element is placed at the head of the queue and the EventHandle
// is non-NULL, the event is signalled.
//
//
#define FrsRtlInsertListOrdered( \
_FRSLIST_, _NEWENTRY_, _TYPE_, _OFFSET_, _BY_, _EVENT_) \ { \ BOOL __InsertDone = FALSE; \ BOOL __FirstOnList = TRUE; \ \ FrsRtlAcquireListLock(_FRSLIST_); \ \ ForEachListEntryLock(_FRSLIST_, _TYPE_, _OFFSET_, \ \ /* pE is loop iterator of type _TYPE_ */ \ \ if ((_NEWENTRY_)->_BY_ < pE->_BY_) { \ FrsRtlInsertBeforeEntryListLock( _FRSLIST_, \ &pE->_OFFSET_, \ &((_NEWENTRY_)->_OFFSET_)); \ __InsertDone = TRUE; \ break; \ } \ \ __FirstOnList = FALSE; \ ); \ \ /* Handle new head or new tail case. */ \ \ if (!__InsertDone) { \ if (__FirstOnList) { \ FrsRtlInsertHeadListLock(_FRSLIST_, &((_NEWENTRY_)->_OFFSET_)); \ } else { \ FrsRtlInsertTailListLock(_FRSLIST_, &((_NEWENTRY_)->_OFFSET_)); \ } \ } \ \ /* If this command became the new first one on the list */ \ /* we set the event here to get the thread to readjust its wait time.*/ \ \ if (__FirstOnList) { \ if (HANDLE_IS_VALID(_EVENT_)) { \ SetEvent(_EVENT_); \ } \ } \ \ FrsRtlReleaseListLock(_FRSLIST_); \ \ }
//
// Request counts are used as a simple means for tracking the number of
// command requests that are pending so the requestor can wait until
// all the commands have been processed.
//
typedef struct _FRS_REQUEST_COUNT FRS_REQUEST_COUNT, *PFRS_REQUEST_COUNT; struct _FRS_REQUEST_COUNT { CRITICAL_SECTION Lock; LONG Count; // Number of requests active
HANDLE Event; // Event set when count goes to zero.
ULONG Status; // Optional status return
};
#define FrsIncrementRequestCount(_RC_) \
EnterCriticalSection(&(_RC_)->Lock); \ (_RC_)->Count += 1; \ if ((_RC_)->Count == 1) { \ ResetEvent((_RC_)->Event); \ } \ LeaveCriticalSection(&(_RC_)->Lock);
#define FrsDecrementRequestCount(_RC_, _Status_) \
EnterCriticalSection(&(_RC_)->Lock); \ (_RC_)->Status |= _Status_; \ (_RC_)->Count -= 1; \ FRS_ASSERT((_RC_)->Count >= 0); \ if ((_RC_)->Count == 0) { \ SetEvent((_RC_)->Event); \ } \ LeaveCriticalSection(&(_RC_)->Lock);
ULONG FrsWaitOnRequestCount( IN PFRS_REQUEST_COUNT RequestCount, IN ULONG Timeout );
struct _COMMAND_PACKET; VOID FrsCompleteRequestCount( IN struct _COMMAND_PACKET *CmdPkt, IN PFRS_REQUEST_COUNT RequestCount );
VOID FrsCompleteRequestCountKeepPkt( IN struct _COMMAND_PACKET *CmdPkt, IN PFRS_REQUEST_COUNT RequestCount );
VOID FrsCompleteKeepPkt( IN struct _COMMAND_PACKET *CmdPkt, IN PVOID CompletionArg );
VOID FrsInitializeRequestCount( IN PFRS_REQUEST_COUNT RequestCount );
VOID FrsDeleteRequestCount( IN PFRS_REQUEST_COUNT RequestCount );
#define FrsInterlockedIncrement64(_Dest_, _Data_, _Lock_) \
EnterCriticalSection(_Lock_); \ _Data_ += (ULONGLONG) 1; \ _Dest_ = (_Data_); \ LeaveCriticalSection(_Lock_);
//
// ADVANCE_VALUE_INTERLOCKED(
// IN PULONG _dest,
// IN ULONG _newval
// )
// Advance the destination to the value given in newval atomically using
// interlocked exchange. _dest is never moved to a smaller value so this
// is a no-op if _newval is < _dest.
//
// *NOTE* Other operations on _dest MUST be done with interlocked ops like
// InterlockedIncrement to ensure that an incremented value is not lost if
// it occurs simultaneously on another processor.
//
#define ADVANCE_VALUE_INTERLOCKED(_dest, _newval) { \
ULONG CurVal, SaveCurVal, Result, *pDest = (_dest); \ CurVal = SaveCurVal = *pDest; \ while ((_newval) > CurVal) { \ Result = (ULONG)InterlockedCompareExchange((PLONG)pDest, (_newval), CurVal); \ if (Result == CurVal) { \ break; \ } \ CurVal = Result; \ } \ FRS_ASSERT(*pDest >= SaveCurVal); \ }
//
//
// Avoiding a torn quadword result (without a crit sect) when 1 thread is
// writing a quadord and another is reading the quadword, or,
// 2 threads are writing the same quadword.
//
// To do this in alpha we need an assembler routine to use load_locked / store_cond.
// To do this in x86 (per DaveC):
#if 0
if (USER_SHARED_DATA->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] == FALSE) { // code to use a crit section.
} else { // code to use inline assembly with cmpxchg8b.
} #endif
// KUSER_SHARED_DATA is defined in sdk\inc\ntxapi.h
// USER_SHARED_DATA is an arch specific typecast pointer to KUSER_SHARED_DATA.
// User shared data has a processor feature list with a cell for
// PF_COMPARE_EXCHANGE_DOUBLE that tells if the processor supports
// the cmpxchg8b instruction for x86. The 486 doesn't have it.
//
#define ReadQuadLock(_qw, _Lock) \
(EnterCriticalSection((_Lock)), *(_qw))
#define WriteQuadUnlock(_qw, _newval, _Lock) \
*(_qw) = (_newval); \ LeaveCriticalSection((_Lock))
#define AcquireQuadLock(_Lock) EnterCriticalSection((_Lock))
#define ReleaseQuadLock(_Lock) LeaveCriticalSection((_Lock))
//
// SET_FLAG_INTERLOCKED(
// IN PULONG _dest,
// IN ULONG _flags
// )
//
// *NOTE* Other operations on _dest MUST be done with interlocked ops like
// InterlockedIncrement to ensure that an incremented value is not lost if
// it occurs simultaneously on another processor.
//
#define SET_FLAG_INTERLOCKED(_dest, _flags) { \
ULONG CurVal, NewVal, Result, *pDest = (_dest); \ CurVal = *pDest; \ NewVal = (_flags) | CurVal; \ while ((NewVal) != CurVal) { \ Result = (ULONG)InterlockedCompareExchange((PLONG)pDest, NewVal, CurVal); \ if (Result == CurVal) { \ break; \ } \ CurVal = Result; \ NewVal = (_flags) | CurVal; \ } \ }
//
// CLEAR_FLAG_INTERLOCKED(
// IN PULONG _dest,
// IN ULONG _flags
// )
//
// *NOTE* Other operations on _dest MUST be done with interlocked ops like
// InterlockedIncrement to ensure that an incremented value is not lost if
// it occurs simultaneously on another processor.
//
#define CLEAR_FLAG_INTERLOCKED(_dest, _flags) { \
ULONG CurVal, NewVal, Result, *pDest = (_dest); \ CurVal = *pDest; \ NewVal = CurVal & ~(_flags); \ while ((NewVal) != CurVal) { \ Result = (ULONG)InterlockedCompareExchange((PLONG)pDest, NewVal, CurVal); \ if (Result == CurVal) { \ break; \ } \ CurVal = Result; \ NewVal = CurVal & ~(_flags); \ } \ }
#define FlagOn(Flags,SingleFlag) ((Flags) & (SingleFlag))
#define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)(((Flags) & (SingleFlag)) != 0))
#define SetFlag(_F,_SF) { \
(_F) |= (_SF); \ }
#define ClearFlag(_F,_SF) { \
(_F) &= ~(_SF); \ }
#define ValueIsMultOf2(_x_) (((ULONG_PTR)(_x_) & 0x00000001) == 0)
#define ValueIsMultOf4(_x_) (((ULONG_PTR)(_x_) & 0x00000003) == 0)
#define ValueIsMultOf8(_x_) (((ULONG_PTR)(_x_) & 0x00000007) == 0)
#define ValueIsMultOf16(_x_) (((ULONG_PTR)(_x_) & 0x0000000F) == 0)
#define ARRAY_SZ(_ar) (sizeof(_ar)/sizeof((_ar)[0]))
#define ARRAY_SZ2(_ar, _type) (sizeof(_ar)/sizeof(_type))
//
// This macros below take a pointer (or ulong) and return the value rounded
// up to the next aligned boundary.
//
#define WordAlign(Ptr) ((PVOID)((((ULONG_PTR)(Ptr)) + 1) & ~1))
#define LongAlign(Ptr) ((PVOID)((((ULONG_PTR)(Ptr)) + 3) & ~3))
#define QuadAlign(Ptr) ((PVOID)((((ULONG_PTR)(Ptr)) + 7) & ~7))
#define DblQuadAlign(Ptr) ((PVOID)((((ULONG_PTR)(Ptr)) + 15) & ~15))
#define QuadQuadAlign(Ptr) ((PVOID)((((ULONG_PTR)(Ptr)) + 31) & ~31))
#define QuadQuadAlignSize(Size) ((((ULONG)(Size)) + 31) & ~31)
//
// Check for a zero FILETIME.
//
#define FILETIME_IS_ZERO(_F_) \
((_F_.dwLowDateTime == 0) && (_F_.dwHighDateTime == 0))
//
// Convert a quad to two ULONGs for printing with format: %08x %08x
//
#define PRINTQUAD(__ARG__) (ULONG)((__ARG__)>>32) ,(ULONG)(__ARG__)
//
// Convert to printable cxtion path with format: %ws\%ws\%ws -> %ws\%ws
//
#define FORMAT_CXTION_PATH2 "%ws\\%ws\\%ws %ws %ws %ws"
#define FORMAT_CXTION_PATH2W L"%ws\\%ws\\%ws %ws %ws %ws"
#define PRINT_CXTION_PATH2(_REPLICA, _CXTION) \
(_REPLICA)->ReplicaName->Name, \ (_REPLICA)->MemberName->Name, \ (((_CXTION) != NULL) ? (_CXTION)->Name->Name : L"null"), \ (((_CXTION) != NULL) ? (((_CXTION)->Inbound) ? L"<-" : L"->") : \ L"?"), \ (((_CXTION) != NULL) ? (_CXTION)->PartSrvName : L"null"), \ (((_CXTION) != NULL) ? (((_CXTION)->JrnlCxtion) ? L"JrnlCxt" : L"RemoteCxt") : L"null")
#define PRINT_CXTION_PATH(_REPLICA, _CXTION) \
(_REPLICA)->ReplicaName->Name, \ (_REPLICA)->MemberName->Name, \ (((_CXTION) != NULL) ? (_CXTION)->Name->Name : L"null"), \ (((_CXTION) != NULL) ? (_CXTION)->Partner->Name : L"null"), \ (((_CXTION) != NULL) ? (_CXTION)->PartSrvName : L"null")
//
// Lower case
//
#define FRS_WCSLWR(_s_) \
{ \ if (_s_) { \ _wcslwr(_s_); \ } \ }
//
// Lock to protect the child lists in the Filter Table. (must be pwr of 2)
// Instead of paying the overhead of having one per node we just use an array
// to help reduce contention. We use the ReplicaNumber masked by the lock
// table size as the index.
//
// Acquire the lock on the ReplicaSet Filter table Child List before
// inserting or removing a child from the list.
//
#define NUMBER_FILTER_TABLE_CHILD_LOCKS 8
extern CRITICAL_SECTION JrnlFilterTableChildLock[NUMBER_FILTER_TABLE_CHILD_LOCKS];
#define FILTER_TABLE_CHILD_INDEX(_x_) \
((ULONG)((_x_)->ReplicaNumber) & (NUMBER_FILTER_TABLE_CHILD_LOCKS - 1))
#define JrnlAcquireChildLock(_replica_) EnterCriticalSection( \
&JrnlFilterTableChildLock[FILTER_TABLE_CHILD_INDEX(_replica_)] )
#define JrnlReleaseChildLock(_replica_) LeaveCriticalSection( \
&JrnlFilterTableChildLock[FILTER_TABLE_CHILD_INDEX(_replica_)] )
//
// Renaming a subtree from one replica set to another requires the child locks
// for both replica sets. Always get them in the same order (low to high)
// to avoid deadlock. Also check if the both use the same lock.
// Note: The caller must use JrnlReleaseChildLockPair() so the check for
// using the same lock can be repeated. Release in reverse order to avoid
// an extra context switch if another thread was waiting behind the first lock.
//
#define JrnlAcquireChildLockPair(_replica1_, _replica2_) \
{ \ ULONG Lx1, Lx2, Lxt; \ Lx1 = FILTER_TABLE_CHILD_INDEX(_replica1_); \ Lx2 = FILTER_TABLE_CHILD_INDEX(_replica2_); \ if (Lx1 > Lx2) { \ Lxt = Lx1; Lx1 = Lx2; Lx2 = Lxt; \ } \ EnterCriticalSection(&JrnlFilterTableChildLock[Lx1]); \ if (Lx1 != Lx2) { \ EnterCriticalSection(&JrnlFilterTableChildLock[Lx2]); \ } \ }
#define JrnlReleaseChildLockPair(_replica1_, _replica2_) \
{ \ ULONG Lx1, Lx2, Lxt; \ Lx1 = FILTER_TABLE_CHILD_INDEX(_replica1_); \ Lx2 = FILTER_TABLE_CHILD_INDEX(_replica2_); \ if (Lx1 < Lx2) { \ Lxt = Lx1; Lx1 = Lx2; Lx2 = Lxt; \ } \ LeaveCriticalSection(&JrnlFilterTableChildLock[Lx1]); \ if (Lx1 != Lx2) { \ LeaveCriticalSection(&JrnlFilterTableChildLock[Lx2]); \ } \ }
#ifdef __cplusplus
} #endif
ULONG FrsRunProcess( IN PWCHAR CommandLine, IN HANDLE StandardIn, IN HANDLE StandardOut, IN HANDLE StandardError );
VOID FrsFlagsToStr( IN DWORD Flags, IN PFLAG_NAME_TABLE NameTable, IN ULONG Length, OUT PSTR Buffer );
//
//######################### COMPRESSION OF STAGING FILE STARTS ###############
//
//
// The compressed chunk header is the structure that starts every
// new chunk in the compressed data stream. In our definition here
// we union it with a ushort to make setting and retrieving the chunk
// header easier. The header stores the size of the compressed chunk,
// its signature, and if the data stored in the chunk is compressed or
// not.
//
// Compressed Chunk Size:
//
// The actual size of a compressed chunk ranges from 4 bytes (2 byte
// header, 1 flag byte, and 1 literal byte) to 4098 bytes (2 byte
// header, and 4096 bytes of uncompressed data). The size is encoded
// in a 12 bit field biased by 3. A value of 1 corresponds to a chunk
// size of 4, 2 => 5, ..., 4095 => 4098. A value of zero is special
// because it denotes the ending chunk header.
//
// Chunk Signature:
//
// The only valid signature value is 3. This denotes a 4KB uncompressed
// chunk using with the 4/12 to 12/4 sliding offset/length encoding.
//
// Is Chunk Compressed:
//
// If the data in the chunk is compressed this field is 1 otherwise
// the data is uncompressed and this field is 0.
//
// The ending chunk header in a compressed buffer contains the a value of
// zero (space permitting).
//
typedef union _FRS_COMPRESSED_CHUNK_HEADER {
struct {
USHORT CompressedChunkSizeMinus3 : 12; USHORT ChunkSignature : 3; USHORT IsChunkCompressed : 1;
} Chunk;
USHORT Short;
} FRS_COMPRESSED_CHUNK_HEADER, *PFRS_COMPRESSED_CHUNK_HEADER;
typedef struct _FRS_DECOMPRESS_CONTEXT { DWORD BytesProcessed; } FRS_DECOMPRESS_CONTEXT, *PFRS_DECOMPRESS_CONTEXT;
#define FRS_MAX_CHUNKS_TO_DECOMPRESS 16
#define FRS_UNCOMPRESSED_CHUNK_SIZE 4096
//
//######################### COMPRESSION OF STAGING FILE ENDS ###############
//
//
// This context is used to send data to the callback functions for the RAW
// encrypt APIs.
//
typedef struct _FRS_ENCRYPT_DATA_CONTEXT {
PWCHAR StagePath; HANDLE StageHandle; LARGE_INTEGER RawEncryptedBytes;
} FRS_ENCRYPT_DATA_CONTEXT, *PFRS_ENCRYPT_DATA_CONTEXT;
|