windows-nt/Source/XPSP1/NT/ds/netapi/svcimgs/ntrepl/inc/replutil.h

1579 lines
48 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;