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.
982 lines
27 KiB
982 lines
27 KiB
/*++
|
|
|
|
Copyright (c) 1994-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
uuid.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the core time and sequence number allocation
|
|
for UUIDs (exposed to user mode), as well as complete UUID
|
|
creation (exposed to kernel mode only).
|
|
|
|
(e.g. RPC Runtime) (e.g. NTFS)
|
|
| |
|
|
V V
|
|
NtAllocateUuids ExUuidCreate
|
|
| |
|
|
V V
|
|
| ExpUuidGetValues
|
|
| |
|
|
| |
|
|
+------> ExpAllocateUuids <----+
|
|
|
|
|
|
|
|
Author:
|
|
|
|
Mario Goertzel (MarioGo) 22-Nov-1994
|
|
|
|
Revision History:
|
|
|
|
MikeHill 17-Jan-96 Ported ExUuidCreate & ExpUuidGetValues from RPCRT4.
|
|
MazharM 17-Feb-98 Add PNP support
|
|
|
|
--*/
|
|
|
|
#include "exp.h"
|
|
|
|
|
|
|
|
//
|
|
// Well known values
|
|
//
|
|
|
|
// Registry info for the sequen number
|
|
#define RPC_SEQUENCE_NUMBER_PATH L"\\Registry\\Machine\\Software\\Microsoft\\Rpc"
|
|
#define RPC_SEQUENCE_NUMBER_NAME L"UuidSequenceNumber"
|
|
|
|
// Masks and constants to interpret the UUID
|
|
#define UUID_TIME_HIGH_MASK 0x0FFF
|
|
#define UUID_VERSION 0x1000
|
|
#define UUID_RESERVED 0x80
|
|
#define UUID_CLOCK_SEQ_HI_MASK 0x3F
|
|
|
|
// Values for ExpUuidCacheValid
|
|
#define CACHE_LOCAL_ONLY 0
|
|
#define CACHE_VALID 1
|
|
|
|
//
|
|
// Custom types
|
|
//
|
|
|
|
// An alternative data-template for a UUID, useful during generation.
|
|
typedef struct _UUID_GENERATE {
|
|
ULONG TimeLow;
|
|
USHORT TimeMid;
|
|
USHORT TimeHiAndVersion;
|
|
UCHAR ClockSeqHiAndReserved;
|
|
UCHAR ClockSeqLow;
|
|
UCHAR NodeId[6];
|
|
} UUID_GENERATE;
|
|
|
|
// A cache of allocated UUIDs
|
|
typedef struct _UUID_CACHED_VALUES_STRUCT {
|
|
ULONGLONG Time; // End time of allocation
|
|
LONG AllocatedCount; // Number of UUIDs allocated
|
|
UCHAR ClockSeqHiAndReserved;
|
|
UCHAR ClockSeqLow;
|
|
UCHAR NodeId[6];
|
|
} UUID_CACHED_VALUES_STRUCT;
|
|
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGEDATA")
|
|
#endif
|
|
|
|
// UUID cache information
|
|
LARGE_INTEGER ExpUuidLastTimeAllocated = {0,0};
|
|
BOOLEAN ExpUuidCacheValid = CACHE_LOCAL_ONLY;
|
|
|
|
// Make cache allocate UUIDs on first call.
|
|
// Time = 0. Allocated = -1, ..., multicast bit set in node id
|
|
UUID_CACHED_VALUES_STRUCT ExpUuidCachedValues = { 0, -1, 0, 0, { 0x80, 'n', 'o', 'n', 'i', 'c' }};
|
|
|
|
// UUID Sequence number information
|
|
ULONG ExpUuidSequenceNumber = 0;
|
|
BOOLEAN ExpUuidSequenceNumberValid = FALSE;
|
|
BOOLEAN ExpUuidSequenceNumberNotSaved = FALSE;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
// A lock to protect all of the above global data.
|
|
FAST_MUTEX ExpUuidLock;
|
|
|
|
//
|
|
// Code section allocations
|
|
//
|
|
|
|
extern NTSTATUS ExpUuidLoadSequenceNumber(
|
|
OUT PULONG
|
|
);
|
|
|
|
extern NTSTATUS ExpUuidSaveSequenceNumber(
|
|
IN ULONG
|
|
);
|
|
|
|
extern NTSTATUS ExpUuidSaveSequenceNumberIf ();
|
|
|
|
extern NTSTATUS ExpUuidGetValues(
|
|
OUT UUID_CACHED_VALUES_STRUCT *Values
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
NTSTATUS
|
|
ExpAllocateUuids (
|
|
OUT PLARGE_INTEGER Time,
|
|
OUT PULONG Range,
|
|
OUT PULONG Sequence
|
|
);
|
|
#pragma alloc_text(PAGE, ExpUuidLoadSequenceNumber)
|
|
#pragma alloc_text(PAGE, ExpUuidSaveSequenceNumber)
|
|
#pragma alloc_text(PAGE, ExpUuidSaveSequenceNumberIf)
|
|
#pragma alloc_text(INIT, ExpUuidInitialization)
|
|
#pragma alloc_text(PAGE, ExpAllocateUuids)
|
|
#pragma alloc_text(PAGE, NtAllocateUuids)
|
|
#pragma alloc_text(PAGE, NtSetUuidSeed)
|
|
#pragma alloc_text(PAGE, ExpUuidGetValues)
|
|
#pragma alloc_text(PAGE, ExUuidCreate)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
ExpUuidLoadSequenceNumber(
|
|
OUT PULONG Sequence
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function loads the saved sequence number from 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 | OBJ_KERNEL_HANDLE,
|
|
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 saves the uuid sequence number in the registry. This
|
|
value will be read by ExpUuidLoadSequenceNumber during the next boot.
|
|
|
|
This routine assumes that the current thread has exclusive access
|
|
to the the ExpUuid* values.
|
|
|
|
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 | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status =
|
|
ZwOpenKey( &Key,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status =
|
|
ZwSetValueKey( Key,
|
|
&KeyName,
|
|
0,
|
|
REG_DWORD,
|
|
&Sequence,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
ZwClose( Key );
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ExpUuidSaveSequenceNumberIf ()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function saves the ExpUuidSequenceNumber, but only
|
|
if necessary (as determined by the ExpUuidSequenceNumberNotSaved
|
|
flag).
|
|
|
|
This routine assumes that the current thread has exclusive access
|
|
to the ExpUuid* values.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the operation was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Does the sequence number need to be saved?
|
|
if (ExpUuidSequenceNumberNotSaved == TRUE) {
|
|
|
|
// Print this message just to make sure we aren't hitting the
|
|
// registry too much under normal usage.
|
|
|
|
KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL, "Uuid: Saving new sequence number.\n"));
|
|
|
|
// Save the sequence number
|
|
|
|
Status = ExpUuidSaveSequenceNumber(ExpUuidSequenceNumber);
|
|
|
|
// Indicate that it's now been saved.
|
|
if (NT_SUCCESS(Status)) {
|
|
ExpUuidSequenceNumberNotSaved = FALSE;
|
|
}
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ExInitializeFastMutex(&ExpUuidLock);
|
|
|
|
ExpUuidSequenceNumberValid = FALSE;
|
|
|
|
// We can use the current time since we'll be changing the sequence number.
|
|
|
|
KeQuerySystemTime(&ExpUuidLastTimeAllocated);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ExpAllocateUuids (
|
|
OUT PLARGE_INTEGER Time,
|
|
OUT PULONG Range,
|
|
OUT PULONG Sequence
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a sequence number and a range of times for a set of UUIDs.
|
|
The caller can use this together with the network address to
|
|
generate complete UUIDs.
|
|
|
|
This routine assumes that the current thread has exclusive access
|
|
to the ExpUuid* values.
|
|
|
|
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 will occur if system clock hasn't advanced
|
|
and the allocator is out of cached values.
|
|
|
|
STATUS_UNSUCCESSFUL is returned if some other service reports
|
|
an error, most likly the registery.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER AvailableTime;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// 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 we're called
|
|
// for the first time on a given machine. (machine, not boot)
|
|
|
|
KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL, "Uuid: Generating first sequence number.\n"));
|
|
|
|
PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
|
|
|
|
ExpUuidSequenceNumber ^= (ULONG)((ULONG_PTR)&Status) ^ PerfCounter.LowPart ^
|
|
PerfCounter.HighPart ^ (ULONG)((ULONG_PTR)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.
|
|
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 > 10*1000) {
|
|
// 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 often.
|
|
*Range = 10*1000;
|
|
AvailableTime.QuadPart -= 10*1000;
|
|
}
|
|
else {
|
|
// Not much time avaiable, give it all away.
|
|
*Range = (ULONG)AvailableTime.QuadPart;
|
|
AvailableTime.QuadPart = 0;
|
|
}
|
|
|
|
Time->QuadPart = CurrentTime.QuadPart - (*Range + AvailableTime.QuadPart);
|
|
|
|
ExpUuidLastTimeAllocated.QuadPart = Time->QuadPart + *Range;
|
|
|
|
// 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.
|
|
|
|
*Sequence = ExpUuidSequenceNumber;
|
|
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
#define SEED_SIZE 6 * sizeof(CHAR)
|
|
|
|
|
|
NTSTATUS
|
|
NtSetUuidSeed (
|
|
IN PCHAR Seed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to set the seed used for UUID generation. The seed
|
|
will be set by RPCSS at startup and each time a card is replaced.
|
|
|
|
Arguments:
|
|
|
|
Seed - Pointer to a six byte buffer
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS is returned if the service is successfully executed.
|
|
|
|
STATUS_ACCESS_DENIED If caller doesn't have the permissions to make this call.
|
|
You need to be logged on as Local System in order to call this API.
|
|
|
|
STATUS_ACCESS_VIOLATION is returned if the Seed could not be read.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
LUID AuthenticationId;
|
|
SECURITY_SUBJECT_CONTEXT SubjectContext;
|
|
LUID SystemLuid = SYSTEM_LUID;
|
|
BOOLEAN CapturedSubjectContext = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(KeGetPreviousMode() != KernelMode);
|
|
|
|
try {
|
|
//
|
|
// Check if the caller has the appropriate permission
|
|
//
|
|
SeCaptureSubjectContext(&SubjectContext);
|
|
CapturedSubjectContext = TRUE;
|
|
|
|
Status = SeQueryAuthenticationIdToken(
|
|
SeQuerySubjectContextToken(&SubjectContext),
|
|
&AuthenticationId);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExRaiseStatus(Status);
|
|
}
|
|
|
|
if (RtlCompareMemory(&AuthenticationId, &SystemLuid, sizeof(LUID)) != sizeof(LUID)) {
|
|
ExRaiseStatus(STATUS_ACCESS_DENIED);
|
|
}
|
|
|
|
//
|
|
// Store the UUID seed
|
|
//
|
|
ProbeForReadSmallStructure(Seed, SEED_SIZE, sizeof(CHAR));
|
|
RtlCopyMemory(&ExpUuidCachedValues.NodeId[0], Seed, SEED_SIZE);
|
|
|
|
if ((Seed[0] & 0x80) == 0)
|
|
{
|
|
// If the high bit is not set the NodeId is a valid IEEE 802
|
|
// address and should be globally unique.
|
|
ExpUuidCacheValid = CACHE_VALID;
|
|
}
|
|
else
|
|
{
|
|
ExpUuidCacheValid = CACHE_LOCAL_ONLY;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
if (CapturedSubjectContext) {
|
|
SeReleaseSubjectContext( &SubjectContext );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtAllocateUuids (
|
|
OUT PULARGE_INTEGER Time,
|
|
OUT PULONG Range,
|
|
OUT PULONG Sequence,
|
|
OUT PCHAR Seed
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
Seed - Pointer to a 6 byte buffer. The current seed is written into this buffer.
|
|
|
|
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 OutputTime;
|
|
ULONG OutputRange;
|
|
ULONG OutputSequence;
|
|
PKTHREAD CurrentThread;
|
|
|
|
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) {
|
|
ProbeForWriteSmallStructure((PVOID)Time, sizeof(LARGE_INTEGER), sizeof(ULONG));
|
|
ProbeForWriteSmallStructure((PVOID)Range, sizeof(ULONG), sizeof(ULONG));
|
|
ProbeForWriteSmallStructure((PVOID)Sequence, sizeof(ULONG), sizeof(ULONG));
|
|
ProbeForWriteSmallStructure((PVOID)Seed, SEED_SIZE, sizeof(CHAR));
|
|
}
|
|
} except (ExSystemExceptionFilter()) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
// Take the lock, because we're about to update the UUID cache.
|
|
CurrentThread = KeGetCurrentThread ();
|
|
KeEnterCriticalRegionThread(CurrentThread);
|
|
ExAcquireFastMutexUnsafe(&ExpUuidLock);
|
|
|
|
// Get the sequence number and a range of times that can
|
|
// be used in UUID-generation.
|
|
|
|
Status = ExpAllocateUuids( &OutputTime, &OutputRange, &OutputSequence );
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
ExReleaseFastMutexUnsafe(&ExpUuidLock);
|
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|
return( Status );
|
|
}
|
|
|
|
// If necessary, save the sequence number. If there's an error,
|
|
// we'll just leave it marked as dirty, and retry on some future call.
|
|
|
|
ExpUuidSaveSequenceNumberIf();
|
|
|
|
// Release the lock
|
|
ExReleaseFastMutexUnsafe(&ExpUuidLock);
|
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|
|
|
//
|
|
// 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;
|
|
RtlCopyMemory((PVOID) Seed, &ExpUuidCachedValues.NodeId[0], SEED_SIZE);
|
|
} except (ExSystemExceptionFilter()) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ExpUuidGetValues(
|
|
OUT UUID_CACHED_VALUES_STRUCT *Values
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a block of UUIDs and stores them in
|
|
the caller-provided cached-values structure.
|
|
|
|
This routine assumes that the current thread has exclusive
|
|
access to the ExpUuid* values.
|
|
|
|
Note that the Time value in this cache is different than the
|
|
Time value returned by NtAllocateUuids (and ExpAllocateUuids).
|
|
As a result, the cache must be interpreted differently in
|
|
order to determine the valid range. The valid range from
|
|
these two routines is:
|
|
|
|
NtAllocateUuids: [ Time, Time+Range )
|
|
ExpUuidGetValues: ( Values.Time-Values.Range, Values.Time ]
|
|
|
|
Arguments:
|
|
|
|
Values - Set to contain everything needed to allocate a block of uuids.
|
|
|
|
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 will occur if system clock hasn't advanced
|
|
and the allocator is out of cached values.
|
|
|
|
STATUS_NO_MEMORY is returned if we're unable to reserve a range
|
|
of UUIDs, for some reason other than the clock not advancing.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER Time;
|
|
ULONG Range;
|
|
ULONG Sequence;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Allocate a range of times for use in UUIDs.
|
|
|
|
Status = ExpAllocateUuids(&Time, &Range, &Sequence);
|
|
|
|
if (STATUS_RETRY == Status) {
|
|
return(Status);
|
|
}
|
|
|
|
else if (!NT_SUCCESS(Status)) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
// ExpAllocateUuids keeps time in SYSTEM_TIME format which is 100ns ticks since
|
|
// Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582.
|
|
|
|
// 17 Days in Oct + 30 (Nov) + 31 (Dec) + 18 years and 5 leap days.
|
|
|
|
Time.QuadPart += (ULONGLONG) (1000*1000*10) // seconds
|
|
* (ULONGLONG) (60 * 60 * 24) // days
|
|
* (ULONGLONG) (17+30+31+365*18+5); // # of days
|
|
|
|
ASSERT(Range);
|
|
|
|
Values->ClockSeqHiAndReserved =
|
|
UUID_RESERVED | (((UCHAR) (Sequence >> 8))
|
|
& (UCHAR) UUID_CLOCK_SEQ_HI_MASK);
|
|
|
|
Values->ClockSeqLow = (UCHAR) (Sequence & 0x00FF);
|
|
|
|
|
|
// We'll modify the Time value so that it indicates the
|
|
// end of the range rather than the beginning of it.
|
|
|
|
// The order of these assignments is important
|
|
|
|
Values->Time = Time.QuadPart + (Range - 1);
|
|
Values->AllocatedCount = Range;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ExUuidCreate(
|
|
OUT UUID *Uuid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a DCE UUID and returns it in the caller's
|
|
buffer.
|
|
|
|
Arguments:
|
|
|
|
Uuid - will receive the UUID.
|
|
|
|
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 will occur if system clock hasn't advanced
|
|
and the allocator is out of cached values.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
UUID_GENERATE *UuidGen = (UUID_GENERATE *) Uuid;
|
|
ULONGLONG Time;
|
|
LONG Delta;
|
|
PKTHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a value from the cache. If the cache is empty, we'll fill
|
|
// it and retry. The first time cache will be empty.
|
|
//
|
|
|
|
CurrentThread = KeGetCurrentThread ();
|
|
for(;;) {
|
|
|
|
// Get the highest value in the cache (though it may not
|
|
// be available).
|
|
Time = ExpUuidCachedValues.Time;
|
|
|
|
// Copy the static info into the UUID. We can't do this later
|
|
// because the clock sequence could be updated by another thread.
|
|
|
|
*(PULONG)&UuidGen->ClockSeqHiAndReserved =
|
|
*(PULONG)&ExpUuidCachedValues.ClockSeqHiAndReserved;
|
|
*(PULONG)&UuidGen->NodeId[2] =
|
|
*(PULONG)&ExpUuidCachedValues.NodeId[2];
|
|
|
|
// See what we need to subtract from Time to get a valid GUID.
|
|
Delta = InterlockedDecrement(&ExpUuidCachedValues.AllocatedCount);
|
|
|
|
if (Time != ExpUuidCachedValues.Time) {
|
|
|
|
// If our captured time doesn't match the cache then another
|
|
// thread already took the lock and updated the cache. We'll
|
|
// just loop and try again.
|
|
continue;
|
|
}
|
|
|
|
// If the cache hadn't already run dry, we can break out of this retry
|
|
// loop.
|
|
if (Delta >= 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate a new block of Uuids.
|
|
//
|
|
|
|
// Take the cache lock
|
|
KeEnterCriticalRegionThread(CurrentThread);
|
|
ExAcquireFastMutexUnsafe(&ExpUuidLock);
|
|
|
|
// If the cache has already been updated, try again.
|
|
if (Time != ExpUuidCachedValues.Time) {
|
|
// Release the lock
|
|
ExReleaseFastMutexUnsafe(&ExpUuidLock);
|
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|
continue;
|
|
}
|
|
|
|
// Update the cache.
|
|
Status = ExpUuidGetValues( &ExpUuidCachedValues );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
// Release the lock
|
|
ExReleaseFastMutexUnsafe(&ExpUuidLock);
|
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|
return(Status);
|
|
}
|
|
|
|
// The sequence number may have been dirtied, see if it needs
|
|
// to be saved. If there's an error, we'll ignore it and
|
|
// retry on a future call.
|
|
|
|
ExpUuidSaveSequenceNumberIf();
|
|
|
|
// Release the lock
|
|
ExReleaseFastMutexUnsafe(&ExpUuidLock);
|
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|
|
|
// Loop
|
|
}
|
|
|
|
// Adjust the time to that of the next available UUID.
|
|
Time -= Delta;
|
|
|
|
// Finish filling in the UUID.
|
|
|
|
UuidGen->TimeLow = (ULONG) Time;
|
|
UuidGen->TimeMid = (USHORT) (Time >> 32);
|
|
UuidGen->TimeHiAndVersion = (USHORT)
|
|
(( (USHORT)(Time >> (32+16))
|
|
& UUID_TIME_HIGH_MASK) | UUID_VERSION);
|
|
|
|
ASSERT(Status == STATUS_SUCCESS);
|
|
|
|
if (ExpUuidCacheValid == CACHE_LOCAL_ONLY) {
|
|
Status = RPC_NT_UUID_LOCAL_ONLY;
|
|
}
|
|
|
|
return(Status);
|
|
}
|