mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
12 KiB
477 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
uuid.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the core time and sequence number allocation
|
|
for UUIDs.
|
|
|
|
|
|
Author:
|
|
|
|
Mario Goertzel (MarioGo) 22-Nov-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "exp.h"
|
|
|
|
|
|
//
|
|
// Well known values
|
|
//
|
|
|
|
#define RPC_SEQUENCE_NUMBER_PATH L"\\Registry\\Machine\\Software\\Microsoft\\Rpc"
|
|
#define RPC_SEQUENCE_NUMBER_NAME L"UuidSequenceNumber"
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
|
|
LARGE_INTEGER ExpUuidLastTimeAllocated;
|
|
ULONG ExpUuidSequenceNumber;
|
|
BOOLEAN ExpUuidSequenceNumberValid;
|
|
BOOLEAN ExpUuidSequenceNumberNotSaved;
|
|
ERESOURCE ExpUuidLock;
|
|
ERESOURCE ExpSequenceLock;
|
|
|
|
//
|
|
// Helper functions to load and save the Uuid sequence number.
|
|
//
|
|
|
|
extern NTSTATUS ExpUuidLoadSequenceNumber(
|
|
OUT PULONG
|
|
);
|
|
|
|
extern NTSTATUS ExpUuidSaveSequenceNumber(
|
|
IN ULONG
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, ExpUuidLoadSequenceNumber)
|
|
#pragma alloc_text(PAGE, ExpUuidSaveSequenceNumber)
|
|
#pragma alloc_text(INIT, ExpUuidInitialization)
|
|
#pragma alloc_text(PAGE, NtAllocateUuids)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
ExpUuidLoadSequenceNumber(
|
|
OUT PULONG Sequence
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function loads the saved sequence number from the registry. If
|
|
no sequence number is found in the registry, it creates a 'random' one
|
|
and saves that in the registry.
|
|
|
|
This function is called only during system startup.
|
|
|
|
Arguments:
|
|
|
|
Sequence - Pointer to storage for the sequence number.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS when the sequence number is successfully read from the
|
|
registry.
|
|
|
|
STATUS_UNSUCCESSFUL when the sequence number is not correctly stored
|
|
in the registry.
|
|
|
|
Failure codes from ZwOpenKey() and ZwQueryValueKey() maybe returned.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyPath, KeyName;
|
|
HANDLE Key;
|
|
CHAR KeyValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
ULONG ResultLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
|
|
RtlInitUnicodeString(&KeyPath, RPC_SEQUENCE_NUMBER_PATH);
|
|
RtlInitUnicodeString(&KeyName, RPC_SEQUENCE_NUMBER_NAME);
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status =
|
|
ZwOpenKey( &Key,
|
|
GENERIC_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status =
|
|
ZwQueryValueKey( Key,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof(KeyValueBuffer),
|
|
&ResultLength
|
|
);
|
|
|
|
ZwClose( Key );
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if ( KeyValueInformation->Type == REG_DWORD &&
|
|
KeyValueInformation->DataLength == sizeof(ULONG)
|
|
) {
|
|
*Sequence = *(PULONG)KeyValueInformation->Data;
|
|
}
|
|
else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ExpUuidSaveSequenceNumber(
|
|
IN ULONG Sequence
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function save the uuid sequence number in the registry. This
|
|
value will be read by ExpUuidLoadSequenceNumber during the next boot.
|
|
|
|
Arguments:
|
|
|
|
Sequence - The sequence number to save.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
Failure codes from ZwOpenKey() and ZwSetValueKey() maybe returned.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyPath, KeyName;
|
|
HANDLE Key;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString(&KeyPath, RPC_SEQUENCE_NUMBER_PATH);
|
|
RtlInitUnicodeString(&KeyName, RPC_SEQUENCE_NUMBER_NAME);
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status =
|
|
ZwOpenKey( &Key,
|
|
GENERIC_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status =
|
|
ZwSetValueKey( Key,
|
|
&KeyName,
|
|
0,
|
|
REG_DWORD,
|
|
&Sequence,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
ZwClose( Key );
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ExpUuidInitialization (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the UUID allocation.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the initialization is successfully
|
|
completed. Otherwise, a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ExInitializeResource(&ExpUuidLock);
|
|
ExInitializeResource(&ExpSequenceLock);
|
|
|
|
ExpUuidSequenceNumberValid = FALSE;
|
|
|
|
// We can use the current time since we'll be changing the sequence number.
|
|
|
|
KeQuerySystemTime(&ExpUuidLastTimeAllocated);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtAllocateUuids (
|
|
OUT PULARGE_INTEGER Time,
|
|
OUT PULONG Range,
|
|
OUT PULONG Sequence
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reserves a range of time for the caller(s) to use for
|
|
handing out Uuids. As far a possible the same range of time and
|
|
sequence number will never be given out.
|
|
|
|
(It's possible to reboot 2^14-1 times and set the clock backwards and then
|
|
call this allocator and get a duplicate. Since only the low 14bits of the
|
|
sequence number are used in a real uuid.)
|
|
|
|
Arguments:
|
|
|
|
Time - Supplies the address of a variable that will receive the
|
|
start time (SYSTEMTIME format) of the range of time reserved.
|
|
|
|
Range - Supplies the address of a variable that will receive the
|
|
number of ticks (100ns) reserved after the value in Time.
|
|
The range reserved is *Time to (*Time + *Range - 1).
|
|
|
|
Sequence - Supplies the address of a variable that will receive
|
|
the time sequence number. This value is used with the associated
|
|
range of time to prevent problems with clocks going backwards.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS is returned if the service is successfully executed.
|
|
|
|
STATUS_RETRY is returned if we're unable to reserve a range of
|
|
UUIDs. This may (?) occur if system clock hasn't advanced
|
|
and the allocator is out of cached values.
|
|
|
|
STATUS_ACCESS_VIOLATION is returned if the output parameter for the
|
|
UUID cannot be written.
|
|
|
|
STATUS_UNSUCCESSFUL is returned if some other service reports
|
|
an error, most likly the registery.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KPROCESSOR_MODE PreviousMode;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER AvailableTime;
|
|
LARGE_INTEGER OutputTime;
|
|
ULONG OutputRange;
|
|
ULONG OutputSequence;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Establish an exception handler and attempt to write the output
|
|
// arguments. If the write attempt fails, then return
|
|
// the exception code as the service status. Otherwise return success
|
|
// as the service status.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Get previous processor mode and probe arguments if necessary.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
if (PreviousMode != KernelMode) {
|
|
ProbeForWrite((PVOID)Time, sizeof(LARGE_INTEGER), sizeof(ULONG));
|
|
ProbeForWrite((PVOID)Range, sizeof(ULONG), sizeof(ULONG));
|
|
ProbeForWrite((PVOID)Sequence, sizeof(ULONG), sizeof(ULONG));
|
|
}
|
|
}
|
|
except (ExSystemExceptionFilter())
|
|
{
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
ExAcquireResourceExclusive(&ExpUuidLock, TRUE);
|
|
|
|
//
|
|
// Make sure we have a valid sequence number. If not, make one up.
|
|
//
|
|
|
|
if (ExpUuidSequenceNumberValid == FALSE) {
|
|
Status = ExpUuidLoadSequenceNumber(&ExpUuidSequenceNumber);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
// Unable read the sequence number, this means we should make one up.
|
|
|
|
LARGE_INTEGER PerfCounter;
|
|
LARGE_INTEGER PerfFrequency;
|
|
|
|
// This should only happen when NtAllocateUuids() is called
|
|
// for the first time on a given machine. (machine, not boot)
|
|
|
|
KdPrint(("Uuid: Generating first sequence number.\n"));
|
|
|
|
PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
|
|
|
|
ExpUuidSequenceNumber ^= (ULONG)&Status ^ PerfCounter.LowPart ^
|
|
PerfCounter.HighPart ^ (ULONG)Sequence;
|
|
}
|
|
else {
|
|
// We increment the sequence number on every boot.
|
|
ExpUuidSequenceNumber++;
|
|
}
|
|
|
|
ExpUuidSequenceNumberValid = TRUE;
|
|
ExpUuidSequenceNumberNotSaved = TRUE;
|
|
}
|
|
|
|
//
|
|
// Get the current time, usually we will have plenty of avaliable
|
|
// to give the caller. But we may need to deal with time going
|
|
// backwards and really fast machines.
|
|
//
|
|
|
|
KeQuerySystemTime(&CurrentTime);
|
|
|
|
AvailableTime.QuadPart = CurrentTime.QuadPart - ExpUuidLastTimeAllocated.QuadPart;
|
|
|
|
if (AvailableTime.QuadPart < 0) {
|
|
|
|
// Time has been set time backwards. This means that we must make sure
|
|
// that somebody increments the sequence number and saves the new
|
|
// sequence number in the registry.
|
|
|
|
ExpUuidSequenceNumberNotSaved = TRUE;
|
|
ExpUuidSequenceNumber++;
|
|
|
|
// The sequence number has been changed, so it's now okay to set time
|
|
// backwards. Since time is going backwards anyway, it's okay to set
|
|
// it back an extra millisecond or two.
|
|
|
|
ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - 20000;
|
|
AvailableTime.QuadPart = 20000;
|
|
}
|
|
|
|
if (AvailableTime.QuadPart == 0) {
|
|
// System time hasn't moved. The caller should yield the CPU and retry.
|
|
ExReleaseResource(&ExpUuidLock);
|
|
return(STATUS_RETRY);
|
|
}
|
|
|
|
//
|
|
// Common case, time has moved forward.
|
|
//
|
|
|
|
if (AvailableTime.QuadPart > 10*1000*1000) {
|
|
// We never want to give out really old (> 1 second) Uuids.
|
|
AvailableTime.QuadPart = 10*1000*1000;
|
|
}
|
|
|
|
if (AvailableTime.QuadPart > 10000) {
|
|
// We've got over a millisecond to give out. We'll save some time for
|
|
// another caller so that we can avoid returning STATUS_RETRY very offen.
|
|
OutputRange = 10000;
|
|
AvailableTime.QuadPart -= 10000;
|
|
}
|
|
else {
|
|
// Not much time avaiable, give it all away.
|
|
OutputRange = (ULONG)AvailableTime.QuadPart;
|
|
AvailableTime.QuadPart = 0;
|
|
}
|
|
|
|
OutputTime.QuadPart = CurrentTime.QuadPart - (OutputRange + AvailableTime.QuadPart);
|
|
|
|
ExpUuidLastTimeAllocated.QuadPart = OutputTime.QuadPart + OutputRange;
|
|
|
|
// Last time allocated is just after the range we hand back to the caller
|
|
// this may be almost a second behind the true system time.
|
|
|
|
OutputSequence = ExpUuidSequenceNumber;
|
|
|
|
ExReleaseResource(&ExpUuidLock);
|
|
|
|
// Saving the sequence number will usually complete without any problems.
|
|
// So we let any other threads go at this point. If the save fails,
|
|
// we'll retry on some future call.
|
|
|
|
if (ExpUuidSequenceNumberNotSaved == TRUE) {
|
|
if (ExAcquireResourceExclusive(&ExpSequenceLock, FALSE) == TRUE) {
|
|
if (ExpUuidSequenceNumberNotSaved == TRUE) {
|
|
|
|
ExpUuidSequenceNumberNotSaved = FALSE;
|
|
|
|
// Print this message just to make sure we aren't hitting the
|
|
// registry too much under normal usage.
|
|
|
|
KdPrint(("Uuid: Saving new sequence number.\n"));
|
|
|
|
Status = ExpUuidSaveSequenceNumber(ExpUuidSequenceNumber);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExpUuidSequenceNumberNotSaved = TRUE;
|
|
}
|
|
}
|
|
}
|
|
ExReleaseResource(&ExpSequenceLock);
|
|
}
|
|
|
|
//
|
|
// Attempt to store the result of this call into the output parameters.
|
|
// This is done within an exception handler in case output parameters
|
|
// are now invalid.
|
|
//
|
|
|
|
try {
|
|
Time->QuadPart = OutputTime.QuadPart;
|
|
*Range = OutputRange;
|
|
*Sequence = OutputSequence;
|
|
}
|
|
except (ExSystemExceptionFilter()) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|