2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/uuid.c
5 * PURPOSE: UUID generator
6 * PROGRAMMERS: Eric Kohl
11 /* INCLUDES *****************************************************************/
17 #define SEED_BUFFER_SIZE 6
19 /* Number of 100ns ticks per clock tick. To be safe, assume that the clock
20 resolution is at least 1000 * 100 * (1/1000000) = 1/10 of a second */
21 #define TICKS_PER_CLOCK_TICK 1000
22 #define SECSPERDAY 86400
23 #define TICKSPERSEC 10000000
25 /* UUID system time starts at October 15, 1582 */
26 #define SECS_15_OCT_1582_TO_1601 ((17 + 30 + 31 + 365 * 18 + 5) * SECSPERDAY)
27 #define TICKS_15_OCT_1582_TO_1601 ((ULONGLONG)SECS_15_OCT_1582_TO_1601 * TICKSPERSEC)
29 /* 10000 in 100-ns model = 0,1 microsecond */
30 #define TIME_FRAME 10000
32 #if defined (ALLOC_PRAGMA)
33 #pragma alloc_text(INIT, ExpUuidInitialization)
34 #pragma alloc_text(INIT, ExLuidInitialization)
38 /* GLOBALS ****************************************************************/
40 FAST_MUTEX ExpUuidLock
;
41 LARGE_INTEGER ExpUuidLastTimeAllocated
;
42 ULONG ExpUuidSequenceNumber
= 0;
43 BOOLEAN ExpUuidSequenceNumberValid
;
44 BOOLEAN ExpUuidSequenceNumberNotSaved
= FALSE
;
45 UUID_CACHED_VALUES_STRUCT ExpUuidCachedValues
= {0ULL, 0xFFFFFFFF, 0, 0, { 0x80, 0x6E, 0x6F, 0x6E, 0x69, 0x63}};
46 BOOLEAN ExpUuidCacheValid
= FALSE
;
47 ULONG ExpLuidIncrement
= 1;
48 LARGE_INTEGER ExpLuid
= {.LowPart
= 0x3e9, .HighPart
= 0x0};
50 /* FUNCTIONS ****************************************************************/
58 ExpUuidInitialization(VOID
)
60 ExInitializeFastMutex(&ExpUuidLock
);
62 ExpUuidSequenceNumberValid
= FALSE
;
63 KeQuerySystemTime(&ExpUuidLastTimeAllocated
);
72 #define VALUE_BUFFER_SIZE 20
74 ExpUuidLoadSequenceNumber(PULONG Sequence
)
76 UCHAR ValueBuffer
[VALUE_BUFFER_SIZE
];
77 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
78 OBJECT_ATTRIBUTES ObjectAttributes
;
79 UNICODE_STRING KeyName
, ValueName
;
86 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
87 RtlInitUnicodeString(&ValueName
, L
"UuidSequenceNumber");
89 InitializeObjectAttributes(&ObjectAttributes
,
91 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
94 Status
= ZwOpenKey(&KeyHandle
, GENERIC_READ
, &ObjectAttributes
);
95 if (!NT_SUCCESS(Status
))
97 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status
);
101 ValueInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)ValueBuffer
;
102 Status
= ZwQueryValueKey(KeyHandle
,
104 KeyValuePartialInformation
,
109 if (!NT_SUCCESS(Status
))
111 DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status
);
115 if (ValueInfo
->Type
!= REG_DWORD
|| ValueInfo
->DataLength
!= sizeof(DWORD
))
117 return STATUS_UNSUCCESSFUL
;
120 *Sequence
= *((PULONG
)ValueInfo
->Data
);
122 DPRINT("Loaded sequence %lx\n", *Sequence
);
124 return STATUS_SUCCESS
;
126 #undef VALUE_BUFFER_SIZE
132 ExpUuidSaveSequenceNumber(PULONG Sequence
)
134 OBJECT_ATTRIBUTES ObjectAttributes
;
135 UNICODE_STRING KeyName
, ValueName
;
141 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
142 RtlInitUnicodeString(&ValueName
, L
"UuidSequenceNumber");
144 InitializeObjectAttributes(&ObjectAttributes
,
146 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
149 Status
= ZwOpenKey(&KeyHandle
,
150 GENERIC_READ
| GENERIC_WRITE
,
152 if (!NT_SUCCESS(Status
))
154 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status
);
158 Status
= ZwSetValueKey(KeyHandle
,
165 if (!NT_SUCCESS(Status
))
167 DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status
);
175 * Warning! This function must be called
176 * with ExpUuidLock held!
179 ExpUuidSaveSequenceNumberIf(VOID
)
185 /* Only save sequence if it has to */
186 if (ExpUuidSequenceNumberNotSaved
== TRUE
)
188 Status
= ExpUuidSaveSequenceNumber(&ExpUuidSequenceNumber
);
189 if (NT_SUCCESS(Status
))
191 ExpUuidSequenceNumberNotSaved
= FALSE
;
198 * Warning! This function must be called
199 * with ExpUuidLock held!
202 ExpAllocateUuids(PULARGE_INTEGER Time
,
207 LARGE_INTEGER Counter
, Frequency
, CurrentTime
, TimeDiff
, ClockDiff
;
211 /* Initialize sequence number */
212 if (!ExpUuidSequenceNumberValid
)
214 /* Try to load sequence number */
215 Status
= ExpUuidLoadSequenceNumber(&ExpUuidSequenceNumber
);
216 if (NT_SUCCESS(Status
))
218 ++ExpUuidSequenceNumber
;
222 /* If we cannot, generate a "true" random */
223 Counter
= KeQueryPerformanceCounter(&Frequency
);
224 ExpUuidSequenceNumber
^= (ULONG_PTR
)&Status
^ (ULONG_PTR
)Sequence
^ Counter
.LowPart
^ Counter
.HighPart
;
227 /* It's valid and to be saved */
228 ExpUuidSequenceNumberValid
= TRUE
;
229 ExpUuidSequenceNumberNotSaved
= TRUE
;
232 KeQuerySystemTime(&CurrentTime
);
233 TimeDiff
.QuadPart
= CurrentTime
.QuadPart
- ExpUuidLastTimeAllocated
.QuadPart
;
234 /* If time went backwards, change sequence (see RFC example) */
235 if (TimeDiff
.QuadPart
< 0)
237 ++ExpUuidSequenceNumber
;
238 TimeDiff
.QuadPart
= 2 * TIME_FRAME
;
240 /* It's to be saved */
241 ExpUuidSequenceNumberNotSaved
= TRUE
;
242 ExpUuidLastTimeAllocated
.QuadPart
= CurrentTime
.QuadPart
- 2 * TIME_FRAME
;
245 if (TimeDiff
.QuadPart
== 0)
250 /* If time diff > 0,1ms, squash it to reduce it to keep our clock resolution */
251 if (TimeDiff
.HighPart
> 0 || TimeDiff
.QuadPart
> TICKS_PER_CLOCK_TICK
* TIME_FRAME
)
253 TimeDiff
.QuadPart
= TICKS_PER_CLOCK_TICK
* TIME_FRAME
;
256 if (TimeDiff
.HighPart
< 0 || TimeDiff
.QuadPart
<= TIME_FRAME
)
258 *Range
= TimeDiff
.QuadPart
;
259 ClockDiff
.QuadPart
= 0LL;
264 ClockDiff
.QuadPart
= TimeDiff
.QuadPart
- TIME_FRAME
;
265 --ClockDiff
.HighPart
;
268 Time
->QuadPart
= CurrentTime
.QuadPart
- *Range
- ClockDiff
.QuadPart
;
269 ExpUuidLastTimeAllocated
.QuadPart
= CurrentTime
.QuadPart
- ClockDiff
.QuadPart
;
270 *Sequence
= ExpUuidSequenceNumber
;
272 return STATUS_SUCCESS
;
277 * Warning! This function must be called
278 * with ExpUuidLock held!
281 ExpUuidGetValues(PUUID_CACHED_VALUES_STRUCT CachedValues
)
291 Status
= ExpAllocateUuids(&Time
, &Range
, &Sequence
);
292 if (Status
== STATUS_RETRY
)
297 if (!NT_SUCCESS(Status
))
299 return STATUS_NO_MEMORY
;
302 /* We need at least one UUID */
305 /* Set up our internal cache
306 * See format_uuid_v1 in RFC4122 for magic values
308 CachedValues
->ClockSeqLow
= Sequence
;
309 CachedValues
->ClockSeqHiAndReserved
= (Sequence
& 0x3F00) >> 8;
310 CachedValues
->ClockSeqHiAndReserved
|= 0x80;
311 CachedValues
->AllocatedCount
= Range
;
314 * Time is relative to UUID time
315 * And we set last time range for all the possibly
318 Time
.QuadPart
+= TICKS_15_OCT_1582_TO_1601
;
319 CachedValues
->Time
= Time
.QuadPart
+ (Range
- 1);
321 return STATUS_SUCCESS
;
330 ExLuidInitialization(VOID
)
340 ExAllocateLocallyUniqueId(OUT LUID
*LocallyUniqueId
)
342 LARGE_INTEGER PrevLuid
;
343 LONGLONG NewLuid
, CompLuid
;
345 /* Atomically increment the luid */
346 PrevLuid
.QuadPart
= ExpLuid
.QuadPart
;
347 for (NewLuid
= ExpLuid
.QuadPart
+ ExpLuidIncrement
; ;
348 NewLuid
= PrevLuid
.QuadPart
+ ExpLuidIncrement
)
350 CompLuid
= InterlockedCompareExchange64(&ExpLuid
.QuadPart
,
353 if (CompLuid
== PrevLuid
.QuadPart
)
358 PrevLuid
.QuadPart
= CompLuid
;
361 LocallyUniqueId
->LowPart
= PrevLuid
.LowPart
;
362 LocallyUniqueId
->HighPart
= PrevLuid
.HighPart
;
371 NtAllocateLocallyUniqueId(OUT LUID
*LocallyUniqueId
)
373 KPROCESSOR_MODE PreviousMode
;
376 /* Probe if user mode */
377 PreviousMode
= ExGetPreviousMode();
378 if (PreviousMode
!= KernelMode
)
382 ProbeForWrite(LocallyUniqueId
,
386 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
388 _SEH2_YIELD(return _SEH2_GetExceptionCode());
393 /* Do the allocation */
394 ExAllocateLocallyUniqueId(LocallyUniqueId
);
395 return STATUS_SUCCESS
;
403 ExUuidCreate(OUT UUID
*Uuid
)
412 Status
= STATUS_SUCCESS
;
413 /* Loop until we have an UUID to return */
416 /* Try to gather node values */
419 Time
.QuadPart
= ExpUuidCachedValues
.Time
;
421 RtlCopyMemory(&Uuid
->Data4
[0],
422 &ExpUuidCachedValues
.NodeId
[0],
424 Valid
= ExpUuidCacheValid
;
425 AllocatedCount
= InterlockedDecrement(&ExpUuidCachedValues
.AllocatedCount
);
427 /* Loop till we can do it without being disturbed */
428 while (Time
.QuadPart
!= ExpUuidCachedValues
.Time
);
430 /* We have more than an allocated UUID left, that's OK to return! */
431 if (AllocatedCount
>= 0)
437 * Here, we're out of UUIDs, we need to allocate more
438 * We need to be alone to do it, so lock the mutex
440 ExAcquireFastMutex(&ExpUuidLock
);
441 if (Time
.QuadPart
== ExpUuidCachedValues
.Time
)
443 /* If allocation fails, bail out! */
444 Status
= ExpUuidGetValues(&ExpUuidCachedValues
);
445 if (Status
!= STATUS_SUCCESS
)
447 ExReleaseFastMutex(&ExpUuidLock
);
451 /* Save our current sequence if changed */
452 ExpUuidSaveSequenceNumberIf();
454 ExReleaseFastMutex(&ExpUuidLock
);
458 * Once here, we've got an UUID to return
459 * But, if our init wasn't sane, then, make
460 * sure it's only used locally
464 Status
= RPC_NT_UUID_LOCAL_ONLY
;
467 /* Set our timestamp - see RFC4211 */
468 Time
.QuadPart
-= AllocatedCount
;
469 Uuid
->Data1
= Time
.LowPart
;
470 Uuid
->Data2
= Time
.HighPart
;
471 /* We also set the bit for GUIDv1 */
472 Uuid
->Data3
= ((Time
.HighPart
>> 16) & 0x0FFF) | 0x1000;
482 NtAllocateUuids(OUT PULARGE_INTEGER Time
,
487 ULARGE_INTEGER IntTime
;
488 ULONG IntRange
, IntSequence
;
490 KPROCESSOR_MODE PreviousMode
;
494 /* Probe if user mode */
495 PreviousMode
= ExGetPreviousMode();
496 if (PreviousMode
!= KernelMode
)
501 sizeof(ULARGE_INTEGER
),
508 ProbeForWrite(Sequence
,
516 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
518 _SEH2_YIELD(return _SEH2_GetExceptionCode());
523 /* During allocation we must be alone */
524 ExAcquireFastMutex(&ExpUuidLock
);
526 Status
= ExpAllocateUuids(&IntTime
,
529 if (!NT_SUCCESS(Status
))
531 ExReleaseFastMutex(&ExpUuidLock
);
535 /* If sequence number was changed, save it */
536 ExpUuidSaveSequenceNumberIf();
538 /* Allocation done, so we can release */
539 ExReleaseFastMutex(&ExpUuidLock
);
541 /* Write back UUIDs to caller */
544 Time
->QuadPart
= IntTime
.QuadPart
;
546 *Sequence
= IntSequence
;
549 &ExpUuidCachedValues
.NodeId
[0],
552 Status
= STATUS_SUCCESS
;
554 _SEH2_EXCEPT(ExSystemExceptionFilter())
556 Status
= _SEH2_GetExceptionCode();
569 NtSetUuidSeed(IN PUCHAR Seed
)
574 SECURITY_SUBJECT_CONTEXT SubjectContext
;
575 LUID CallerLuid
, SystemLuid
= SYSTEM_LUID
;
579 /* Should only be done by umode */
580 ASSERT(KeGetPreviousMode() != KernelMode
);
582 /* No context to release */
586 /* Get our caller context and remember to release it */
587 SeCaptureSubjectContext(&SubjectContext
);
590 /* Get caller access token and its associated ID */
591 Token
= SeQuerySubjectContextToken(&SubjectContext
);
592 Status
= SeQueryAuthenticationIdToken(Token
, &CallerLuid
);
593 if (!NT_SUCCESS(Status
))
595 RtlRaiseStatus(Status
);
598 /* This call is only allowed for SYSTEM */
599 if (!RtlEqualLuid(&CallerLuid
, &SystemLuid
))
601 RtlRaiseStatus(STATUS_ACCESS_DENIED
);
604 /* Check for buffer validity and then copy it to our seed */
605 ProbeForRead(Seed
, SEED_BUFFER_SIZE
, sizeof(UCHAR
));
606 RtlCopyMemory(&ExpUuidCachedValues
.NodeId
[0], Seed
, SEED_BUFFER_SIZE
);
609 * According to RFC 4122, UUID seed is based on MAC addresses
610 * If it is randomly set, then, it must have its multicast be set
611 * to be valid and avoid collisions
614 ExpUuidCacheValid
= ~(*Seed
>> 7) & 1;
616 Status
= STATUS_SUCCESS
;
618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
620 Status
= _SEH2_GetExceptionCode();
624 /* Release context if required */
627 SeReleaseSubjectContext(&SubjectContext
);