summaryrefslogtreecommitdiffstats
path: root/private/ntos/ex/uuid.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/ex/uuid.c
downloadNT4.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.c477
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);
+}