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