|
|
/*++
Module Name:
frsexts.cxx
Abstract:
Author:
Sudarshan Chitre (sudarc) 12-May-1999
Revision History:
12-May-1999 sudarc
--*/ #include "frsexts.h"
WINDBG_EXTENSION_APIS ExtensionApis; HANDLE ProcessHandle = 0; BOOL fKD = 0;
#define MAX_ARGS 4
//
// stuff not common to kernel-mode and user-mode DLLs
//
#undef DECLARE_API
#define DECLARE_API(s) \
VOID \ s( \ HANDLE hCurrentProcess, \ HANDLE hCurrentThread, \ DWORD dwCurrentPc, \ PWINDBG_EXTENSION_APIS pExtensionApis, \ LPSTR lpArgumentString \ )
#define INIT_DPRINTF() { if (!fKD) ExtensionApis = *pExtensionApis; ProcessHandle = hCurrentProcess; }
#define MIN(x, y) ((x) < (y)) ? x:y
// define our own operators new and delete, so that we do not have to include the crt
void * __cdecl ::operator new(size_t dwBytes) { void *p; p = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytes); return (p); }
void __cdecl ::operator delete (void *p) { HeapFree(GetProcessHeap(), 0, p); }
BOOL GetData(IN ULONG_PTR dwAddress, IN LPVOID ptr, IN ULONG size, IN PCSTR type ) { BOOL b; ULONG BytesRead; ULONG count;
if (fKD == 0) { return ReadProcessMemory(ProcessHandle, (LPVOID) dwAddress, ptr, size, 0); }
while( size > 0 ) { count = MIN( size, 3000 );
b = ReadMemory((ULONG) dwAddress, ptr, count, &BytesRead );
if (!b || BytesRead != count ) { if (NULL == type) { type = "unspecified" ; } return FALSE; }
dwAddress += count; size -= count; ptr = (LPVOID)((ULONG_PTR)ptr + count); }
return TRUE; }
char *Days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
VOID FileTimeToString( FILETIME *FileTime, PCHAR Buffer ) /*++
Routine Description:
Convert a FileTime (UTC time) to an ANSI date/time string in the local time zone.
Arguments:
Time - ptr to a FILETIME Str - a string of at least TIME_STRING_LENGTH bytes to receive the time.
Return Value:
None
--*/ {
FILETIME LocalFileTime; SYSTEMTIME SystemTime;
Buffer[0] = '\0'; strcpy(Buffer, "Time???"); if (FileTime->dwHighDateTime != 0 || FileTime->dwLowDateTime != 0) { if (!FileTimeToLocalFileTime(FileTime, &LocalFileTime) || !FileTimeToSystemTime(&LocalFileTime, &SystemTime)) { return; } sprintf( Buffer, "%s %s %2d, %4d %02d:%02d:%02d", Days[SystemTime.wDayOfWeek], Months[SystemTime.wMonth - 1], SystemTime.wDay, SystemTime.wYear, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond); } return; }
#define DMPGUID(_TEXT_,_Guid_) \
{ \ dprintf((_TEXT_)); \ dprintf("%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n", \ (_Guid_).Data1, \ (_Guid_).Data2, \ (_Guid_).Data3, \ (_Guid_).Data4[0], \ (_Guid_).Data4[1], \ (_Guid_).Data4[2], \ (_Guid_).Data4[3], \ (_Guid_).Data4[4], \ (_Guid_).Data4[5], \ (_Guid_).Data4[6], \ (_Guid_).Data4[7]); \ }
#define DMPPGUID(_TEXT_,_pGuid_) \
{ \ BOOL bDmpGuid; \ BYTE bufDmpGuid[sizeof(GUID)]; \ dprintf((_TEXT_)); \ bDmpGuid = GetData((ULONG)(_pGuid_), &bufDmpGuid, sizeof(bufDmpGuid), NULL);\ if ( !bDmpGuid ) { \ dprintf("<Error reading memory>\n"); \ } else { \ dprintf("%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n", \ ((GUID *)(bufDmpGuid))->Data1, \ ((GUID *)(bufDmpGuid))->Data2, \ ((GUID *)(bufDmpGuid))->Data3, \ ((GUID *)(bufDmpGuid))->Data4[0], \ ((GUID *)(bufDmpGuid))->Data4[1], \ ((GUID *)(bufDmpGuid))->Data4[2], \ ((GUID *)(bufDmpGuid))->Data4[3], \ ((GUID *)(bufDmpGuid))->Data4[4], \ ((GUID *)(bufDmpGuid))->Data4[5], \ ((GUID *)(bufDmpGuid))->Data4[6], \ ((GUID *)(bufDmpGuid))->Data4[7]); \ \ } \ }
#define DMPGNAME(_TEXT_,_pGname_) \
{ \ BOOL bDmpGname; \ BYTE bufDmpGname[sizeof(GNAME)];\ BYTE DmpName[MAX_PATH * 2]; \ UINT count = 0; \ bDmpGname = GetData((ULONG)(_pGname_), &bufDmpGname, sizeof(bufDmpGname), NULL);\ if ( !bDmpGname ) { \ dprintf((_TEXT_)); \ dprintf("<Error reading memory>\n"); \ } else { \ DMPPGUID((_TEXT_),((GNAME *)bufDmpGname)->Guid); \ dprintf((_TEXT_)); \ if (((GNAME *)bufDmpGname)->Name == NULL) { \ dprintf("<null>\n"); \ } else { \ while (count < MAX_PATH * 2 && bDmpGname) { \ bDmpGname = GetData((ULONG)(((GNAME *)bufDmpGname)->Name) + count, &DmpName[count], sizeof(BYTE), NULL);\ count++; \ } \ \ dprintf("%ws\n",(WCHAR *)DmpName); \ } \ } \ }
#define DMPQUAD(_TEXT_,_Fid_) \
{ \ dprintf((_TEXT_)); \ dprintf("%08x %08x\n",(ULONG)((_Fid_)>>32) ,(ULONG)(_Fid_)); \ }
#define DMPTIME(_TEXT_,_Time_) \
{ \ CHAR TimeStr[TIME_STRING_LENGTH]; \ FileTimeToString((FILETIME *)&(_Time_),TimeStr); \ dprintf((_TEXT_)); \ dprintf("%s\n",TimeStr); \ }
#define DMPSTRW(_TEXT_,_pStr_) \
{ \ UINT count = 0; \ BOOL bDmpStrW = TRUE; \ BYTE DmpStr[MAX_PATH * 2]; \ while (count < MAX_PATH * 2 && bDmpStrW) { \ bDmpStrW = GetData((ULONG)(_pStr_) + count, &DmpStr[count], sizeof(BYTE), NULL);\ count++; \ } \ dprintf((_TEXT_)); \ dprintf("%ws\n",(WCHAR *)DmpStr); \ } //
// Common print functions for all the data structures.
//
VOID do_coe( PCHANGE_ORDER_ENTRY ChangeOrder ) /*
typedef struct _CHANGE_ORDER_ENTRY_ { GENERIC_HASH_ENTRY_HEADER HashEntryHeader; // Change Order hash Table support
UNICODE_STRING UFileName; // Used in renames to make file name bigger
ULONG EntryFlags; // misc state flags. See below.
ULONG CoMorphGenCount; // for debugging.
//
// Change order process list management.
//
LIST_ENTRY ProcessList; // Link on the change order process list.
ULONG TimeToRun; // Time to process the change order.
ULONG EntryCreateTime; // Tick Count at entry create time.
SINGLE_LIST_ENTRY DupCoList; // Duplicate change order list.
//
//
ULONG DirNestingLevel; // Number levels file is down in tree.
ULONGLONG FileReferenceNumber; // File's FID
ULONGLONG ParentFileReferenceNumber; // File's parent FID
ULONGLONG OriginalParentFid; // For rename processing
ULONGLONG NewParentFid; // For rename processing
ULONGLONG NameConflictHashValue; // Key value for NameConflict table cleanup.
ULONG StreamLastMergeSeqNum; // Stream seq num of last Usn record merged with this CO.
PREPLICA_THREAD_CTX RtCtx; // For DB access during CO processing.
GUID *pParentGuid; // ptr to the File's parent Guid in CoCmd.
//
// The joinguid is a cxtion's session id and, in this case,
// is used to retry change orders that were accepted by
// the change order accept thread for a cxtion that has since
// unjoined from its partner. The change orders for previous
// sessions are retried because they are out-of-order wrt the
// change orders for the current session id. In other words,
// order is maintained per session by coordinating the partners
// at join time.
GUID JoinGuid; // Cxtion's session id
// undefined if local co
//
// Remote and control change orders are associated with a cxtion.
// If this field is non-null, then the field
// ChangeOrderCount has been incremente for this change
// order. The count should be decremented when the
// change order is freed in ChgOrdIssueCleanup().
//
PCXTION Cxtion; // NULL if local co
//
// Issue cleanup flags -- As a change order is processed it acquires
// various resources that must be released when it retires or goes thru
// retry. The ISCU flag bits below are used to set these bits. Note:
// Not all bits may be set here. Some may get set just before the CO goes
// thru cleanup.
//
ULONG IssueCleanup;
//
// Needed to dampen basic info changes (e.g., resetting the archive bit)
// Copied from the idtable entry when the change order is created and
// used to update the change order when the change order is retired.
//
ULONG FileAttributes; LARGE_INTEGER FileCreateTime; LARGE_INTEGER FileWriteTime;
//
// Change order command parameters.
// (must be last since it ends with FileName)
//
CHANGE_ORDER_COMMAND Cmd;
} CHANGE_ORDER_ENTRY, *PCHANGE_ORDER_ENTRY;
*/ { PWCHAR FileName = NULL;
dprintf("Dumping CHANGE_ORDER_ENTRY.\n\n"); dprintf("HashEntryHeader : Address( 0x%x )\n",ChangeOrder->HashEntryHeader);
dprintf("FileNameLength : %d\n",ChangeOrder->UFileName.Length);
FileName = new WCHAR((ChangeOrder->UFileName.Length)/2 + 1);
GetData((ULONG)(ChangeOrder->UFileName.Buffer),(BYTE*)FileName,ChangeOrder->UFileName.Length,NULL); FileName[(ChangeOrder->UFileName.Length)/2] = L'\0';
dprintf("FileName : Address( 0x%x )\n",ChangeOrder->UFileName.Buffer); dprintf("FileName : %ws\n",FileName); delete FileName; dprintf("EntryFlags : 0x%x\n",ChangeOrder->EntryFlags); dprintf("CoMorphGenCount : %d\n",ChangeOrder->CoMorphGenCount); dprintf("ProcessList : Address( 0x%x )\n",ChangeOrder->ProcessList); dprintf("TimeToRun : %d\n",ChangeOrder->TimeToRun); dprintf("EntryCreateTime : %d\n",ChangeOrder->EntryCreateTime); dprintf("DupCoList : Address( 0x%x )\n",ChangeOrder->DupCoList); dprintf("DirNestingLevel : %d\n",ChangeOrder->DirNestingLevel); DMPQUAD("FileReferenceNumber : ",ChangeOrder->FileReferenceNumber); DMPQUAD("ParentFileReferenceNumber : ",ChangeOrder->ParentFileReferenceNumber); DMPQUAD("OriginalParentFid : ",ChangeOrder->OriginalParentFid); DMPQUAD("NewParentFid : ",ChangeOrder->NewParentFid); DMPQUAD("NameConflictHashValue : ",ChangeOrder->NameConflictHashValue); dprintf("StreamLastMergeSeqNum : %d\n",ChangeOrder->StreamLastMergeSeqNum); dprintf("RtCtx : Address( 0x%x )\n",ChangeOrder->RtCtx); DMPPGUID("pParentGuid : ",ChangeOrder->pParentGuid); DMPGUID("JoinGuid : ",ChangeOrder->JoinGuid); dprintf("Cxtion : Address( 0x%x )\n",ChangeOrder->Cxtion); dprintf("IssueCleanup : 0x%x\n",ChangeOrder->IssueCleanup); dprintf("FileAttributes : 0x%x\n",ChangeOrder->FileAttributes); DMPTIME("FileCreateTime : ",ChangeOrder->FileCreateTime); DMPTIME("FileWriteTime : ",ChangeOrder->FileWriteTime); dprintf("Cmd : Address( 0x%x )\n",ChangeOrder->Cmd); }
VOID do_coc( PCHANGE_ORDER_COMMAND Cmd ) /*
typedef struct _CHANGE_ORDER_RECORD_ { ULONG SequenceNumber; // Unique sequence number for change order.
ULONG Flags; // Change order flags
ULONG IFlags; // These flags can ONLY be updated with interlocked exchange.
ULONG State; // State is sep DWORD to avoid locking.
ULONG ContentCmd; // File content changes from UsnReason
union { ULONG LocationCmd; CO_LOCATION_CMD Field; // File Location command
} Lcmd;
ULONG FileAttributes; ULONG FileVersionNumber; // The file version number, inc on each close.
ULONG PartnerAckSeqNumber; // Save seq number for Partner Ack.
ULONGLONG FileSize; ULONGLONG FileOffset; // The current committed progress for staging file.
ULONGLONG FrsVsn; // Originator Volume sequence number
bugbug("perf: FileUsn and JrnlUsn can probably be combined") USN FileUsn; // The USN of the file must match on the Fetch request.
USN JrnlUsn; // USN of last journal record contributing to this CO.
USN JrnlFirstUsn; // USN of first journal record contributing to this CO.
struct _REPLICA *OriginalReplica; // Contains Replica ID when in DB
struct _REPLICA *NewReplica; // Contains Replica ID when in DB
GUID ChangeOrderGuid; // Guid that identifies the change order everywhere.
GUID OriginatorGuid; // The GUID of the originating member
GUID FileGuid; // The obj ID of the file
GUID OldParentGuid; // The Obj ID of the file's original parent directory
GUID NewParentGuid; // The Obj ID of the file's current parent directory
GUID CxtionGuid; // The obj ID of remote CO connection.
ULONGLONG Spare1Ull; ULONGLONG Spare2Ull; GUID Spare1Guid; GUID Spare2Guid; PWCHAR Spare1Wcs; PWCHAR Spare2Wcs; PVOID Spare1Bin; PVOID Spare2Bin;
LARGE_INTEGER EventTime; // The USN Journal Entry Timestamp.
USHORT FileNameLength; WCHAR FileName[MAX_PATH+1]; // The file name. (Must be Last)
} CHANGE_ORDER_COMMAND, *PCHANGE_ORDER_COMMAND, */ { PWCHAR FileName = NULL;
dprintf("Dumping CHANGE_ORDER_COMMAND.\n\n"); dprintf("SequenceNumber : %d\n",Cmd->SequenceNumber); dprintf("Flags : 0x%x\n",Cmd->Flags); dprintf("IFlags : 0x%x\n",Cmd->IFlags); dprintf("State : %d\n",Cmd->State); dprintf("ContentCmd : 0x%x\n",Cmd->ContentCmd); dprintf("LocationCmd : 0x%x\n",Cmd->Lcmd.LocationCmd); dprintf("FileAttributes : 0x%x\n",Cmd->FileAttributes); dprintf("FileVersionNumber : %d\n",Cmd->FileVersionNumber); dprintf("PartnerAckSeqNumber : %d\n",Cmd->PartnerAckSeqNumber); DMPQUAD("FileSize : ",Cmd->FileSize); DMPQUAD("FileOffset : ",Cmd->FileOffset); DMPQUAD("FrsVsn : ",Cmd->FrsVsn); DMPQUAD("FileUsn : ",Cmd->FileUsn); DMPQUAD("JrnlUsn : ",Cmd->JrnlUsn); DMPQUAD("JrnlFirstUsn : ",Cmd->JrnlFirstUsn); dprintf("OriginalReplicaNum : %d\n",Cmd->OriginalReplicaNum); dprintf("NewReplicaNum : %d\n",Cmd->NewReplicaNum); DMPGUID("ChangeOrderGuid : ",Cmd->ChangeOrderGuid); DMPGUID("OriginatorGuid : ",Cmd->OriginatorGuid); DMPGUID("FileGuid : ",Cmd->FileGuid); DMPGUID("OldParentGuid : ",Cmd->OldParentGuid); DMPGUID("NewParentGuid : ",Cmd->NewParentGuid); DMPGUID("CxtionGuid : ",Cmd->CxtionGuid); DMPTIME("EventTime : ",Cmd->EventTime); dprintf("FileNameLength : %d\n",Cmd->FileNameLength); dprintf("FileName : %ws\n",Cmd->FileName); }
VOID do_cxt( PCXTION pCxtion ) /*
typedef struct _CXTION CXTION, *PCXTION; struct _CXTION { FRS_NODE_HEADER Header; // memory management
ULONG State; // Incore state
ULONG Flags; // misc flags
BOOL Inbound; // TRUE if inbound cxtion *
BOOL JrnlCxtion; // TRUE if this Cxtion struct is for the local NTFS Journal
PGNAME Name; // Cxtion name/guid from the DS *
PGNAME Partner; // Partner's name/guid from the DS *
PWCHAR PartnerDnsName; // partner's DNS name from the DS *
PWCHAR PartnerPrincName; // partner's server principle name *
PWCHAR PartnerSid; // partner's sid (string) *
PWCHAR PartSrvName; // Partner's server name
ULONG PartnerAuthLevel; // Authentication level *
PGEN_TABLE VVector; // partner's version vector
PSCHEDULE Schedule; // schedule *
ULONG TerminationCoSeqNum;// The Seq Num of most recent Termination CO inserted.
PCOMMAND_SERVER VvJoinCs; // command server for vvjoins
struct _COMMAND_PACKET *JoinCmd; // check join status; rejoin if needed
// NULL == no delayed cmd outstanding
ULONGLONG LastJoinTime; // The time of the last successful join on this cxtion.
GUID JoinGuid; // Unique id for this join
GUID ReplicaVersionGuid; // partner's originator guid
DWORD CommQueueIndex; // Comm layer queue for sending pkts
DWORD ChangeOrderCount; // remote/control change orders pending
PGEN_TABLE CoeTable; // table of idle change orders
struct _COMMAND_PACKET *CommTimeoutCmd; // Timeout (waitable timer) packet
DWORD UnjoinTrigger; // DBG force unjoin in # remote cos
DWORD UnjoinReset; // reset force unjoin trigger
PFRS_QUEUE CoProcessQueue; // If non-null then Unidle the queue when
// JOIN succeeds or fails.
ULONG CommPkts; // Number of comm pkts
ULONG Penalty; // Penalty in Milliseconds
PCOMM_PACKET ActiveJoinCommPkt; // Don't flood Q w/many join pkts
ULONG PartnerMajor; // From comm packet
ULONG PartnerMinor; // From comm packet
struct _OUT_LOG_PARTNER_ *OLCtx; // Outbound Log Context for this connection.
struct _HASHTABLEDATA_REPLICACONN *PerfRepConnData; // PERFMON counter data structure
}; */ { dprintf("Dumping CXTION.\n\n"); dprintf("Header : Address ( 0x%x )\n",pCxtion->Header); dprintf("State : %d\n",pCxtion->State); dprintf("Flags : 0x%x\n",pCxtion->Flags); dprintf("Inbound : %d\n",pCxtion->Inbound); dprintf("JrnlCxtion : %d\n",pCxtion->JrnlCxtion); DMPGNAME("Name : ",pCxtion->Name); DMPGNAME("Partner : ",pCxtion->Partner); dprintf("PartnerDnsName : %ws\n",pCxtion->PartnerDnsName); dprintf("PartnerPrincName : %ws\n",pCxtion->PartnerPrincName); dprintf("PartnerSid : %ws\n",pCxtion->PartnerSid); dprintf("PartSrvName : %ws\n",pCxtion->PartSrvName); dprintf("PartnerAuthLevel : %d\n",pCxtion->PartnerAuthLevel); dprintf("VVector : Address ( 0x%x )\n",pCxtion->VVector); dprintf("Schedule : Address ( 0x%x )\n",pCxtion->Schedule); dprintf("TerminationCoSeqNum : %d\n",pCxtion->TerminationCoSeqNum); dprintf("VvJoinCs : Address ( 0x%x )\n",pCxtion->VvJoinCs); dprintf("JoinCmd : Address ( 0x%x )\n",pCxtion->JoinCmd); DMPTIME("LastJoinTime : ",pCxtion->LastJoinTime); DMPGUID("JoinGuid : ",pCxtion->JoinGuid); DMPGUID("ReplicaVersionGuid : ",pCxtion->ReplicaVersionGuid); dprintf("CommQueueIndex : %d\n",pCxtion->CommQueueIndex); dprintf("ChangeOrderCount : %d\n",pCxtion->ChangeOrderCount); dprintf("CoeTable : Address ( 0x%x )\n",pCxtion->CoeTable); dprintf("CommTimeoutCmd : Address ( 0x%x )\n",pCxtion->CommTimeoutCmd); dprintf("UnjoinTrigger : %d\n",pCxtion->UnjoinTrigger); dprintf("UnjoinReset : %d\n",pCxtion->UnjoinReset); dprintf("CoProcessQueue : Address ( 0x%x )\n",pCxtion->CoProcessQueue); dprintf("CommPkts : %d\n",pCxtion->CommPkts); dprintf("Penalty : %d\n",pCxtion->Penalty); dprintf("ActiveJoinCommPkt : Address ( 0x%x )\n",pCxtion->ActiveJoinCommPkt); dprintf("PartnerMajor : %d\n",pCxtion->PartnerMajor); dprintf("PartnerMinor : %d\n",pCxtion->PartnerMinor); dprintf("OLCtx : Address ( 0x%x )\n",pCxtion->OLCtx); dprintf("PerfRepConnData : Address ( 0x%x )\n",pCxtion->PerfRepConnData); }
VOID do_olp( POUT_LOG_PARTNER pOlp ) /*
typedef struct _OUT_LOG_PARTNER_ { FRS_NODE_HEADER Header; // Memory alloc
LIST_ENTRY List; // Link on the change order set list. (DONT MOVE)
ULONG Flags; // misc state flags. see below.
ULONG State; // Current state of this outbound partner.
SINGLE_LIST_ENTRY SaveList; // The link for the DB save list.
ULONG COLxRestart; // Restart point for Leading change order index.
ULONG COLxVVJoinDone; // COLx where VVJoin Finished and was rolled back.
ULONG COLx; // Leading change order index / sequence number.
ULONG COTx; // Trailing change order index / sequence number.
ULONG COTxLastSaved; // COTx value last saved in DB.
ULONG COTxNormalModeSave;// Saved Normal Mode COTx while in VV Join Mode.
ULONG COTslot; // Slot in Ack Vector corresponding to COTx.
ULONG OutstandingCos; // The current number of change orders outstanding.
ULONG OutstandingQuota; // The maximum number of COs outstanding.
ULONG AckVector[ACK_VECTOR_LONGS]; // The partner ack vector.
PCXTION Cxtion; // The partner connection. Has Guid and VVector.
} OUT_LOG_PARTNER, *POUT_LOG_PARTNER; */ { dprintf("Dumping OUT_LOG_PARTNER.\n\n"); dprintf("Header : Address ( 0x%x )\n",pOlp->Header); dprintf("List : Address ( 0x%x )\n",pOlp->List); dprintf("Flags : 0x%x\n",pOlp->Flags); dprintf("State : %d\n",pOlp->State); dprintf("SaveList : Address ( 0x%x )\n",pOlp->SaveList); dprintf("COLxRestart : %d\n",pOlp->COLxRestart); dprintf("COLxVVJoinDone : %d\n",pOlp->COLxVVJoinDone); dprintf("COLx : %d\n",pOlp->COLx); dprintf("COTx : %d\n",pOlp->COTx); dprintf("COTxLastSaved : %d\n",pOlp->COTxLastSaved); dprintf("COTxNormalModeSave : %d\n",pOlp->COTxNormalModeSave); dprintf("COTslot : %d\n",pOlp->COTslot); dprintf("OutstandingCos : %d\n",pOlp->OutstandingCos); dprintf("OutstandingQuota : %d\n",pOlp->OutstandingQuota); }
VOID do_rep( PREPLICA pReplica ) /*
typedef struct _REPLICA { FRS_NODE_HEADER Header; // memory management
CRITICAL_SECTION ReplicaLock; // protects filter list (for now)
ULONG ReferenceCount; ULONG CnfFlags; // From the config record
ULONG ReplicaSetType; // Type of replica set
BOOL Consistent; // replica is consistent
BOOL IsOpen; // database table is open
BOOL IsJournaling; // journal has been started
BOOL IsAccepting; // accepting comm requests
BOOL NeedsUpdate; // needs updating in the database
BOOL IsSeeding; // Seeding thread is deployed
BOOL IsSysvolReady; // SysvolReady is set to 1
LIST_ENTRY ReplicaList; // Link all replicas together
ULONG ServiceState; // stop, started, ...
FRS_ERROR_CODE FStatus; // error
PFRS_QUEUE Queue; // controlled by the command server
PGNAME ReplicaName; // Set name/Server guid from the DS
ULONG ReplicaNumber; // Internal id (name)
PGNAME MemberName; // Member name/guid from the DS
PGNAME SetName; // Set/guid name from the DS
GUID *ReplicaRootGuid; // guid assigned to Root dir
GUID ReplicaVersionGuid; // originator guid for version vector
PSCHEDULE Schedule; // schedule
PGEN_TABLE VVector; // Version vector
PGEN_TABLE Cxtions; // in/outbound cxtions
PWCHAR Root; // Root path
PWCHAR Stage; // Staging path
PWCHAR NewStage; // This maps to the current staging path in the
// DS. NewStage will be the one written to
// the config record but Stage will be used until
// next reboot.
PWCHAR Volume; // Volume??? bugbug
ULONGLONG MembershipExpires;// membership tombstone
ULONGLONG PreInstallFid; // For journal filtering.
TABLE_CTX ConfigTable; // Db table context
FRS_LIST ReplicaCtxListHead; // Links all open contexts on this replica set.
PWCHAR FileFilterList; // Raw file filter
PWCHAR DirFilterList; // Raw directory filter
LIST_ENTRY FileNameFilterHead; // Head of file name filter list.
LIST_ENTRY DirNameFilterHead; // Head of directory name filter list.
PQHASH_TABLE NameConflictTable; // Sequence COs using the same file name.
LONG InLogRetryCount; // Count of number CO needing a Retry.
ULONG InLogSeqNumber; // The last sequence number used in Inlog
//
//
// The inlog retry table tracks which retry change orders are currently
// active so we don't reissue the same change order until current
// invocation completes. This can happen when the system gets backed up
// and the change order retry thread kicks off again to issue retry COs
// before the last batch are able to finish. This state could be kept in
// the Inlog record but then it means extra writes to the DB.
// The sequence number is used to detect changes in the table when we don't
// have the lock. It is per-replica because it uses the change order
// sequence number of the inlog record and they aren't unique across
// replicas.
//
PQHASH_TABLE ActiveInlogRetryTable; union { struct { ULONG AIRSequenceNum; ULONG AIRSequenceNumSample; }; ULONGLONG QuadChunkA; };
//
// Status of sysvol seeding.
// Returned for NtFrsApi_Rpc_PromotionStatusW().
//
DWORD NtFrsApi_ServiceState; DWORD NtFrsApi_ServiceWStatus; #ifndef NOVVJOINHACK
DWORD NtFrsApi_HackCount; // temporary hack
#endif NOVVJOINHACK
PWCHAR NtFrsApi_ServiceDisplay;
//
// The Outbound log process state for this replica.
//
CRITICAL_SECTION OutLogLock; // protects the OutLog state
LIST_ENTRY OutLogEligible; // Eligible outbound log partners
LIST_ENTRY OutLogStandBy; // Partners ready to join eligible list
LIST_ENTRY OutLogActive; // Active outbound log partners
LIST_ENTRY OutLogInActive; // Inactive outbound log partners
PQHASH_TABLE OutLogRecordLock; // Sync access to outlog records.
ULONG OutLogSeqNumber; // The last sequence number used in Outlog
ULONG OutLogJLx; // The Joint Leading Index
ULONG OutLogJTx; // The Joint Trailing Index
ULONG OutLogCOMax; // The index of the Max change order in the log.
ULONG OutLogWorkState; // The output log current processing state.
struct _COMMAND_PACKET *OutLogCmdPkt; // Cmd pkt to queue when idle and have work.
PTABLE_CTX OutLogTableCtx; // Output Log Table context.
ULONG OutLogCountVVJoins; // Count of number of VVJoins in progress.
BOOL OutLogDoCleanup; // True means give log cleanup a run.
//
// The handle to the preinstall directory
//
HANDLE PreInstallHandle;
//
// The volume journal state for this replica.
//
GUID JrnlCxtionGuid; // Used as the Cxtion Guid for Local Cos
USN InlogCommitUsn; // Our current USN Journal commit point.
//USN JournalUsn; // The Journal USN for this replica.
USN JrnlRecoveryStart; // Point to start recovery.
USN JrnlRecoveryEnd; // Point where recovery is complete.
LIST_ENTRY RecoveryRefreshList; // List of file refresh req change orders.
LIST_ENTRY VolReplicaList; // Links all REPLICA structs on volume together.
USN LastUsnRecordProcessed; // Current Journal subsystem read USN.
LONG LocalCoQueueCount; // Count of number local COs in process queue
struct _VOLUME_MONITOR_ENTRY *pVme; // Ref to the VME for this Replica.
struct _HASHTABLEDATA_REPLICASET *PerfRepSetData; // PERFMON counter data structure
} REPLICA, *PREPLICA; */ { dprintf("Dumping REPLICA.\n\n"); dprintf("Header : Address ( 0x%x )\n",pReplica->Header); dprintf("ReplicaLock : Address ( 0x%x )\n",pReplica->ReplicaLock); dprintf("ReferenceCount : %d\n",pReplica->ReferenceCount); dprintf("CnfFlags : 0x%x\n",pReplica->CnfFlags); dprintf("ReplicaSetType : %d\n",pReplica->ReplicaSetType); dprintf("Consistent : %d\n",pReplica->Consistent); dprintf("IsOpen : %d\n",pReplica->IsOpen); dprintf("IsJournaling : %d\n",pReplica->IsJournaling); dprintf("IsAccepting : %d\n",pReplica->IsAccepting); dprintf("NeedsUpdate : %d\n",pReplica->NeedsUpdate); dprintf("IsSeeding : %d\n",pReplica->IsSeeding); dprintf("IsSysvolReady : %d\n",pReplica->IsSysvolReady); dprintf("ReplicaList : Address ( 0x%x )\n",pReplica->ReplicaList); dprintf("ServiceState : %d\n",pReplica->ServiceState); dprintf("FStatus : %d\n",pReplica->FStatus); dprintf("Queue : Address ( 0x%x )\n",pReplica->Queue); DMPGNAME("ReplicaName : ",pReplica->ReplicaName); dprintf("ReplicaNumber : %d\n",pReplica->ReplicaNumber); DMPGNAME("MemberName : ",pReplica->MemberName); DMPGNAME("SetName : ",pReplica->SetName); DMPPGUID("ReplicaRootGuid : ",pReplica->ReplicaRootGuid); DMPGUID("ReplicaVersionGuid : ",pReplica->ReplicaVersionGuid); dprintf("Schedule : Address ( 0x%x )\n",pReplica->Schedule); dprintf("VVector : Address ( 0x%x )\n",pReplica->VVector); dprintf("Cxtions : Address ( 0x%x )\n",pReplica->Cxtions); DMPSTRW("Root : ",pReplica->Root); DMPSTRW("Stage : ",pReplica->Stage); DMPSTRW("NewStage : ",pReplica->NewStage); DMPSTRW("Volume : ",pReplica->Volume); DMPTIME("MembershipExpires : ",pReplica->MembershipExpires); DMPQUAD("PreInstallFid : ",pReplica->PreInstallFid); dprintf("ConfigTable : Address ( 0x%x )\n",pReplica->ConfigTable); dprintf("ReplicaCtxListHead : Address ( 0x%x )\n",pReplica->ReplicaCtxListHead); DMPSTRW("FileFilterList : ",pReplica->FileFilterList); DMPSTRW("DirFilterList : ",pReplica->DirFilterList); dprintf("FileNameFilterHead : Address ( 0x%x )\n",pReplica->FileNameFilterHead); dprintf("DirNameFilterHead : Address ( 0x%x )\n",pReplica->DirNameFilterHead); dprintf("NameConflictTable : Address ( 0x%x )\n",pReplica->NameConflictTable); dprintf("InLogRetryCount : %d\n",pReplica->InLogRetryCount); dprintf("InLogSeqNumber : %d\n",pReplica->InLogSeqNumber); dprintf("ActiveInlogRetryTable : Address ( 0x%x )\n",pReplica->ActiveInlogRetryTable); dprintf("NtFrsApi_ServiceState : %d\n",pReplica->NtFrsApi_ServiceState); dprintf("NtFrsApi_ServiceWStatus : %d\n",pReplica->NtFrsApi_ServiceWStatus); DMPSTRW("NtFrsApi_ServiceDisplay : ",pReplica->NtFrsApi_ServiceDisplay); dprintf("OutLogLock : Address ( 0x%x )\n",pReplica->OutLogLock); dprintf("OutLogEligible : Address ( 0x%x )\n",pReplica->OutLogEligible); dprintf("OutLogStandBy : Address ( 0x%x )\n",pReplica->OutLogStandBy); dprintf("OutLogActive : Address ( 0x%x )\n",pReplica->OutLogActive); dprintf("OutLogInActive : Address ( 0x%x )\n",pReplica->OutLogInActive); dprintf("OutLogRecordLock : Address ( 0x%x )\n",pReplica->OutLogRecordLock); dprintf("OutLogSeqNumber : %d\n",pReplica->OutLogSeqNumber); dprintf("OutLogJLx : %d\n",pReplica->OutLogJLx); dprintf("OutLogJTx : %d\n",pReplica->OutLogJTx); dprintf("OutLogCOMax : %d\n",pReplica->OutLogCOMax); dprintf("OutLogWorkState : %d\n",pReplica->OutLogWorkState); dprintf("OutLogCmdPkt : Address ( 0x%x )\n",pReplica->OutLogCmdPkt); dprintf("OutLogTableCtx : Address ( 0x%x )\n",pReplica->OutLogTableCtx); dprintf("OutLogCountVVJoins : %d\n",pReplica->OutLogCountVVJoins); dprintf("OutLogDoCleanup : %d\n",pReplica->OutLogDoCleanup); dprintf("PreInstallHandle : Address ( 0x%x )\n",pReplica->PreInstallHandle); DMPGUID("JrnlCxtionGuid : ",pReplica->JrnlCxtionGuid); DMPQUAD("InlogCommitUsn : ",pReplica->InlogCommitUsn); DMPQUAD("JrnlRecoveryStart : ",pReplica->JrnlRecoveryStart); DMPQUAD("JrnlRecoveryEnd : ",pReplica->JrnlRecoveryEnd); dprintf("RecoveryRefreshList : Address ( 0x%x )\n",pReplica->RecoveryRefreshList); dprintf("VolReplicaList : Address ( 0x%x )\n",pReplica->VolReplicaList); DMPQUAD("LastUsnRecordProcessed : ",pReplica->LastUsnRecordProcessed); dprintf("LocalCoQueueCount : %d\n",pReplica->LocalCoQueueCount); dprintf("pVme : Address ( 0x%x )\n",pReplica->pVme); dprintf("PerfRepSetData : Address ( 0x%x )\n",pReplica->PerfRepSetData); }
VOID do_vme( PVOLUME_MONITOR_ENTRY pVme ) /*
typedef struct _VOLUME_MONITOR_ENTRY { FRS_NODE_HEADER Header; LIST_ENTRY ListEntry; // MUST FOLLOW HEADER
//
// This is the list head for all replica sets on the this volume. It links
// the REPLICA structs together.
//
FRS_LIST ReplicaListHead; // List of Replica Sets on Vol.
//
// The following USNs are for managing the NTFS USN journal on the volume.
//
USN JrnlRecoveryEnd; // Point where recovery is complete.
USN CurrentUsnRecord; // USN of record currently being processed.
USN CurrentUsnRecordDone; // USN of most recent record done processing.
USN LastUsnSavePoint; // USN of last vol wide save.
USN MonitorMaxProgressUsn; // Farthest progress made in this journal.
USN JrnlReadPoint; // The current active read point for journal.
USN_JOURNAL_DATA UsnJournalData; // FSCTL_QUERY_USN_JOURNAL data at journal open.
USN MonitorProgressUsn; // Start journal from here after pause.
USN ReplayUsn; // Start journal here after replica startup request
BOOL ReplayUsnValid; // above has valid data.
//
// The FrsVsn is a USN kept by FRS and exported by all replica sets on the
// volume. It is unaffected by disk reformats and is saved in the config
// record of each replica set. At startup we use the maximum value for all
// replica sets on a given volume. The only time they might differ is when
// service on a given replica set is not started.
//
ULONGLONG FrsVsn; // Private FRS volume seq num.
CRITICAL_SECTION Lock; // To sync access to VME.
CRITICAL_SECTION QuadWriteLock; // To sync updates to quadwords.
OVERLAPPED CancelOverlap; // Overlap struct for cancel req
ULONG WStatus; // Win32 status on error
ULONG ActiveReplicas; // Num replica sets active on journal
HANDLE Event; // Event handle for pause journal.
HANDLE VolumeHandle; // The vol handle for journal.
WCHAR DriveLetter[4]; // Drive letter for this volume.
//
// A change order table is kept on each volume to track the pending
// change orders. Tracking it for each replica set would be nice but
// that approach has a problem with renames that move files or dirs
// across replica sets on the volume. If there are prior change orders
// outstanding on a parent dir (MOVEOUT) in RS-A followed by a MOVEIN on
// a child file X to RS-B we must be sure the MOVEOUT on the parent happens
// before the MOVEIN on X. Similar problems arise with a MOVEOUT of file X
// followed by a MOVEIN to a different R.S. on the same volume. We need to
// locate the pending MOVEOUT change order on the volume or ensure it is
// processed first. One list per volume solves these problems.
//
PGENERIC_HASH_TABLE ChangeOrderTable;// The Replica Change Order table.
FRS_QUEUE ChangeOrderList; // Change order processing list head.
LIST_ENTRY UpdateList; // Link for the Replica Update Process Queue.
ULONG InitTime; // Time reference for the ChangeOrderList.
//
// THe Active Inbound Change Order table holds the change order structs
// indexed by File ID. An entry in the table means that we have an
// inbound (either local or remote) change order active on this file.
//
PGENERIC_HASH_TABLE ActiveInboundChangeOrderTable; //
// The ActiveChildren hash table is used to record the parent FID of each
// active change order. This is used to prevent a change order from starting
// on the parent while a change order is active on one or more children.
// For example if the child change order was a create and the parent change
// order was an ACL change to prevent further creates, we must ensure the
// child completes before starting the parent change order. Each entry has
// a count of the number of active children and a flag that is set if the
// change order process queue is blocked because of a pending change order
// on the parent. When the count goes to zero the queue is unblocked.
//
PQHASH_TABLE ActiveChildren; //
// The Parent Table is a simple hash table used to keep the parent File ID
// for each file and dir in any Replica Set on the volume. It is used in
// renames to find the old parent.
//
PQHASH_TABLE ParentFidTable; //
// The FRS Write Filter table filters out journal entries caused
// by file system write from the File Replication Service (Us) when we
// install files in the replica tree.
//
PQHASH_TABLE FrsWriteFilter; //
// The Recovery Conflict Table contains the FIDs of files that were in
// the inbound log when we crashed. At the start of recovery the inbound
// log for the given replica set is scanned and the FIDs are entered into
// the table. During journal processing any USN records with a matching
// FID are deemed to caused by FRS so we skip the record. (This is because
// the FrsWriteFilter table was lost in the crash).
PQHASH_TABLE RecoveryConflictTable;
//
// The name space table controls the merging of USN records into COs
// that use the same file name. If a name usage conflict exists in the
// USN record stream then we can't merge the USN record into a previous
// change order on the same file.
//
PQHASH_TABLE NameSpaceTable; ULONG StreamSequenceNumberFetched; ULONG StreamSequenceNumberClean; ULONG StreamSequenceNumber;
//
// The Filter Table contains an entry for each direcctory that is within a
// replica set on this volume. It is used to filter out Journal records for
// files/dirs that are not in a Replica set. For those Journal records that
// are in a replica set, a lookup on the parent FileId tells us which one.
//
PGENERIC_HASH_TABLE FilterTable; // THe directory filter table.
BOOL StopIo; // True means StopIo requested.
BOOL IoActive; // True means I/O active on volume.
ULONG JournalState; // Current journal state.
ULONG ReferenceCount; // Free all hash tables when it hits 0.
LONG ActiveIoRequests;// Number of Journal reads currently outstanding.
FILE_OBJECTID_BUFFER RootDirObjectId; // Object ID for volume
FILE_FS_VOLUME_INFORMATION FSVolInfo; // NT volume info.
CHAR FSVolLabel[MAXIMUM_VOLUME_LABEL_LENGTH];
} VOLUME_MONITOR_ENTRY, *PVOLUME_MONITOR_ENTRY; */ { dprintf("Dumping OUT_LOG_PARTNER.\n\n"); dprintf("Header : Address ( 0x%x )\n",pVme->Header); dprintf("ListEntry : Address ( 0x%x )\n",pVme->ListEntry); dprintf("WStatus : 0x%x\n",pVme->WStatus); dprintf("ActiveReplicas : %d\n" ,pVme->ActiveReplicas); dprintf("Event Handle for pause : 0x%x\n",pVme->Event); dprintf("VolumeHandle : 0x%x\n",pVme->VolumeHandle); dprintf("DriveLetter : %ws\n" ,pVme->DriveLetter); dprintf("StopIo (bool) : 0x%x\n",pVme->StopIo); dprintf("IoActive (bool) : 0x%x\n",pVme->IoActive); dprintf("JournalState : 0x%x\n",pVme->JournalState); dprintf("ReferenceCount : %d\n" ,pVme->ReferenceCount); dprintf("ActiveIoRequests : %d\n" ,pVme->ActiveIoRequests); dprintf("FSVolLabel : %s\n" ,pVme->FSVolLabel);
}
//
// Version info
//
#if DBG
USHORT SavedMajorVersion = 0x0c; #else
USHORT SavedMajorVersion; #endif
EXT_API_VERSION ApiVersion = { 3, 5, EXT_API_VERSION_NUMBER, 0 }; USHORT SavedMinorVersion = VER_PRODUCTBUILD; BOOL ChkTarget; // is debuggee a CHK build?
VOID WinDbgExtensionDllInit( PWINDBG_EXTENSION_APIS lpExtensionApis, USHORT MajorVersion, USHORT MinorVersion ) { fKD = 1; ExtensionApis = *lpExtensionApis ; SavedMajorVersion = MajorVersion; SavedMinorVersion = MinorVersion; ChkTarget = SavedMajorVersion == 0x0c ? TRUE : FALSE; }
DECLARE_API( help ) { INIT_DPRINTF();
if (lpArgumentString[0] == '\0') { dprintf("\n"); dprintf("FRS Debugger extensions help:\n\n"); dprintf("%20s - CHANGE_ORDER_ENTRY\n","coe"); dprintf("%20s - CHANGE_ORDER_COMMAND\n","coc"); dprintf("%20s - CXTION\n","cxt"); dprintf("%20s - OUT_LOG_PARTNER\n","olp"); dprintf("%20s - REPLICA\n","rep"); dprintf("%20s - VME\n","vme"); dprintf("\n"); } }
DECLARE_API( version ) { INIT_DPRINTF();
if (fKD) { dprintf( "FRS Extension dll for Build %d debugging %s kernel for Build %d\n", VER_PRODUCTBUILD, SavedMajorVersion == 0x0c ? "Checked" : "Free", SavedMinorVersion ); } else { dprintf( "FRS Extension dll for Build %d\n", VER_PRODUCTBUILD ); } }
DECLARE_API( coe ) { ULONG_PTR dwAddr;
INIT_DPRINTF();
dwAddr = GetExpression(lpArgumentString); if ( !dwAddr ) { dprintf("Error: can't evaluate '%s'\n", lpArgumentString); return; }
BOOL b; char block[sizeof(CHANGE_ORDER_ENTRY)];
b = GetData(dwAddr, &block, sizeof(block), NULL); if ( !b ) { dprintf("can't read %p, error 0x%lx\n", dwAddr, GetLastError()); return; }
do_coe((CHANGE_ORDER_ENTRY *) block); }
DECLARE_API( coc ) { ULONG_PTR dwAddr;
INIT_DPRINTF();
dwAddr = GetExpression(lpArgumentString); if ( !dwAddr ) { dprintf("Error: can't evaluate '%s'\n", lpArgumentString); return; }
BOOL b; char block[sizeof(CHANGE_ORDER_COMMAND)];
b = GetData(dwAddr, &block, sizeof(block), NULL); if ( !b ) { dprintf("can't read %p, error 0x%lx\n", dwAddr, GetLastError()); return; }
do_coc((CHANGE_ORDER_COMMAND *) block); }
DECLARE_API( cxt ) { ULONG_PTR dwAddr;
INIT_DPRINTF();
dwAddr = GetExpression(lpArgumentString); if ( !dwAddr ) { dprintf("Error: can't evaluate '%s'\n", lpArgumentString); return; }
BOOL b; char block[sizeof(CXTION)];
b = GetData(dwAddr, &block, sizeof(block), NULL); if ( !b ) { dprintf("can't read %p, error 0x%lx\n", dwAddr, GetLastError()); return; }
do_cxt((CXTION *) block); }
DECLARE_API( olp ) { ULONG_PTR dwAddr;
INIT_DPRINTF();
dwAddr = GetExpression(lpArgumentString); if ( !dwAddr ) { dprintf("Error: can't evaluate '%s'\n", lpArgumentString); return; }
BOOL b; char block[sizeof(OUT_LOG_PARTNER)];
b = GetData(dwAddr, &block, sizeof(block), NULL); if ( !b ) { dprintf("can't read %p, error 0x%lx\n", dwAddr, GetLastError()); return; }
do_olp((OUT_LOG_PARTNER *) block); }
DECLARE_API( vme ) { ULONG_PTR dwAddr;
INIT_DPRINTF();
dwAddr = GetExpression(lpArgumentString); if ( !dwAddr ) { dprintf("Error: can't evaluate '%s'\n", lpArgumentString); return; }
BOOL b; char block[sizeof(VOLUME_MONITOR_ENTRY)];
b = GetData(dwAddr, &block, sizeof(block), NULL); if ( !b ) { dprintf("can't read %p, error 0x%lx\n", dwAddr, GetLastError()); return; }
do_vme((VOLUME_MONITOR_ENTRY *) block); }
DECLARE_API( rep ) { ULONG_PTR dwAddr;
INIT_DPRINTF();
dwAddr = GetExpression(lpArgumentString); if ( !dwAddr ) { dprintf("Error: can't evaluate '%s'\n", lpArgumentString); return; }
BOOL b; char block[sizeof(REPLICA)];
b = GetData(dwAddr, &block, sizeof(block), NULL); if ( !b ) { dprintf("can't read %p, error 0x%lx\n", dwAddr, GetLastError()); return; }
do_rep((REPLICA *) block); }
LPEXT_API_VERSION ExtensionApiVersion( VOID ) { return &ApiVersion; }
|