1579 lines
48 KiB
C
1579 lines
48 KiB
C
/*++
|
||
|
||
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;
|
||
|
||
|