xref: /reactos/sdk/lib/rtl/rxact.c (revision 3b78272d)
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