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