4.1.11.3 Server Behavior of the IDL_DRSGetNT4ChangeLog Method
Informative summary of behavior: If the server is the PDC emulator FSMO role owner, it returns either a sequence of PDC change log entries or the NT4 replication state, or both, as requested by the client.
Multiple calls of this method might be required to retrieve the entire PDC change log. The client passes pRestart = null on the first call in a series of calls; the server returns a sequence of change log entries, including the first, a pointer to an opaque cookie, and a result code. If the server returns no change log entries, it returns null instead of a pointer to a cookie. If the server returns the result code zero, the sequence of change log entries in the response includes the final entry in the log.
The cookie encodes the serial number of the last change log entry returned. If the server returns ERROR_MORE_DATA, the final change log entry in the response was not the final entry in the change log. The client can make another call, with pRestart pointing to the cookie. The server processes this call identically to a call with pRestart = null, except that it returns change log entries starting with the entry following the last previously returned entry, as indicated by the cookie. By making enough calls the client can retrieve the entire change log.
If the client includes a cookie that is either corrupted or identifies a nonexistent change log entry (possibly because the cookie is too old), the server returns ERROR_INVALID_PARAMETER. If there are change log entries to return, but the client specifies a bound on the size of the returned change log entries that is too small to hold even a single entry, the server returns ERROR_INSUFFICIENT_BUFFER.
The NT4 replication state is a small, fixed-size structure and the server simply copies it into the response.
When the client requests both the PDC change log and the NT4 replication state, the server processes the PDC change log request first. If an error occurs during this processing the server does not process the request for NT4 replication state. If an error occurs while processing the NT4 replication state request, the server returns no indication to the client that the PDC change log request succeeded.
-
ULONG IDL_DRSGetNT4ChangeLog( [in, ref] DRS_HANDLE hDrs, [in] DWORD dwInVersion, [in, ref, switch_is(dwInVersion)] DRS_MSG_NT4_CHGLOG_REQ *pmsgIn, [out, ref] DWORD *pdwOutVersion, [out, ref, switch_is(*pdwOutVersion)] DRS_MSG_NT4_CHGLOG_REPLY *pmsgOut) msgIn: DRS_MSG_NT4_CHGLOG_REQ_V1 readStatus, ntStatus: DWORD sequenceNumber: integer nextIndexToBeReturned, lastIndexToBeReturned: integer lastReturnedSerialNumber: LONGLONG lastReturnedIndex: integer pChangeLog: ADDRESS OF CHANGE_LOG_ENTRIES ValidateDRSInput(hDrs, 11) pdwOutVersion^ := 1 pmsgOut^.V1.cbRestart := 0 pmsgOut^.V1.cbLog := 0 pmsgOut^.V1.ReplicationState.SamSerialNumber := 0 pmsgOut^.V1.ReplicationState.SamCreationTime := 0 pmsgOut^.V1.ReplicationState.BuiltinSerialNumber := 0 pmsgOut^.V1.ReplicationState.BuiltinCreationTime := 0 pmsgOut^.V1.ReplicationState.LsaSerialNumber := 0 pmsgOut^.V1.ReplicationState.LsaCreationTime := 0 pmsgOut^.V1.ActualNtStatus := 0 pmsgOut^.V1.pRestart := null pmsgOut^.V1.pLog := null /* Validate the request version */ if dwInVersion ≠ 1 then return ERROR_DS_DRA_INVALID_PARAMETER endif msgIn := pmsgIn^.V1 /* Access check */ if not AccessCheckCAR(DefaultNC(), DS-Replication-Get-Changes) then return ERR0R_ACCESS_DENIED endif /* The DC must own the PDC role */ if not IsPDC() then return ERROR_INVALID_DOMAIN_ROLE endif ntStatus := 0 readStatus := 0 if DRS_NT4_CHGLOG_GET_CHANGE_LOG in msgIn.dwFlags then /* Return NT4 change log entries. */ /* Determine the position of the first entry in the change log that * needs to be returned. If pRestart = null, this is the first * entry of the change log, otherwise it is the entry following the * entry identified in the cookie pRestart^. */ if msgIn.pRestart = null then sequenceNumber := 1 nextIndexToBeReturned := 0 else sequenceNumber := (Sequence number extracted from msgIn.pRestart^) + 1 lastReturnedSerialNumber := Serial number extracted from msgIn.pRestart^ lastReturnedIndex := select one i in dc.pdcChangeLog where dc.pdcChangeLog[i].SerialNumber = lastReturnedSerialNumber if lastReturnedIndex = null then /* Cookie is old or corrupted. * The STATUS code STATUS_INVALID_PARAMETER corresponds to * the Windows error code ERROR_INVALID_PARAMETER. */ ntStatus := STATUS_INVALID_PARAMETER else nextIndexToBeReturned := lastReturnedIndex + 1 endif endif if ntStatus = 0 and nextIndexToBeReturned ≥ dc.pdcChangeLog.length then /* No entries to be returned, complete the response message */ pmsgOut^.V1.pLog := null pmsgOut^.V1.cbLog := 0 pmsgOut^.V1.pRestart := null pmsgOut^.V1.cbRestart := 0 endif if ntStatus = 0 and nextIndexToBeReturned < dc.pdcChangeLog.length then /* Entries to be returned. First, determine how many entries fit * into the response message */ lastIndexToBeReturned := the largest integer q such that q < dc.pdcChangeLog.length and the size in bytes of dc.pdcChangeLog[nextIndexToBeReturned .. q] is <= msgIn.PreferredMaximumLength if lastIndexToBeReturned < nextIndexToBeReturned then /* Client's PreferredMaximumLength is too small for a single * entry, so return no entries. * The STATUS code STATUS_BUFFER_TOO_SMALL corresponds to * the Windows error code ERROR_INSUFFICIENT_BUFFER. */ ntStatus := STATUS_BUFFER_TOO_SMALL else /* Client's PreferredMaximumLength is large enough for one or * more entries. Fill in pChangeLog^ from dc.pdcChangeLog */ pChangeLog^.Size := 0x00000010 pChangeLog^.Version := 0x00000001 pChangeLog^.SequenceNumber := sequenceNumber pChangeLog^.Flags := 0x00000000 pChangeLog^.ChangeLogEntries := dc.pdcChangeLog[nextIndexToBeReturned .. lastIndexToBeReturned] if a fatal error occurred while retrieving dc.pdcChangeLog then ntStatus := STATUS code of error that occurred, high-order bit set end endif if ntStatus = 0 then /* No errors, complete the response message */ pmsgOut^.V1.pLog := pChangeLog pmsgOut^.V1.cbLog := size in bytes of pmsgOut^.V1.pLog^ /* Construct a new cookie */ lastReturnedSerialNumber := dc.pdcChangeLog[lastIndexToBeReturned].SerialNumber pmsgOut^.V1.pRestart := ADDRESS OF implementation-specific struct encapsulating lastReturnedSerialNumber and sequenceNumber pmsgOut^.V1.cbRestart := size in bytes of pmsgOut^.V1.pRestart^ if lastIndexToBeReturned < dc.pdcChangeLog.length - 1 then /* There are more entries to be returned. * The STATUS code STATUS_MORE_ENTRIES corresponds to * the Windows error code ERROR_MORE_DATA. */ ntStatus := STATUS_MORE_ENTRIES endif endif /* Response complete */ endif /* Entries returned */ endif /* Processed change log request */ /* Save the status code from the previous operation */ readStatus := ntStatus if ntStatus < 0x80000000 and DRS_NT4_CHGLOG_GET_SERIAL_NUMBERS in msgIn.dwFlags then /* Return NT4 replication state. */ pmsgOut^.V1.ReplicationState.SamSerialNumber := dc.nt4ReplicationState.SamNT4ReplicationUSN pmsgOut^.V1.ReplicationState.SamCreationTime := dc.nt4ReplicationState.SamCreationTime pmsgOut^.V1.ReplicationState.BuiltinSerialNumber := dc.nt4ReplicationState.BuiltinNT4ReplicationUSN pmsgOut^.V1.ReplicationState.BuiltinCreationTime := dc.nt4ReplicationState.BuiltinCreationTime pmsgOut^.V1.ReplicationState.LsaSerialNumber := 1 pmsgOut^.V1.ReplicationState.LsaCreationTime := current time on the DC if a fatal error occurred while retrieving NT4 replication state then ntStatus := STATUS code of error that occurred, high-order bit set end endif if ntStatus < 0x80000000 then pmsgOut^.V1.ActualStatus := readStatus else pmsgOut^.V1.ActualStatus := ntStatus endif return GetWindowsErrorCode(ntStatus)