Source code of Windows XP (NT5)
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
26 KiB

  1. /*++
  2. Copyright (c) 1994-1997 Microsoft Corporation
  3. Module Name:
  4. uuid.c
  5. Abstract:
  6. This module implements the core time and sequence number allocation
  7. for UUIDs (exposed to user mode), as well as complete UUID
  8. creation (exposed to kernel mode only).
  9. (e.g. RPC Runtime) (e.g. NTFS)
  10. | |
  11. V V
  12. NtAllocateUuids ExUuidCreate
  13. | |
  14. V V
  15. | ExpUuidGetValues
  16. | |
  17. | |
  18. +------> ExpAllocateUuids <----+
  19. Author:
  20. Mario Goertzel (MarioGo) 22-Nov-1994
  21. Revision History:
  22. MikeHill 17-Jan-96 Ported ExUuidCreate & ExpUuidGetValues from RPCRT4.
  23. MazharM 17-Feb-98 Add PNP support
  24. --*/
  25. #include "exp.h"
  26. //
  27. // Well known values
  28. //
  29. // Registry info for the sequen number
  30. #define RPC_SEQUENCE_NUMBER_PATH L"\\Registry\\Machine\\Software\\Microsoft\\Rpc"
  31. #define RPC_SEQUENCE_NUMBER_NAME L"UuidSequenceNumber"
  32. // Masks and constants to interpret the UUID
  33. #define UUID_TIME_HIGH_MASK 0x0FFF
  34. #define UUID_VERSION 0x1000
  35. #define UUID_RESERVED 0x80
  36. #define UUID_CLOCK_SEQ_HI_MASK 0x3F
  37. // Values for ExpUuidCacheValid
  38. #define CACHE_LOCAL_ONLY 0
  39. #define CACHE_VALID 1
  40. //
  41. // Custom types
  42. //
  43. // An alternative data-template for a UUID, useful during generation.
  44. typedef struct _UUID_GENERATE {
  45. ULONG TimeLow;
  46. USHORT TimeMid;
  47. USHORT TimeHiAndVersion;
  48. UCHAR ClockSeqHiAndReserved;
  49. UCHAR ClockSeqLow;
  50. UCHAR NodeId[6];
  51. } UUID_GENERATE;
  52. // A cache of allocated UUIDs
  53. typedef struct _UUID_CACHED_VALUES_STRUCT {
  54. ULONGLONG Time; // End time of allocation
  55. LONG AllocatedCount; // Number of UUIDs allocated
  56. UCHAR ClockSeqHiAndReserved;
  57. UCHAR ClockSeqLow;
  58. UCHAR NodeId[6];
  59. } UUID_CACHED_VALUES_STRUCT;
  60. //
  61. // Global variables
  62. //
  63. #ifdef ALLOC_DATA_PRAGMA
  64. #pragma data_seg("PAGEDATA")
  65. #endif
  66. // UUID cache information
  67. LARGE_INTEGER ExpUuidLastTimeAllocated = {0,0};
  68. BOOLEAN ExpUuidCacheValid = CACHE_LOCAL_ONLY;
  69. // Make cache allocate UUIDs on first call.
  70. // Time = 0. Allocated = -1, ..., multicast bit in node id
  71. UUID_CACHED_VALUES_STRUCT ExpUuidCachedValues = { 0, -1, 0, 0, { 0x80, 'm', 'a', 'r', 'i', 'o' }};
  72. // UUID Sequence number information
  73. ULONG ExpUuidSequenceNumber = 0;
  74. BOOLEAN ExpUuidSequenceNumberValid = FALSE;
  75. BOOLEAN ExpUuidSequenceNumberNotSaved = FALSE;
  76. #ifdef ALLOC_DATA_PRAGMA
  77. #pragma data_seg()
  78. #endif
  79. // A lock to protect all of the above global data.
  80. FAST_MUTEX ExpUuidLock;
  81. //
  82. // Code section allocations
  83. //
  84. extern NTSTATUS ExpUuidLoadSequenceNumber(
  85. OUT PULONG
  86. );
  87. extern NTSTATUS ExpUuidSaveSequenceNumber(
  88. IN ULONG
  89. );
  90. extern NTSTATUS ExpUuidSaveSequenceNumberIf ();
  91. extern NTSTATUS ExpUuidGetValues(
  92. OUT UUID_CACHED_VALUES_STRUCT *Values
  93. );
  94. #ifdef ALLOC_PRAGMA
  95. NTSTATUS
  96. ExpAllocateUuids (
  97. OUT PLARGE_INTEGER Time,
  98. OUT PULONG Range,
  99. OUT PULONG Sequence
  100. );
  101. #pragma alloc_text(PAGE, ExpUuidLoadSequenceNumber)
  102. #pragma alloc_text(PAGE, ExpUuidSaveSequenceNumber)
  103. #pragma alloc_text(PAGE, ExpUuidSaveSequenceNumberIf)
  104. #pragma alloc_text(INIT, ExpUuidInitialization)
  105. #pragma alloc_text(PAGE, ExpAllocateUuids)
  106. #pragma alloc_text(PAGE, NtAllocateUuids)
  107. #pragma alloc_text(PAGE, NtSetUuidSeed)
  108. #pragma alloc_text(PAGE, ExpUuidGetValues)
  109. #pragma alloc_text(PAGE, ExUuidCreate)
  110. #endif
  111. NTSTATUS
  112. ExpUuidLoadSequenceNumber(
  113. OUT PULONG Sequence
  114. )
  115. /*++
  116. Routine Description:
  117. This function loads the saved sequence number from the registry.
  118. This function is called only during system startup.
  119. Arguments:
  120. Sequence - Pointer to storage for the sequence number.
  121. Return Value:
  122. STATUS_SUCCESS when the sequence number is successfully read from the
  123. registry.
  124. STATUS_UNSUCCESSFUL when the sequence number is not correctly stored
  125. in the registry.
  126. Failure codes from ZwOpenKey() and ZwQueryValueKey() maybe returned.
  127. --*/
  128. {
  129. NTSTATUS Status;
  130. OBJECT_ATTRIBUTES ObjectAttributes;
  131. UNICODE_STRING KeyPath, KeyName;
  132. HANDLE Key;
  133. CHAR KeyValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
  134. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  135. ULONG ResultLength;
  136. PAGED_CODE();
  137. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
  138. RtlInitUnicodeString(&KeyPath, RPC_SEQUENCE_NUMBER_PATH);
  139. RtlInitUnicodeString(&KeyName, RPC_SEQUENCE_NUMBER_NAME);
  140. InitializeObjectAttributes( &ObjectAttributes,
  141. &KeyPath,
  142. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  143. NULL,
  144. NULL
  145. );
  146. Status =
  147. ZwOpenKey( &Key,
  148. GENERIC_READ,
  149. &ObjectAttributes
  150. );
  151. if (NT_SUCCESS(Status)) {
  152. Status =
  153. ZwQueryValueKey( Key,
  154. &KeyName,
  155. KeyValuePartialInformation,
  156. KeyValueInformation,
  157. sizeof(KeyValueBuffer),
  158. &ResultLength
  159. );
  160. ZwClose( Key );
  161. }
  162. if (NT_SUCCESS(Status)) {
  163. if ( KeyValueInformation->Type == REG_DWORD &&
  164. KeyValueInformation->DataLength == sizeof(ULONG)
  165. ) {
  166. *Sequence = *(PULONG)KeyValueInformation->Data;
  167. }
  168. else {
  169. Status = STATUS_UNSUCCESSFUL;
  170. }
  171. }
  172. return(Status);
  173. }
  174. NTSTATUS
  175. ExpUuidSaveSequenceNumber(
  176. IN ULONG Sequence
  177. )
  178. /*++
  179. Routine Description:
  180. This function saves the uuid sequence number in the registry. This
  181. value will be read by ExpUuidLoadSequenceNumber during the next boot.
  182. This routine assumes that the current thread has exclusive access
  183. to the the ExpUuid* values.
  184. Arguments:
  185. Sequence - The sequence number to save.
  186. Return Value:
  187. STATUS_SUCCESS
  188. Failure codes from ZwOpenKey() and ZwSetValueKey() maybe returned.
  189. --*/
  190. {
  191. NTSTATUS Status;
  192. OBJECT_ATTRIBUTES ObjectAttributes;
  193. UNICODE_STRING KeyPath, KeyName;
  194. HANDLE Key;
  195. PAGED_CODE();
  196. RtlInitUnicodeString(&KeyPath, RPC_SEQUENCE_NUMBER_PATH);
  197. RtlInitUnicodeString(&KeyName, RPC_SEQUENCE_NUMBER_NAME);
  198. InitializeObjectAttributes( &ObjectAttributes,
  199. &KeyPath,
  200. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  201. NULL,
  202. NULL
  203. );
  204. Status =
  205. ZwOpenKey( &Key,
  206. GENERIC_READ | GENERIC_WRITE,
  207. &ObjectAttributes
  208. );
  209. if (NT_SUCCESS(Status)) {
  210. Status =
  211. ZwSetValueKey( Key,
  212. &KeyName,
  213. 0,
  214. REG_DWORD,
  215. &Sequence,
  216. sizeof(ULONG)
  217. );
  218. ZwClose( Key );
  219. }
  220. return(Status);
  221. }
  222. NTSTATUS
  223. ExpUuidSaveSequenceNumberIf ()
  224. /*++
  225. Routine Description:
  226. This function saves the ExpUuidSequenceNumber, but only
  227. if necessary (as determined by the ExpUuidSequenceNumberNotSaved
  228. flag).
  229. This routine assumes that the current thread has exclusive access
  230. to the ExpUuid* values.
  231. Arguments:
  232. None.
  233. Return Value:
  234. STATUS_SUCCESS if the operation was successful.
  235. --*/
  236. {
  237. NTSTATUS Status = STATUS_SUCCESS;
  238. PAGED_CODE();
  239. // Does the sequence number need to be saved?
  240. if (ExpUuidSequenceNumberNotSaved == TRUE) {
  241. // Print this message just to make sure we aren't hitting the
  242. // registry too much under normal usage.
  243. KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL, "Uuid: Saving new sequence number.\n"));
  244. // Save the sequence number
  245. Status = ExpUuidSaveSequenceNumber(ExpUuidSequenceNumber);
  246. // Indicate that it's now been saved.
  247. if (NT_SUCCESS(Status)) {
  248. ExpUuidSequenceNumberNotSaved = FALSE;
  249. }
  250. }
  251. return( Status );
  252. }
  253. BOOLEAN
  254. ExpUuidInitialization (
  255. VOID
  256. )
  257. /*++
  258. Routine Description:
  259. This function initializes the UUID allocation.
  260. Arguments:
  261. None.
  262. Return Value:
  263. A value of TRUE is returned if the initialization is successfully
  264. completed. Otherwise, a value of FALSE is returned.
  265. --*/
  266. {
  267. PAGED_CODE();
  268. ExInitializeFastMutex(&ExpUuidLock);
  269. ExpUuidSequenceNumberValid = FALSE;
  270. // We can use the current time since we'll be changing the sequence number.
  271. KeQuerySystemTime(&ExpUuidLastTimeAllocated);
  272. return TRUE;
  273. }
  274. NTSTATUS
  275. ExpAllocateUuids (
  276. OUT PLARGE_INTEGER Time,
  277. OUT PULONG Range,
  278. OUT PULONG Sequence
  279. )
  280. /*++
  281. Routine Description:
  282. Allocates a sequence number and a range of times for a set of UUIDs.
  283. The caller can use this together with the network address to
  284. generate complete UUIDs.
  285. This routine assumes that the current thread has exclusive access
  286. to the ExpUuid* values.
  287. Arguments:
  288. Time - Supplies the address of a variable that will receive the
  289. start time (SYSTEMTIME format) of the range of time reserved.
  290. Range - Supplies the address of a variable that will receive the
  291. number of ticks (100ns) reserved after the value in Time.
  292. The range reserved is *Time to (*Time+*Range-1).
  293. Sequence - Supplies the address of a variable that will receive
  294. the time sequence number. This value is used with the associated
  295. range of time to prevent problems with clocks going backwards.
  296. Return Value:
  297. STATUS_SUCCESS is returned if the service is successfully executed.
  298. STATUS_RETRY is returned if we're unable to reserve a range of
  299. UUIDs. This will occur if system clock hasn't advanced
  300. and the allocator is out of cached values.
  301. STATUS_UNSUCCESSFUL is returned if some other service reports
  302. an error, most likly the registery.
  303. --*/
  304. {
  305. NTSTATUS Status;
  306. LARGE_INTEGER CurrentTime;
  307. LARGE_INTEGER AvailableTime;
  308. PAGED_CODE();
  309. //
  310. // Make sure we have a valid sequence number. If not, make one up.
  311. //
  312. if (ExpUuidSequenceNumberValid == FALSE) {
  313. Status = ExpUuidLoadSequenceNumber(&ExpUuidSequenceNumber);
  314. if (!NT_SUCCESS(Status)) {
  315. // Unable read the sequence number, this means we should make one up.
  316. LARGE_INTEGER PerfCounter;
  317. LARGE_INTEGER PerfFrequency;
  318. // This should only happen when we're called
  319. // for the first time on a given machine. (machine, not boot)
  320. KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL, "Uuid: Generating first sequence number.\n"));
  321. PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
  322. ExpUuidSequenceNumber ^= (ULONG)((ULONG_PTR)&Status) ^ PerfCounter.LowPart ^
  323. PerfCounter.HighPart ^ (ULONG)((ULONG_PTR)Sequence);
  324. }
  325. else {
  326. // We increment the sequence number on every boot.
  327. ExpUuidSequenceNumber++;
  328. }
  329. ExpUuidSequenceNumberValid = TRUE;
  330. ExpUuidSequenceNumberNotSaved = TRUE;
  331. }
  332. //
  333. // Get the current time, usually we will have plenty of avaliable
  334. // to give the caller. But we may need to deal with time going
  335. // backwards and really fast machines.
  336. //
  337. KeQuerySystemTime(&CurrentTime);
  338. AvailableTime.QuadPart = CurrentTime.QuadPart - ExpUuidLastTimeAllocated.QuadPart;
  339. if (AvailableTime.QuadPart < 0) {
  340. // Time has been set time backwards. This means that we must make sure
  341. // that somebody increments the sequence number and saves the new
  342. // sequence number in the registry.
  343. ExpUuidSequenceNumberNotSaved = TRUE;
  344. ExpUuidSequenceNumber++;
  345. // The sequence number has been changed, so it's now okay to set time
  346. // backwards. Since time is going backwards anyway, it's okay to set
  347. // it back an extra millisecond or two.
  348. ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - 20000;
  349. AvailableTime.QuadPart = 20000;
  350. }
  351. if (AvailableTime.QuadPart == 0) {
  352. // System time hasn't moved. The caller should yield the CPU and retry.
  353. return(STATUS_RETRY);
  354. }
  355. //
  356. // Common case, time has moved forward.
  357. //
  358. if (AvailableTime.QuadPart > 10*1000*1000) {
  359. // We never want to give out really old (> 1 second) Uuids.
  360. AvailableTime.QuadPart = 10*1000*1000;
  361. }
  362. if (AvailableTime.QuadPart > 10*1000) {
  363. // We've got over a millisecond to give out. We'll save some time for
  364. // another caller so that we can avoid returning STATUS_RETRY very often.
  365. *Range = 10*1000;
  366. AvailableTime.QuadPart -= 10*1000;
  367. }
  368. else {
  369. // Not much time avaiable, give it all away.
  370. *Range = (ULONG)AvailableTime.QuadPart;
  371. AvailableTime.QuadPart = 0;
  372. }
  373. Time->QuadPart = CurrentTime.QuadPart - (*Range + AvailableTime.QuadPart);
  374. ExpUuidLastTimeAllocated.QuadPart = Time->QuadPart + *Range;
  375. // Last time allocated is just after the range we hand back to the caller
  376. // this may be almost a second behind the true system time.
  377. *Sequence = ExpUuidSequenceNumber;
  378. return(STATUS_SUCCESS);
  379. }
  380. #define SEED_SIZE 6 * sizeof(CHAR)
  381. NTSTATUS
  382. NtSetUuidSeed (
  383. IN PCHAR Seed
  384. )
  385. /*++
  386. Routine Description:
  387. This routine is used to set the seed used for UUID generation. The seed
  388. will be set by RPCSS at startup and each time a card is replaced.
  389. Arguments:
  390. Seed - Pointer to a six byte buffer
  391. Return Value:
  392. STATUS_SUCCESS is returned if the service is successfully executed.
  393. STATUS_ACCESS_DENIED If caller doesn't have the permissions to make this call.
  394. You need to be logged on as Local System in order to call this API.
  395. STATUS_ACCESS_VIOLATION is returned if the Seed could not be read.
  396. --*/
  397. {
  398. NTSTATUS Status;
  399. LUID AuthenticationId;
  400. SECURITY_SUBJECT_CONTEXT SubjectContext;
  401. LUID SystemLuid = SYSTEM_LUID;
  402. BOOLEAN CapturedSubjectContext = FALSE;
  403. PAGED_CODE();
  404. ASSERT(KeGetPreviousMode() != KernelMode);
  405. try {
  406. //
  407. // Check if the caller has the appropriate permission
  408. //
  409. SeCaptureSubjectContext(&SubjectContext);
  410. CapturedSubjectContext = TRUE;
  411. Status = SeQueryAuthenticationIdToken(
  412. SeQuerySubjectContextToken(&SubjectContext),
  413. &AuthenticationId);
  414. if (!NT_SUCCESS(Status)) {
  415. ExRaiseStatus(Status);
  416. }
  417. if (RtlCompareMemory(&AuthenticationId, &SystemLuid, sizeof(LUID)) != sizeof(LUID)) {
  418. ExRaiseStatus(STATUS_ACCESS_DENIED);
  419. }
  420. //
  421. // Store the UUID seed
  422. //
  423. ProbeForReadSmallStructure(Seed, SEED_SIZE, sizeof(CHAR));
  424. RtlCopyMemory(&ExpUuidCachedValues.NodeId[0], Seed, SEED_SIZE);
  425. if ((Seed[0] & 0x80) == 0)
  426. {
  427. // If the high bit is not set the NodeId is a valid IEEE 802
  428. // address and should be globally unique.
  429. ExpUuidCacheValid = CACHE_VALID;
  430. }
  431. else
  432. {
  433. ExpUuidCacheValid = CACHE_LOCAL_ONLY;
  434. }
  435. Status = STATUS_SUCCESS;
  436. }
  437. except (EXCEPTION_EXECUTE_HANDLER) {
  438. Status = GetExceptionCode();
  439. }
  440. if (CapturedSubjectContext) {
  441. SeReleaseSubjectContext( &SubjectContext );
  442. }
  443. return Status;
  444. }
  445. NTSTATUS
  446. NtAllocateUuids (
  447. OUT PULARGE_INTEGER Time,
  448. OUT PULONG Range,
  449. OUT PULONG Sequence,
  450. OUT PCHAR Seed
  451. )
  452. /*++
  453. Routine Description:
  454. This function reserves a range of time for the caller(s) to use for
  455. handing out Uuids. As far a possible the same range of time and
  456. sequence number will never be given out.
  457. (It's possible to reboot 2^14-1 times and set the clock backwards and then
  458. call this allocator and get a duplicate. Since only the low 14bits of the
  459. sequence number are used in a real uuid.)
  460. Arguments:
  461. Time - Supplies the address of a variable that will receive the
  462. start time (SYSTEMTIME format) of the range of time reserved.
  463. Range - Supplies the address of a variable that will receive the
  464. number of ticks (100ns) reserved after the value in Time.
  465. The range reserved is *Time to (*Time + *Range - 1).
  466. Sequence - Supplies the address of a variable that will receive
  467. the time sequence number. This value is used with the associated
  468. range of time to prevent problems with clocks going backwards.
  469. Seed - Pointer to a 6 byte buffer. The current seed is written into this buffer.
  470. Return Value:
  471. STATUS_SUCCESS is returned if the service is successfully executed.
  472. STATUS_RETRY is returned if we're unable to reserve a range of
  473. UUIDs. This may (?) occur if system clock hasn't advanced
  474. and the allocator is out of cached values.
  475. STATUS_ACCESS_VIOLATION is returned if the output parameter for the
  476. UUID cannot be written.
  477. STATUS_UNSUCCESSFUL is returned if some other service reports
  478. an error, most likly the registery.
  479. --*/
  480. {
  481. KPROCESSOR_MODE PreviousMode;
  482. NTSTATUS Status;
  483. LARGE_INTEGER OutputTime;
  484. ULONG OutputRange;
  485. ULONG OutputSequence;
  486. PKTHREAD CurrentThread;
  487. PAGED_CODE();
  488. //
  489. // Establish an exception handler and attempt to write the output
  490. // arguments. If the write attempt fails, then return
  491. // the exception code as the service status. Otherwise return success
  492. // as the service status.
  493. //
  494. try {
  495. //
  496. // Get previous processor mode and probe arguments if necessary.
  497. //
  498. PreviousMode = KeGetPreviousMode();
  499. if (PreviousMode != KernelMode) {
  500. ProbeForWriteSmallStructure((PVOID)Time, sizeof(LARGE_INTEGER), sizeof(ULONG));
  501. ProbeForWriteSmallStructure((PVOID)Range, sizeof(ULONG), sizeof(ULONG));
  502. ProbeForWriteSmallStructure((PVOID)Sequence, sizeof(ULONG), sizeof(ULONG));
  503. ProbeForWriteSmallStructure((PVOID)Seed, SEED_SIZE, sizeof(CHAR));
  504. }
  505. } except (ExSystemExceptionFilter()) {
  506. return GetExceptionCode();
  507. }
  508. // Take the lock, because we're about to update the UUID cache.
  509. CurrentThread = KeGetCurrentThread ();
  510. KeEnterCriticalRegionThread(CurrentThread);
  511. ExAcquireFastMutexUnsafe(&ExpUuidLock);
  512. // Get the sequence number and a range of times that can
  513. // be used in UUID-generation.
  514. Status = ExpAllocateUuids( &OutputTime, &OutputRange, &OutputSequence );
  515. if( !NT_SUCCESS(Status) ) {
  516. ExReleaseFastMutexUnsafe(&ExpUuidLock);
  517. KeLeaveCriticalRegionThread(CurrentThread);
  518. return( Status );
  519. }
  520. // If necessary, save the sequence number. If there's an error,
  521. // we'll just leave it marked as dirty, and retry on some future call.
  522. ExpUuidSaveSequenceNumberIf();
  523. // Release the lock
  524. ExReleaseFastMutexUnsafe(&ExpUuidLock);
  525. KeLeaveCriticalRegionThread(CurrentThread);
  526. //
  527. // Attempt to store the result of this call into the output parameters.
  528. // This is done within an exception handler in case output parameters
  529. // are now invalid.
  530. //
  531. try {
  532. Time->QuadPart = OutputTime.QuadPart;
  533. *Range = OutputRange;
  534. *Sequence = OutputSequence;
  535. RtlCopyMemory((PVOID) Seed, &ExpUuidCachedValues.NodeId[0], SEED_SIZE);
  536. } except (ExSystemExceptionFilter()) {
  537. return GetExceptionCode();
  538. }
  539. return(STATUS_SUCCESS);
  540. }
  541. NTSTATUS
  542. ExpUuidGetValues(
  543. OUT UUID_CACHED_VALUES_STRUCT *Values
  544. )
  545. /*++
  546. Routine Description:
  547. This routine allocates a block of UUIDs and stores them in
  548. the caller-provided cached-values structure.
  549. This routine assumes that the current thread has exclusive
  550. access to the ExpUuid* values.
  551. Note that the Time value in this cache is different than the
  552. Time value returned by NtAllocateUuids (and ExpAllocateUuids).
  553. As a result, the cache must be interpreted differently in
  554. order to determine the valid range. The valid range from
  555. these two routines is:
  556. NtAllocateUuids: [ Time, Time+Range )
  557. ExpUuidGetValues: ( Values.Time-Values.Range, Values.Time ]
  558. Arguments:
  559. Values - Set to contain everything needed to allocate a block of uuids.
  560. Return Value:
  561. STATUS_SUCCESS is returned if the service is successfully executed.
  562. STATUS_RETRY is returned if we're unable to reserve a range of
  563. UUIDs. This will occur if system clock hasn't advanced
  564. and the allocator is out of cached values.
  565. STATUS_NO_MEMORY is returned if we're unable to reserve a range
  566. of UUIDs, for some reason other than the clock not advancing.
  567. --*/
  568. {
  569. NTSTATUS Status;
  570. LARGE_INTEGER Time;
  571. ULONG Range;
  572. ULONG Sequence;
  573. PAGED_CODE();
  574. // Allocate a range of times for use in UUIDs.
  575. Status = ExpAllocateUuids(&Time, &Range, &Sequence);
  576. if (STATUS_RETRY == Status) {
  577. return(Status);
  578. }
  579. else if (!NT_SUCCESS(Status)) {
  580. return(STATUS_NO_MEMORY);
  581. }
  582. // ExpAllocateUuids keeps time in SYSTEM_TIME format which is 100ns ticks since
  583. // Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582.
  584. // 17 Days in Oct + 30 (Nov) + 31 (Dec) + 18 years and 5 leap days.
  585. Time.QuadPart += (ULONGLONG) (1000*1000*10) // seconds
  586. * (ULONGLONG) (60 * 60 * 24) // days
  587. * (ULONGLONG) (17+30+31+365*18+5); // # of days
  588. ASSERT(Range);
  589. Values->ClockSeqHiAndReserved =
  590. UUID_RESERVED | (((UCHAR) (Sequence >> 8))
  591. & (UCHAR) UUID_CLOCK_SEQ_HI_MASK);
  592. Values->ClockSeqLow = (UCHAR) (Sequence & 0x00FF);
  593. // We'll modify the Time value so that it indicates the
  594. // end of the range rather than the beginning of it.
  595. // The order of these assignments is important
  596. Values->Time = Time.QuadPart + (Range - 1);
  597. Values->AllocatedCount = Range;
  598. return(STATUS_SUCCESS);
  599. }
  600. NTSTATUS
  601. ExUuidCreate(
  602. OUT UUID *Uuid
  603. )
  604. /*++
  605. Routine Description:
  606. This routine creates a DCE UUID and returns it in the caller's
  607. buffer.
  608. Arguments:
  609. Uuid - will receive the UUID.
  610. Return Value:
  611. STATUS_SUCCESS is returned if the service is successfully executed.
  612. STATUS_RETRY is returned if we're unable to reserve a range of
  613. UUIDs. This will occur if system clock hasn't advanced
  614. and the allocator is out of cached values.
  615. --*/
  616. {
  617. NTSTATUS Status = STATUS_SUCCESS;
  618. UUID_GENERATE *UuidGen = (UUID_GENERATE *) Uuid;
  619. ULONGLONG Time;
  620. LONG Delta;
  621. PKTHREAD CurrentThread;
  622. PAGED_CODE();
  623. //
  624. // Get a value from the cache. If the cache is empty, we'll fill
  625. // it and retry. The first time cache will be empty.
  626. //
  627. CurrentThread = KeGetCurrentThread ();
  628. for(;;) {
  629. // Get the highest value in the cache (though it may not
  630. // be available).
  631. Time = ExpUuidCachedValues.Time;
  632. // Copy the static info into the UUID. We can't do this later
  633. // because the clock sequence could be updated by another thread.
  634. *(PULONG)&UuidGen->ClockSeqHiAndReserved =
  635. *(PULONG)&ExpUuidCachedValues.ClockSeqHiAndReserved;
  636. *(PULONG)&UuidGen->NodeId[2] =
  637. *(PULONG)&ExpUuidCachedValues.NodeId[2];
  638. // See what we need to subtract from Time to get a valid GUID.
  639. Delta = InterlockedDecrement(&ExpUuidCachedValues.AllocatedCount);
  640. if (Time != ExpUuidCachedValues.Time) {
  641. // If our captured time doesn't match the cache then another
  642. // thread already took the lock and updated the cache. We'll
  643. // just loop and try again.
  644. continue;
  645. }
  646. // If the cache hadn't already run dry, we can break out of this retry
  647. // loop.
  648. if (Delta >= 0) {
  649. break;
  650. }
  651. //
  652. // Allocate a new block of Uuids.
  653. //
  654. // Take the cache lock
  655. KeEnterCriticalRegionThread(CurrentThread);
  656. ExAcquireFastMutexUnsafe(&ExpUuidLock);
  657. // If the cache has already been updated, try again.
  658. if (Time != ExpUuidCachedValues.Time) {
  659. // Release the lock
  660. ExReleaseFastMutexUnsafe(&ExpUuidLock);
  661. KeLeaveCriticalRegionThread(CurrentThread);
  662. continue;
  663. }
  664. // Update the cache.
  665. Status = ExpUuidGetValues( &ExpUuidCachedValues );
  666. if (Status != STATUS_SUCCESS) {
  667. // Release the lock
  668. ExReleaseFastMutexUnsafe(&ExpUuidLock);
  669. KeLeaveCriticalRegionThread(CurrentThread);
  670. return(Status);
  671. }
  672. // The sequence number may have been dirtied, see if it needs
  673. // to be saved. If there's an error, we'll ignore it and
  674. // retry on a future call.
  675. ExpUuidSaveSequenceNumberIf();
  676. // Release the lock
  677. ExReleaseFastMutexUnsafe(&ExpUuidLock);
  678. KeLeaveCriticalRegionThread(CurrentThread);
  679. // Loop
  680. }
  681. // Adjust the time to that of the next available UUID.
  682. Time -= Delta;
  683. // Finish filling in the UUID.
  684. UuidGen->TimeLow = (ULONG) Time;
  685. UuidGen->TimeMid = (USHORT) (Time >> 32);
  686. UuidGen->TimeHiAndVersion = (USHORT)
  687. (( (USHORT)(Time >> (32+16))
  688. & UUID_TIME_HIGH_MASK) | UUID_VERSION);
  689. ASSERT(Status == STATUS_SUCCESS);
  690. if (ExpUuidCacheValid == CACHE_LOCAL_ONLY) {
  691. Status = RPC_NT_UUID_LOCAL_ONLY;
  692. }
  693. return(Status);
  694. }