1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/rxact.c 5 * PURPOSE: Registry Transaction API 6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <rtl.h> 12 #include <ndk/cmfuncs.h> 13 14 #define NDEBUG 15 #include <debug.h> 16 17 #define RXACT_DEFAULT_BUFFER_SIZE (4 * PAGE_SIZE) 18 19 typedef struct _RXACT_INFO 20 { 21 ULONG Revision; 22 ULONG Unknown1; 23 ULONG Unknown2; 24 } RXACT_INFO, *PRXACT_INFO; 25 26 typedef struct _RXACT_DATA 27 { 28 ULONG ActionCount; 29 ULONG BufferSize; 30 ULONG CurrentSize; 31 } RXACT_DATA, *PRXACT_DATA; 32 33 typedef struct _RXACT_CONTEXT 34 { 35 HANDLE RootDirectory; 36 HANDLE KeyHandle; 37 BOOLEAN CanUseHandles; 38 PRXACT_DATA Data; 39 } RXACT_CONTEXT, *PRXACT_CONTEXT; 40 41 typedef struct _RXACT_ACTION 42 { 43 ULONG Size; 44 ULONG Type; 45 UNICODE_STRING KeyName; 46 UNICODE_STRING ValueName; 47 HANDLE KeyHandle; 48 ULONG ValueType; 49 ULONG ValueDataSize; 50 PVOID ValueData; 51 } RXACT_ACTION, *PRXACT_ACTION; 52 53 enum 54 { 55 RXactDeleteKey = 1, 56 RXactSetValueKey = 2, 57 }; 58 59 /* FUNCTIONS *****************************************************************/ 60 61 static 62 VOID 63 NTAPI 64 RXactInitializeContext( 65 PRXACT_CONTEXT Context, 66 HANDLE RootDirectory, 67 HANDLE KeyHandle) 68 { 69 Context->Data = NULL; 70 Context->RootDirectory = RootDirectory; 71 Context->CanUseHandles = TRUE; 72 Context->KeyHandle = KeyHandle; 73 } 74 75 static 76 NTSTATUS 77 NTAPI 78 RXactpOpenTargetKey( 79 HANDLE RootDirectory, 80 ULONG ActionType, 81 PUNICODE_STRING KeyName, 82 PHANDLE KeyHandle) 83 { 84 NTSTATUS Status; 85 ULONG Disposition; 86 OBJECT_ATTRIBUTES ObjectAttributes; 87 88 /* Check what kind of action this is */ 89 if (ActionType == RXactDeleteKey) 90 { 91 /* This is a delete, so open the key for delete */ 92 InitializeObjectAttributes(&ObjectAttributes, 93 KeyName, 94 OBJ_CASE_INSENSITIVE, 95 RootDirectory, 96 NULL); 97 Status = ZwOpenKey(KeyHandle, DELETE, &ObjectAttributes); 98 } 99 else if (ActionType == RXactSetValueKey) 100 { 101 /* This is a create, so open or create with write access */ 102 InitializeObjectAttributes(&ObjectAttributes, 103 KeyName, 104 OBJ_CASE_INSENSITIVE | OBJ_OPENIF, 105 RootDirectory, 106 NULL); 107 Status = ZwCreateKey(KeyHandle, 108 KEY_WRITE, 109 &ObjectAttributes, 110 0, 111 NULL, 112 0, 113 &Disposition); 114 } 115 else 116 { 117 return STATUS_INVALID_PARAMETER; 118 } 119 120 return Status; 121 } 122 123 static 124 NTSTATUS 125 NTAPI 126 RXactpCommit( 127 PRXACT_CONTEXT Context) 128 { 129 PRXACT_DATA Data; 130 PRXACT_ACTION Action; 131 NTSTATUS Status, TmpStatus; 132 HANDLE KeyHandle; 133 ULONG i; 134 135 Data = Context->Data; 136 137 /* The first action record starts after the data header */ 138 Action = (PRXACT_ACTION)(Data + 1); 139 140 /* Loop all recorded actions */ 141 for (i = 0; i < Data->ActionCount; i++) 142 { 143 /* Translate relative offsets to actual pointers */ 144 Action->KeyName.Buffer = (PWSTR)((PUCHAR)Data + (ULONG_PTR)Action->KeyName.Buffer); 145 Action->ValueName.Buffer = (PWSTR)((PUCHAR)Data + (ULONG_PTR)Action->ValueName.Buffer); 146 Action->ValueData = (PUCHAR)Data + (ULONG_PTR)Action->ValueData; 147 148 /* Check what kind of action this is */ 149 if (Action->Type == RXactDeleteKey) 150 { 151 /* This is a delete action. Check if we can use a handle */ 152 if ((Action->KeyHandle != INVALID_HANDLE_VALUE) && Context->CanUseHandles) 153 { 154 /* Delete the key by the given handle */ 155 Status = ZwDeleteKey(Action->KeyHandle); 156 if (!NT_SUCCESS(Status)) 157 { 158 return Status; 159 } 160 } 161 else 162 { 163 /* We cannot use a handle, open the key first by it's name */ 164 Status = RXactpOpenTargetKey(Context->RootDirectory, 165 RXactDeleteKey, 166 &Action->KeyName, 167 &KeyHandle); 168 if (NT_SUCCESS(Status)) 169 { 170 Status = ZwDeleteKey(KeyHandle); 171 TmpStatus = NtClose(KeyHandle); 172 ASSERT(NT_SUCCESS(TmpStatus)); 173 if (!NT_SUCCESS(Status)) 174 { 175 return Status; 176 } 177 } 178 else 179 { 180 /* Failed to open the key, it's ok, if it was not found */ 181 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) 182 return Status; 183 } 184 } 185 } 186 else if (Action->Type == RXactSetValueKey) 187 { 188 /* This is a set action. Check if we can use a handle */ 189 if ((Action->KeyHandle != INVALID_HANDLE_VALUE) && Context->CanUseHandles) 190 { 191 /* Set the key value using the given key handle */ 192 Status = ZwSetValueKey(Action->KeyHandle, 193 &Action->ValueName, 194 0, 195 Action->ValueType, 196 Action->ValueData, 197 Action->ValueDataSize); 198 if (!NT_SUCCESS(Status)) 199 { 200 return Status; 201 } 202 } 203 else 204 { 205 /* We cannot use a handle, open the key first by it's name */ 206 Status = RXactpOpenTargetKey(Context->RootDirectory, 207 RXactSetValueKey, 208 &Action->KeyName, 209 &KeyHandle); 210 if (!NT_SUCCESS(Status)) 211 { 212 return Status; 213 } 214 215 /* Set the key value */ 216 Status = ZwSetValueKey(KeyHandle, 217 &Action->ValueName, 218 0, 219 Action->ValueType, 220 Action->ValueData, 221 Action->ValueDataSize); 222 223 TmpStatus = NtClose(KeyHandle); 224 ASSERT(NT_SUCCESS(TmpStatus)); 225 226 if (!NT_SUCCESS(Status)) 227 { 228 return Status; 229 } 230 } 231 } 232 else 233 { 234 ASSERT(FALSE); 235 return STATUS_INVALID_PARAMETER; 236 } 237 238 /* Go to the next action record */ 239 Action = (PRXACT_ACTION)((PUCHAR)Action + Action->Size); 240 } 241 242 return STATUS_SUCCESS; 243 } 244 245 NTSTATUS 246 NTAPI 247 RtlStartRXact( 248 PRXACT_CONTEXT Context) 249 { 250 PRXACT_DATA Buffer; 251 252 /* We must not have a buffer yet */ 253 if (Context->Data != NULL) 254 { 255 return STATUS_RXACT_INVALID_STATE; 256 } 257 258 /* Allocate a buffer */ 259 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, RXACT_DEFAULT_BUFFER_SIZE); 260 if (Buffer == NULL) 261 { 262 return STATUS_NO_MEMORY; 263 } 264 265 /* Initialize the buffer */ 266 Buffer->ActionCount = 0; 267 Buffer->BufferSize = RXACT_DEFAULT_BUFFER_SIZE; 268 Buffer->CurrentSize = sizeof(RXACT_DATA); 269 Context->Data = Buffer; 270 271 return STATUS_SUCCESS; 272 } 273 274 NTSTATUS 275 NTAPI 276 RtlAbortRXact( 277 PRXACT_CONTEXT Context) 278 { 279 /* We must have a data buffer */ 280 if (Context->Data == NULL) 281 { 282 return STATUS_RXACT_INVALID_STATE; 283 } 284 285 /* Free the buffer */ 286 RtlFreeHeap(RtlGetProcessHeap(), 0, Context->Data); 287 288 /* Reinitialize the context */ 289 RXactInitializeContext(Context, Context->RootDirectory, Context->KeyHandle); 290 291 return STATUS_SUCCESS; 292 } 293 294 NTSTATUS 295 NTAPI 296 RtlInitializeRXact( 297 HANDLE RootDirectory, 298 BOOLEAN Commit, 299 PRXACT_CONTEXT *OutContext) 300 { 301 NTSTATUS Status, TmpStatus; 302 PRXACT_CONTEXT Context; 303 PKEY_VALUE_FULL_INFORMATION KeyValueInformation; 304 KEY_VALUE_BASIC_INFORMATION KeyValueBasicInfo; 305 UNICODE_STRING ValueName; 306 UNICODE_STRING KeyName; 307 OBJECT_ATTRIBUTES ObjectAttributes; 308 RXACT_INFO TransactionInfo; 309 ULONG Disposition; 310 ULONG ValueType; 311 ULONG ValueDataLength; 312 ULONG Length; 313 HANDLE KeyHandle; 314 315 /* Open or create the 'RXACT' key in the root directory */ 316 RtlInitUnicodeString(&KeyName, L"RXACT"); 317 InitializeObjectAttributes(&ObjectAttributes, 318 &KeyName, 319 OBJ_CASE_INSENSITIVE | OBJ_OPENIF, 320 RootDirectory, 321 NULL); 322 Status = ZwCreateKey(&KeyHandle, 323 KEY_READ | KEY_WRITE | DELETE, 324 &ObjectAttributes, 325 0, 326 NULL, 327 0, 328 &Disposition); 329 if (!NT_SUCCESS(Status)) 330 { 331 return Status; 332 } 333 334 /* Allocate a new context */ 335 Context = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Context)); 336 *OutContext = Context; 337 if (Context == NULL) 338 { 339 TmpStatus = ZwDeleteKey(KeyHandle); 340 ASSERT(NT_SUCCESS(TmpStatus)); 341 342 TmpStatus = NtClose(KeyHandle); 343 ASSERT(NT_SUCCESS(TmpStatus)); 344 345 return STATUS_NO_MEMORY; 346 } 347 348 /* Initialize the context */ 349 RXactInitializeContext(Context, RootDirectory, KeyHandle); 350 351 /* Check if we created a new key */ 352 if (Disposition == REG_CREATED_NEW_KEY) 353 { 354 /* The key is new, set the default value */ 355 TransactionInfo.Revision = 1; 356 RtlInitUnicodeString(&ValueName, NULL); 357 Status = ZwSetValueKey(KeyHandle, 358 &ValueName, 359 0, 360 REG_NONE, 361 &TransactionInfo, 362 sizeof(TransactionInfo)); 363 if (!NT_SUCCESS(Status)) 364 { 365 TmpStatus = ZwDeleteKey(KeyHandle); 366 ASSERT(NT_SUCCESS(TmpStatus)); 367 368 TmpStatus = NtClose(KeyHandle); 369 ASSERT(NT_SUCCESS(TmpStatus)); 370 371 RtlFreeHeap(RtlGetProcessHeap(), 0, *OutContext); 372 return Status; 373 } 374 375 return STATUS_RXACT_STATE_CREATED; 376 } 377 else 378 { 379 /* The key exited, get the default key value */ 380 ValueDataLength = sizeof(TransactionInfo); 381 Status = RtlpNtQueryValueKey(KeyHandle, 382 &ValueType, 383 &TransactionInfo, 384 &ValueDataLength, 385 0); 386 if (!NT_SUCCESS(Status)) 387 { 388 TmpStatus = NtClose(KeyHandle); 389 ASSERT(NT_SUCCESS(TmpStatus)); 390 RtlFreeHeap(RtlGetProcessHeap(), 0, Context); 391 return Status; 392 } 393 394 /* Check if the value date is valid */ 395 if ((ValueDataLength != sizeof(TransactionInfo)) || 396 (TransactionInfo.Revision != 1)) 397 { 398 TmpStatus = NtClose(KeyHandle); 399 ASSERT(NT_SUCCESS(TmpStatus)); 400 RtlFreeHeap(RtlGetProcessHeap(), 0, Context); 401 return STATUS_UNKNOWN_REVISION; 402 } 403 404 /* Query the 'Log' key value */ 405 RtlInitUnicodeString(&ValueName, L"Log"); 406 Status = ZwQueryValueKey(KeyHandle, 407 &ValueName, 408 KeyValueBasicInformation, 409 &KeyValueBasicInfo, 410 sizeof(KeyValueBasicInfo), 411 &Length); 412 if (!NT_SUCCESS(Status)) 413 { 414 /* There is no 'Log', so we are done */ 415 return STATUS_SUCCESS; 416 } 417 418 /* Check if the caller asked to commit the current state */ 419 if (!Commit) 420 { 421 /* We have a log, that must be committed first! */ 422 return STATUS_RXACT_COMMIT_NECESSARY; 423 } 424 425 /* Query the size of the 'Log' key value */ 426 Status = ZwQueryValueKey(KeyHandle, 427 &ValueName, 428 KeyValueFullInformation, 429 NULL, 430 0, 431 &Length); 432 if (Status != STATUS_BUFFER_TOO_SMALL) 433 { 434 return Status; 435 } 436 437 /* Allocate a buffer for the key value information */ 438 KeyValueInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length); 439 if (KeyValueInformation == NULL) 440 { 441 return STATUS_NO_MEMORY; 442 } 443 444 /* Query the 'Log' key value */ 445 Status = ZwQueryValueKey(KeyHandle, 446 &ValueName, 447 KeyValueFullInformation, 448 KeyValueInformation, 449 Length, 450 &Length); 451 if (!NT_SUCCESS(Status)) 452 { 453 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyValueInformation); 454 RtlFreeHeap(RtlGetProcessHeap(), 0, Context); 455 return Status; 456 } 457 458 /* Set the Data pointer to the key value data */ 459 Context->Data = (PRXACT_DATA)((PUCHAR)KeyValueInformation + 460 KeyValueInformation->DataOffset); 461 462 /* This is an old log, don't use handles when committing! */ 463 Context->CanUseHandles = FALSE; 464 465 /* Commit the data */ 466 Status = RXactpCommit(Context); 467 if (!NT_SUCCESS(Status)) 468 { 469 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyValueInformation); 470 RtlFreeHeap(RtlGetProcessHeap(), 0, Context); 471 return Status; 472 } 473 474 /* Delete the old key */ 475 Status = NtDeleteValueKey(KeyHandle, &ValueName); 476 ASSERT(NT_SUCCESS(Status)); 477 478 /* Set the data member to the allocated buffer, so it will get freed */ 479 Context->Data = (PRXACT_DATA)KeyValueInformation; 480 481 /* Abort the old transaction */ 482 Status = RtlAbortRXact(Context); 483 ASSERT(NT_SUCCESS(Status)); 484 485 return Status; 486 } 487 } 488 489 NTSTATUS 490 NTAPI 491 RtlAddAttributeActionToRXact( 492 PRXACT_CONTEXT Context, 493 ULONG ActionType, 494 PUNICODE_STRING KeyName, 495 HANDLE KeyHandle, 496 PUNICODE_STRING ValueName, 497 ULONG ValueType, 498 PVOID ValueData, 499 ULONG ValueDataSize) 500 { 501 ULONG ActionSize; 502 ULONG RequiredSize; 503 ULONG BufferSize; 504 ULONG CurrentOffset; 505 PRXACT_DATA NewData; 506 PRXACT_ACTION Action; 507 508 /* Validate ActionType parameter */ 509 if ((ActionType != RXactDeleteKey) && (ActionType != RXactSetValueKey)) 510 { 511 return STATUS_INVALID_PARAMETER; 512 } 513 514 /* Calculate the size of the new action record */ 515 ActionSize = ALIGN_UP_BY(ValueName->Length, sizeof(ULONG)) + 516 ALIGN_UP_BY(ValueDataSize, sizeof(ULONG)) + 517 ALIGN_UP_BY(KeyName->Length, sizeof(ULONG)) + 518 ALIGN_UP_BY(sizeof(RXACT_ACTION), sizeof(ULONG)); 519 520 /* Calculate the new buffer size we need */ 521 RequiredSize = ActionSize + Context->Data->CurrentSize; 522 523 /* Check for integer overflow */ 524 if (RequiredSize < ActionSize) 525 { 526 return STATUS_NO_MEMORY; 527 } 528 529 /* Check if the buffer is large enough */ 530 BufferSize = Context->Data->BufferSize; 531 if (RequiredSize > BufferSize) 532 { 533 /* Increase by a factor of 2, until it is large enough */ 534 while (BufferSize < RequiredSize) 535 { 536 BufferSize *= 2; 537 } 538 539 /* Allocate a new buffer from the heap */ 540 NewData = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize); 541 if (NewData == NULL) 542 { 543 return STATUS_NO_MEMORY; 544 } 545 546 /* Copy the old buffer to the new one */ 547 RtlCopyMemory(NewData, Context->Data, Context->Data->CurrentSize); 548 549 /* Free the old buffer and use the new one */ 550 RtlFreeHeap(RtlGetProcessHeap(), 0, Context->Data); 551 Context->Data = NewData; 552 NewData->BufferSize = BufferSize; 553 } 554 555 /* Get the next action record */ 556 Action = (RXACT_ACTION *)((PUCHAR)Context->Data + Context->Data->CurrentSize); 557 558 /* Fill in the fields */ 559 Action->Size = ActionSize; 560 Action->Type = ActionType; 561 Action->KeyName = *KeyName; 562 Action->ValueName = *ValueName; 563 Action->ValueType = ValueType; 564 Action->ValueDataSize = ValueDataSize; 565 Action->KeyHandle = KeyHandle; 566 567 /* Copy the key name (and convert the pointer to a buffer offset) */ 568 CurrentOffset = Context->Data->CurrentSize + sizeof(RXACT_ACTION); 569 Action->KeyName.Buffer = UlongToPtr(CurrentOffset); 570 RtlCopyMemory((PUCHAR)Context->Data + CurrentOffset, 571 KeyName->Buffer, 572 KeyName->Length); 573 574 /* Copy the value name (and convert the pointer to a buffer offset) */ 575 CurrentOffset += ALIGN_UP_BY(KeyName->Length, sizeof(ULONG)); 576 Action->ValueName.Buffer = UlongToPtr(CurrentOffset); 577 RtlCopyMemory((PUCHAR)Context->Data + CurrentOffset, 578 ValueName->Buffer, 579 ValueName->Length); 580 581 /* Update the offset */ 582 CurrentOffset += ALIGN_UP_BY(ValueName->Length, sizeof(ULONG)); 583 584 /* Is this a set action? */ 585 if (ActionType == RXactSetValueKey) 586 { 587 /* Copy the key value data as well */ 588 Action->ValueData = UlongToPtr(CurrentOffset); 589 RtlCopyMemory((PUCHAR)Context->Data + CurrentOffset, 590 ValueData, 591 ValueDataSize); 592 CurrentOffset += ALIGN_UP_BY(ValueDataSize, sizeof(ULONG)); 593 } 594 595 /* Update data site and action count */ 596 Context->Data->CurrentSize = CurrentOffset; 597 Context->Data->ActionCount++; 598 599 return STATUS_SUCCESS; 600 } 601 602 NTSTATUS 603 NTAPI 604 RtlAddActionToRXact( 605 PRXACT_CONTEXT Context, 606 ULONG ActionType, 607 PUNICODE_STRING KeyName, 608 ULONG ValueType, 609 PVOID ValueData, 610 ULONG ValueDataSize) 611 { 612 UNICODE_STRING ValueName; 613 614 /* Create a key and set the default key value or delete a key. */ 615 RtlInitUnicodeString(&ValueName, NULL); 616 return RtlAddAttributeActionToRXact(Context, 617 ActionType, 618 KeyName, 619 INVALID_HANDLE_VALUE, 620 &ValueName, 621 ValueType, 622 ValueData, 623 ValueDataSize); 624 } 625 626 NTSTATUS 627 NTAPI 628 RtlApplyRXactNoFlush( 629 PRXACT_CONTEXT Context) 630 { 631 NTSTATUS Status; 632 633 /* Commit the transaction */ 634 Status = RXactpCommit(Context); 635 if (!NT_SUCCESS(Status)) 636 { 637 return Status; 638 } 639 640 /* Reset the transaction */ 641 Status = RtlAbortRXact(Context); 642 ASSERT(NT_SUCCESS(Status)); 643 644 return Status; 645 } 646 647 NTSTATUS 648 NTAPI 649 RtlApplyRXact( 650 PRXACT_CONTEXT Context) 651 { 652 UNICODE_STRING ValueName; 653 NTSTATUS Status; 654 655 /* Temporarily safe the current transaction in the 'Log' key value */ 656 RtlInitUnicodeString(&ValueName, L"Log"); 657 Status = ZwSetValueKey(Context->KeyHandle, 658 &ValueName, 659 0, 660 REG_BINARY, 661 Context->Data, 662 Context->Data->CurrentSize); 663 if (!NT_SUCCESS(Status)) 664 { 665 return Status; 666 } 667 668 /* Flush the key */ 669 Status = NtFlushKey(Context->KeyHandle); 670 if (!NT_SUCCESS(Status)) 671 { 672 NtDeleteValueKey(Context->KeyHandle, &ValueName); 673 return Status; 674 } 675 676 /* Now commit the transaction */ 677 Status = RXactpCommit(Context); 678 if (!NT_SUCCESS(Status)) 679 { 680 NtDeleteValueKey(Context->KeyHandle, &ValueName); 681 return Status; 682 } 683 684 /* Delete the 'Log' key value */ 685 Status = NtDeleteValueKey(Context->KeyHandle, &ValueName); 686 ASSERT(NT_SUCCESS(Status)); 687 688 /* Reset the transaction */ 689 Status = RtlAbortRXact(Context); 690 ASSERT(NT_SUCCESS(Status)); 691 692 return STATUS_SUCCESS; 693 } 694 695