diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/ex/uuid.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/ex/uuid.c')
-rw-r--r-- | private/ntos/ex/uuid.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/private/ntos/ex/uuid.c b/private/ntos/ex/uuid.c new file mode 100644 index 000000000..2d84c304c --- /dev/null +++ b/private/ntos/ex/uuid.c @@ -0,0 +1,477 @@ +/*++ + +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); +} |