[NTOSKRNL] UuidMutex -> ExpUuidLock
[reactos.git] / ntoskrnl / ex / uuid.c
1 /*
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
7 * Thomas Weidenmueller
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define SEED_BUFFER_SIZE 6
17
18 /* Number of 100ns ticks per clock tick. To be safe, assume that the clock
19 resolution is at least 1000 * 100 * (1/1000000) = 1/10 of a second */
20 #define TICKS_PER_CLOCK_TICK 1000
21 #define SECSPERDAY 86400
22 #define TICKSPERSEC 10000000
23
24 /* UUID system time starts at October 15, 1582 */
25 #define SECS_15_OCT_1582_TO_1601 ((17 + 30 + 31 + 365 * 18 + 5) * SECSPERDAY)
26 #define TICKS_15_OCT_1582_TO_1601 ((ULONGLONG)SECS_15_OCT_1582_TO_1601 * TICKSPERSEC)
27
28 #if defined (ALLOC_PRAGMA)
29 #pragma alloc_text(INIT, ExpInitUuids)
30 #endif
31
32
33 /* GLOBALS ****************************************************************/
34
35 static FAST_MUTEX ExpUuidLock;
36 static ULARGE_INTEGER UuidLastTime;
37 static ULONG UuidSequence;
38 static BOOLEAN UuidSequenceInitialized = FALSE;
39 static BOOLEAN UuidSequenceChanged = FALSE;
40 static UCHAR UuidSeed[SEED_BUFFER_SIZE];
41 static ULONG UuidCount;
42 static LARGE_INTEGER LuidIncrement;
43 static LARGE_INTEGER LuidValue;
44
45 /* FUNCTIONS ****************************************************************/
46
47 VOID
48 INIT_FUNCTION
49 NTAPI
50 ExpInitUuids(VOID)
51 {
52 ExInitializeFastMutex(&ExpUuidLock);
53
54 KeQuerySystemTime((PLARGE_INTEGER)&UuidLastTime);
55 UuidLastTime.QuadPart += TICKS_15_OCT_1582_TO_1601;
56
57 UuidCount = TICKS_PER_CLOCK_TICK;
58 RtlZeroMemory(UuidSeed, SEED_BUFFER_SIZE);
59 }
60
61
62 #define VALUE_BUFFER_SIZE 256
63
64 static NTSTATUS
65 ExpLoadUuidSequence(PULONG Sequence)
66 {
67 UCHAR ValueBuffer[VALUE_BUFFER_SIZE];
68 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
69 OBJECT_ATTRIBUTES ObjectAttributes;
70 UNICODE_STRING Name;
71 HANDLE KeyHandle;
72 ULONG ValueLength;
73 NTSTATUS Status;
74
75 RtlInitUnicodeString(&Name,
76 L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
77 InitializeObjectAttributes(&ObjectAttributes,
78 &Name,
79 OBJ_CASE_INSENSITIVE,
80 NULL,
81 NULL);
82 Status = ZwOpenKey(&KeyHandle,
83 KEY_QUERY_VALUE,
84 &ObjectAttributes);
85 if (!NT_SUCCESS(Status))
86 {
87 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
88 return Status;
89 }
90
91 RtlInitUnicodeString(&Name, L"UuidSequenceNumber");
92
93 ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
94 Status = ZwQueryValueKey(KeyHandle,
95 &Name,
96 KeyValuePartialInformation,
97 ValueBuffer,
98 VALUE_BUFFER_SIZE,
99 &ValueLength);
100 ZwClose(KeyHandle);
101 if (!NT_SUCCESS(Status))
102 {
103 DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
104 return Status;
105 }
106
107 *Sequence = *((PULONG)ValueInfo->Data);
108
109 DPRINT("Loaded sequence %lx\n", *Sequence);
110
111 return STATUS_SUCCESS;
112 }
113 #undef VALUE_BUFFER_SIZE
114
115
116 static NTSTATUS
117 ExpSaveUuidSequence(PULONG Sequence)
118 {
119 OBJECT_ATTRIBUTES ObjectAttributes;
120 UNICODE_STRING Name;
121 HANDLE KeyHandle;
122 NTSTATUS Status;
123
124 RtlInitUnicodeString(&Name,
125 L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
126 InitializeObjectAttributes(&ObjectAttributes,
127 &Name,
128 OBJ_CASE_INSENSITIVE,
129 NULL,
130 NULL);
131 Status = ZwOpenKey(&KeyHandle,
132 KEY_SET_VALUE,
133 &ObjectAttributes);
134 if (!NT_SUCCESS(Status))
135 {
136 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
137 return Status;
138 }
139
140 RtlInitUnicodeString(&Name, L"UuidSequenceNumber");
141 Status = ZwSetValueKey(KeyHandle,
142 &Name,
143 0,
144 REG_DWORD,
145 Sequence,
146 sizeof(ULONG));
147 ZwClose(KeyHandle);
148 if (!NT_SUCCESS(Status))
149 {
150 DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status);
151 }
152
153 return Status;
154 }
155
156
157 static VOID
158 ExpGetRandomUuidSequence(PULONG Sequence)
159 {
160 LARGE_INTEGER Counter;
161 LARGE_INTEGER Frequency;
162 ULONG Value;
163
164 Counter = KeQueryPerformanceCounter(&Frequency);
165 Value = Counter.u.LowPart ^ Counter.u.HighPart;
166
167 *Sequence = *Sequence ^ Value;
168
169 DPRINT("Sequence %lx\n", *Sequence);
170 }
171
172
173 static NTSTATUS
174 ExpCreateUuids(PULARGE_INTEGER Time,
175 PULONG Range,
176 PULONG Sequence)
177 {
178 /*
179 * Generate time element of the UUID. Account for going faster
180 * than our clock as well as the clock going backwards.
181 */
182 while (1)
183 {
184 KeQuerySystemTime((PLARGE_INTEGER)Time);
185 Time->QuadPart += TICKS_15_OCT_1582_TO_1601;
186
187 if (Time->QuadPart > UuidLastTime.QuadPart)
188 {
189 UuidCount = 0;
190 break;
191 }
192
193 if (Time->QuadPart < UuidLastTime.QuadPart)
194 {
195 (*Sequence)++;
196 UuidSequenceChanged = TRUE;
197 UuidCount = 0;
198 break;
199 }
200
201 if (UuidCount < TICKS_PER_CLOCK_TICK)
202 {
203 UuidCount++;
204 break;
205 }
206 }
207
208 UuidLastTime.QuadPart = Time->QuadPart;
209 Time->QuadPart += UuidCount;
210
211 *Range = 10000; /* What does this mean? Ticks per millisecond?*/
212
213 return STATUS_SUCCESS;
214 }
215
216 VOID
217 INIT_FUNCTION
218 NTAPI
219 ExpInitLuid(VOID)
220 {
221 LUID DummyLuidValue = SYSTEM_LUID;
222
223 LuidValue.u.HighPart = DummyLuidValue.HighPart;
224 LuidValue.u.LowPart = DummyLuidValue.LowPart;
225 LuidIncrement.QuadPart = 1;
226 }
227
228
229 VOID
230 NTAPI
231 ExAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
232 {
233 LARGE_INTEGER NewLuid, PrevLuid;
234
235 /* atomically increment the luid */
236 do
237 {
238 PrevLuid = LuidValue;
239 NewLuid = RtlLargeIntegerAdd(PrevLuid,
240 LuidIncrement);
241 } while(ExInterlockedCompareExchange64(&LuidValue.QuadPart,
242 &NewLuid.QuadPart,
243 &PrevLuid.QuadPart,
244 NULL) != PrevLuid.QuadPart);
245
246 LocallyUniqueId->LowPart = NewLuid.u.LowPart;
247 LocallyUniqueId->HighPart = NewLuid.u.HighPart;
248 }
249
250
251 /*
252 * @implemented
253 */
254 NTSTATUS
255 NTAPI
256 NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
257 {
258 LUID NewLuid;
259 KPROCESSOR_MODE PreviousMode;
260 NTSTATUS Status;
261 PAGED_CODE();
262
263 /* Probe if user mode */
264 PreviousMode = ExGetPreviousMode();
265 if (PreviousMode != KernelMode)
266 {
267 _SEH2_TRY
268 {
269 ProbeForWrite(LocallyUniqueId,
270 sizeof(LUID),
271 sizeof(ULONG));
272 }
273 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274 {
275 _SEH2_YIELD(return _SEH2_GetExceptionCode());
276 }
277 _SEH2_END;
278 }
279
280 /* Do the allocation */
281 ExAllocateLocallyUniqueId(&NewLuid);
282 Status = STATUS_SUCCESS;
283
284 /* Write back LUID to caller */
285 _SEH2_TRY
286 {
287 *LocallyUniqueId = NewLuid;
288 }
289 _SEH2_EXCEPT(ExSystemExceptionFilter())
290 {
291 Status = _SEH2_GetExceptionCode();
292 }
293 _SEH2_END;
294 return Status;
295 }
296
297 /*
298 * @unimplemented
299 */
300 NTSTATUS
301 NTAPI
302 ExUuidCreate(OUT UUID *Uuid)
303 {
304 UNIMPLEMENTED;
305 return FALSE;
306 }
307
308 /*
309 * @unimplemented
310 */
311 NTSTATUS
312 NTAPI
313 NtAllocateUuids(OUT PULARGE_INTEGER Time,
314 OUT PULONG Range,
315 OUT PULONG Sequence,
316 OUT PUCHAR Seed)
317 {
318 ULARGE_INTEGER IntTime;
319 ULONG IntRange;
320 NTSTATUS Status;
321 KPROCESSOR_MODE PreviousMode;
322
323 PAGED_CODE();
324
325 /* Probe if user mode */
326 PreviousMode = ExGetPreviousMode();
327 if (PreviousMode != KernelMode)
328 {
329 _SEH2_TRY
330 {
331 ProbeForWrite(Time,
332 sizeof(ULARGE_INTEGER),
333 sizeof(ULONG));
334
335 ProbeForWrite(Range,
336 sizeof(ULONG),
337 sizeof(ULONG));
338
339 ProbeForWrite(Sequence,
340 sizeof(ULONG),
341 sizeof(ULONG));
342
343 ProbeForWrite(Seed,
344 SEED_BUFFER_SIZE,
345 sizeof(UCHAR));
346 }
347 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
348 {
349 _SEH2_YIELD(return _SEH2_GetExceptionCode());
350 }
351 _SEH2_END;
352 }
353
354 ExAcquireFastMutex(&ExpUuidLock);
355
356 if (!UuidSequenceInitialized)
357 {
358 Status = ExpLoadUuidSequence(&UuidSequence);
359 if (NT_SUCCESS(Status))
360 {
361 UuidSequence++;
362 }
363 else
364 {
365 ExpGetRandomUuidSequence(&UuidSequence);
366 }
367
368 UuidSequenceInitialized = TRUE;
369 UuidSequenceChanged = TRUE;
370 }
371
372 Status = ExpCreateUuids(&IntTime,
373 &IntRange,
374 &UuidSequence);
375 if (!NT_SUCCESS(Status))
376 {
377 ExReleaseFastMutex(&ExpUuidLock);
378 return Status;
379 }
380
381 if (UuidSequenceChanged)
382 {
383 Status = ExpSaveUuidSequence(&UuidSequence);
384 if (NT_SUCCESS(Status))
385 UuidSequenceChanged = FALSE;
386 }
387
388 ExReleaseFastMutex(&ExpUuidLock);
389
390 /* Write back UUIDs to caller */
391 _SEH2_TRY
392 {
393 Time->QuadPart = IntTime.QuadPart;
394 *Range = IntRange;
395 *Sequence = UuidSequence;
396
397 RtlCopyMemory(Seed,
398 UuidSeed,
399 SEED_BUFFER_SIZE);
400
401 Status = STATUS_SUCCESS;
402 }
403 _SEH2_EXCEPT(ExSystemExceptionFilter())
404 {
405 Status = _SEH2_GetExceptionCode();
406 }
407 _SEH2_END;
408
409 return Status;
410 }
411
412
413 /*
414 * @implemented
415 */
416 NTSTATUS
417 NTAPI
418 NtSetUuidSeed(IN PUCHAR Seed)
419 {
420 NTSTATUS Status;
421 BOOLEAN GotContext;
422 PACCESS_TOKEN Token;
423 SECURITY_SUBJECT_CONTEXT SubjectContext;
424 LUID CallerLuid, SystemLuid = SYSTEM_LUID;
425
426 PAGED_CODE();
427
428 /* Should only be done by umode */
429 ASSERT(KeGetPreviousMode() != KernelMode);
430
431 /* No context to release */
432 GotContext = FALSE;
433 _SEH2_TRY
434 {
435 /* Get our caller context and remember to release it */
436 SeCaptureSubjectContext(&SubjectContext);
437 GotContext = TRUE;
438
439 /* Get caller access token and its associated ID */
440 Token = SeQuerySubjectContextToken(&SubjectContext);
441 Status = SeQueryAuthenticationIdToken(Token, &CallerLuid);
442 if (!NT_SUCCESS(Status))
443 {
444 RtlRaiseStatus(Status);
445 }
446
447 /* This call is only allowed for SYSTEM */
448 if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
449 {
450 RtlRaiseStatus(STATUS_ACCESS_DENIED);
451 }
452
453 /* Check for buffer validity and then copy it to our seed */
454 ProbeForRead(Seed, SEED_BUFFER_SIZE, sizeof(UCHAR));
455 RtlCopyMemory(UuidSeed, Seed, SEED_BUFFER_SIZE);
456
457 Status = STATUS_SUCCESS;
458 }
459 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
460 {
461 Status = _SEH2_GetExceptionCode();
462 }
463 _SEH2_END;
464
465 /* Release context if required */
466 if (GotContext)
467 {
468 SeReleaseSubjectContext(&SubjectContext);
469 }
470
471 return Status;
472 }
473
474 /* EOF */