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
RXactInitializeContext(PRXACT_CONTEXT Context,HANDLE RootDirectory,HANDLE KeyHandle)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
RXactpOpenTargetKey(HANDLE RootDirectory,ULONG ActionType,PUNICODE_STRING KeyName,PHANDLE KeyHandle)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
RXactpCommit(PRXACT_CONTEXT Context)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
RtlStartRXact(PRXACT_CONTEXT Context)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
RtlAbortRXact(PRXACT_CONTEXT Context)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
RtlInitializeRXact(HANDLE RootDirectory,BOOLEAN Commit,PRXACT_CONTEXT * OutContext)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
RtlAddAttributeActionToRXact(PRXACT_CONTEXT Context,ULONG ActionType,PUNICODE_STRING KeyName,HANDLE KeyHandle,PUNICODE_STRING ValueName,ULONG ValueType,PVOID ValueData,ULONG ValueDataSize)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
RtlAddActionToRXact(PRXACT_CONTEXT Context,ULONG ActionType,PUNICODE_STRING KeyName,ULONG ValueType,PVOID ValueData,ULONG ValueDataSize)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
RtlApplyRXactNoFlush(PRXACT_CONTEXT Context)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
RtlApplyRXact(PRXACT_CONTEXT Context)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