xref: /reactos/ntoskrnl/config/cmboot.c (revision c2c66aff)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/config/cmboot.c
5  * PURPOSE:         Configuration Manager - Boot Initialization
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Alex Ionescu (alex.ionescu@reactos.org)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include "ntoskrnl.h"
13 #define NDEBUG
14 #include "debug.h"
15 
16 /* GLOBALS ********************************************************************/
17 
18 extern ULONG InitSafeBootMode;
19 
20 /* FUNCTIONS ******************************************************************/
21 
22 HCELL_INDEX
23 NTAPI
24 INIT_FUNCTION
25 CmpFindControlSet(IN PHHIVE SystemHive,
26                   IN HCELL_INDEX RootCell,
27                   IN PUNICODE_STRING SelectKeyName,
28                   OUT PBOOLEAN AutoSelect)
29 {
30     UNICODE_STRING KeyName;
31     PCM_KEY_NODE Node;
32     HCELL_INDEX SelectCell, AutoSelectCell, SelectValueCell, ControlSetCell;
33     HCELL_INDEX CurrentValueCell;
34     PCM_KEY_VALUE KeyValue;
35     ULONG Length;
36     PULONG ControlSetId;
37     ANSI_STRING ControlSetAnsiName;
38     CHAR Buffer[128];
39     WCHAR WideBuffer[128];
40     NTSTATUS Status;
41     PULONG CurrentData;
42 
43     /* Sanity check */
44     ASSERT(SystemHive->ReleaseCellRoutine == NULL);
45 
46     /* Get the Select subkey */
47     RtlInitUnicodeString(&KeyName, L"select");
48     Node = (PCM_KEY_NODE)HvGetCell(SystemHive, RootCell);
49     if (!Node) return HCELL_NIL;
50     SelectCell = CmpFindSubKeyByName(SystemHive, Node, &KeyName);
51     if (SelectCell == HCELL_NIL) return SelectCell;
52 
53     /* Get AutoSelect value */
54     RtlInitUnicodeString(&KeyName, L"AutoSelect");
55     Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell);
56     if (!Node) return HCELL_NIL;
57     AutoSelectCell = CmpFindValueByName(SystemHive, Node, &KeyName);
58     if (AutoSelectCell == HCELL_NIL)
59     {
60         /* Assume TRUE if the value is missing. */
61         *AutoSelect = TRUE;
62     }
63     else
64     {
65         /* Read the value */
66         KeyValue = (PCM_KEY_VALUE)HvGetCell(SystemHive, AutoSelectCell);
67         if (KeyValue == NULL) return HCELL_NIL;
68 
69         /* Convert it to a boolean */
70         *AutoSelect = *(PBOOLEAN)CmpValueToData(SystemHive, KeyValue, &Length);
71     }
72 
73     /* Now find the control set being looked up */
74     Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell);
75     if (!Node) return HCELL_NIL;
76     SelectValueCell = CmpFindValueByName(SystemHive, Node, SelectKeyName);
77     if (SelectValueCell == HCELL_NIL) return SelectValueCell;
78 
79     /* Read the value (corresponding to the CCS ID) */
80     KeyValue = (PCM_KEY_VALUE)HvGetCell(SystemHive, SelectValueCell);
81     if (!KeyValue) return HCELL_NIL;
82     if (KeyValue->Type != REG_DWORD) return HCELL_NIL;
83     ControlSetId = (PULONG)CmpValueToData(SystemHive, KeyValue, &Length);
84 
85     /* Now build an Ansi String for the CCS's Name */
86     sprintf(Buffer, "ControlSet%03lu", *ControlSetId);
87     ControlSetAnsiName.Length = (USHORT)strlen(Buffer);
88     ControlSetAnsiName.MaximumLength = (USHORT)strlen(Buffer);
89     ControlSetAnsiName.Buffer = Buffer;
90 
91     /* And convert it to Unicode... */
92     KeyName.MaximumLength = 256;
93     KeyName.Buffer = WideBuffer;
94     Status = RtlAnsiStringToUnicodeString(&KeyName,
95                                           &ControlSetAnsiName,
96                                           FALSE);
97     if (!NT_SUCCESS(Status)) return HCELL_NIL;
98 
99     /* Now open it */
100     Node = (PCM_KEY_NODE)HvGetCell(SystemHive, RootCell);
101     if (!Node) return HCELL_NIL;
102     ControlSetCell = CmpFindSubKeyByName(SystemHive, Node, &KeyName);
103     if (ControlSetCell == HCELL_NIL) return ControlSetCell;
104 
105     /* Get the value of the "Current" CCS */
106     RtlInitUnicodeString(&KeyName, L"Current");
107     Node =  (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell);
108     if (!Node) return HCELL_NIL;
109     CurrentValueCell = CmpFindValueByName(SystemHive, Node, &KeyName);
110 
111     /* Make sure it exists */
112     if (CurrentValueCell != HCELL_NIL)
113     {
114         /* Get the current value and make sure its a ULONG */
115         KeyValue = (PCM_KEY_VALUE)HvGetCell(SystemHive, CurrentValueCell);
116         if (!KeyValue) return HCELL_NIL;
117         if (KeyValue->Type == REG_DWORD)
118         {
119             /* Get the data and update it */
120             CurrentData = (PULONG)CmpValueToData(SystemHive,
121                                                  KeyValue,
122                                                  &Length);
123             if (!CurrentData) return HCELL_NIL;
124             *CurrentData = *ControlSetId;
125         }
126     }
127 
128     /* Return the CCS Cell */
129     return ControlSetCell;
130 }
131 
132 ULONG
133 NTAPI
134 INIT_FUNCTION
135 CmpFindTagIndex(IN PHHIVE Hive,
136                 IN HCELL_INDEX TagCell,
137                 IN HCELL_INDEX GroupOrderCell,
138                 IN PUNICODE_STRING GroupName)
139 {
140     PCM_KEY_VALUE TagValue, Value;
141     HCELL_INDEX OrderCell;
142     PULONG TagOrder, DriverTag;
143     ULONG CurrentTag, Length;
144     PCM_KEY_NODE Node;
145     BOOLEAN BufferAllocated;
146     ASSERT(Hive->ReleaseCellRoutine == NULL);
147 
148     /* Get the tag */
149     Value = HvGetCell(Hive, TagCell);
150     ASSERT(Value);
151     DriverTag = (PULONG)CmpValueToData(Hive, Value, &Length);
152     ASSERT(DriverTag);
153 
154     /* Get the order array */
155     Node = HvGetCell(Hive, GroupOrderCell);
156     ASSERT(Node);
157     OrderCell = CmpFindValueByName(Hive, Node, GroupName);
158     if (OrderCell == HCELL_NIL) return -2;
159 
160     /* And read it */
161     TagValue = HvGetCell(Hive, OrderCell);
162     CmpGetValueData(Hive, TagValue, &Length, (PVOID*)&TagOrder, &BufferAllocated, &OrderCell);
163     ASSERT(TagOrder);
164 
165     /* Parse each tag */
166     for (CurrentTag = 1; CurrentTag <= TagOrder[0]; CurrentTag++)
167     {
168         /* Find a match */
169         if (TagOrder[CurrentTag] == *DriverTag)
170         {
171             /* Found it -- return the tag */
172             if (BufferAllocated) ExFreePool(TagOrder);
173             return CurrentTag;
174         }
175     }
176 
177     /* No matches, so assume next to last ordering */
178     if (BufferAllocated) ExFreePool(TagOrder);
179     return -2;
180 }
181 
182 BOOLEAN
183 NTAPI
184 INIT_FUNCTION
185 CmpAddDriverToList(IN PHHIVE Hive,
186                    IN HCELL_INDEX DriverCell,
187                    IN HCELL_INDEX GroupOrderCell,
188                    IN PUNICODE_STRING RegistryPath,
189                    IN PLIST_ENTRY BootDriverListHead)
190 {
191     PBOOT_DRIVER_NODE DriverNode;
192     PBOOT_DRIVER_LIST_ENTRY DriverEntry;
193     PCM_KEY_NODE Node;
194     ULONG Length;
195     USHORT NameLength;
196     HCELL_INDEX ValueCell, TagCell;    PCM_KEY_VALUE Value;
197     PUNICODE_STRING FileName, RegistryString;
198     UNICODE_STRING UnicodeString;
199     PULONG ErrorControl;
200     PWCHAR Buffer;
201     ASSERT(Hive->ReleaseCellRoutine == NULL);
202 
203     /* Allocate a driver node and initialize it */
204     DriverNode = CmpAllocate(sizeof(BOOT_DRIVER_NODE), FALSE, TAG_CM);
205     if (!DriverNode) return FALSE;
206     DriverEntry = &DriverNode->ListEntry;
207     DriverEntry->RegistryPath.Buffer = NULL;
208     DriverEntry->FilePath.Buffer = NULL;
209 
210     /* Get the driver cell */
211     Node = HvGetCell(Hive, DriverCell);
212     ASSERT(Node);
213 
214     /* Get the name from the cell */
215     DriverNode->Name.Length = Node->Flags & KEY_COMP_NAME ?
216                               CmpCompressedNameSize(Node->Name, Node->NameLength) :
217                               Node->NameLength;
218     DriverNode->Name.MaximumLength = DriverNode->Name.Length;
219     NameLength = DriverNode->Name.Length;
220 
221     /* Now allocate the buffer for it and copy the name */
222     DriverNode->Name.Buffer = CmpAllocate(NameLength, FALSE, TAG_CM);
223     if (!DriverNode->Name.Buffer) return FALSE;
224     if (Node->Flags & KEY_COMP_NAME)
225     {
226         /* Compressed name */
227         CmpCopyCompressedName(DriverNode->Name.Buffer,
228                               DriverNode->Name.Length,
229                               Node->Name,
230                               Node->NameLength);
231     }
232     else
233     {
234         /* Normal name */
235         RtlCopyMemory(DriverNode->Name.Buffer, Node->Name, Node->NameLength);
236     }
237 
238     /* Now find the image path */
239     RtlInitUnicodeString(&UnicodeString, L"ImagePath");
240     ValueCell = CmpFindValueByName(Hive, Node, &UnicodeString);
241     if (ValueCell == HCELL_NIL)
242     {
243         /* Couldn't find it, so assume the drivers path */
244         Length = sizeof(L"System32\\Drivers\\") + NameLength + sizeof(L".sys");
245 
246         /* Allocate the path name */
247         FileName = &DriverEntry->FilePath;
248         FileName->Length = 0;
249         FileName->MaximumLength = (USHORT)Length;
250         FileName->Buffer = CmpAllocate(Length, FALSE,TAG_CM);
251         if (!FileName->Buffer) return FALSE;
252 
253         /* Write the path name */
254         RtlAppendUnicodeToString(FileName, L"System32\\Drivers\\");
255         RtlAppendUnicodeStringToString(FileName, &DriverNode->Name);
256         RtlAppendUnicodeToString(FileName, L".sys");
257     }
258     else
259     {
260         /* Path name exists, so grab it */
261         Value = HvGetCell(Hive, ValueCell);
262         ASSERT(Value);
263 
264         /* Allocate and setup the path name */
265         FileName = &DriverEntry->FilePath;
266         Buffer = (PWCHAR)CmpValueToData(Hive, Value, &Length);
267         FileName->MaximumLength = FileName->Length = (USHORT)Length;
268         FileName->Buffer = CmpAllocate(Length, FALSE, TAG_CM);
269 
270         /* Transfer the data */
271         if (!(FileName->Buffer) || !(Buffer)) return FALSE;
272         RtlCopyMemory(FileName->Buffer, Buffer, Length);
273     }
274 
275     /* Now build the registry path */
276     RegistryString = &DriverEntry->RegistryPath;
277     RegistryString->Length = 0;
278     RegistryString->MaximumLength = RegistryPath->Length + NameLength;
279     RegistryString->Buffer = CmpAllocate(RegistryString->MaximumLength, FALSE, TAG_CM);
280     if (!RegistryString->Buffer) return FALSE;
281 
282     /* Add the driver name to it */
283     RtlAppendUnicodeStringToString(RegistryString, RegistryPath);
284     RtlAppendUnicodeStringToString(RegistryString, &DriverNode->Name);
285 
286     /* The entry is done, add it */
287     InsertHeadList(BootDriverListHead, &DriverEntry->Link);
288 
289     /* Now find error control settings */
290     RtlInitUnicodeString(&UnicodeString, L"ErrorControl");
291     ValueCell = CmpFindValueByName(Hive, Node, &UnicodeString);
292     if (ValueCell == HCELL_NIL)
293     {
294         /* Couldn't find it, so assume default */
295         DriverNode->ErrorControl = NormalError;
296     }
297     else
298     {
299         /* Otherwise, read whatever the data says */
300         Value = HvGetCell(Hive, ValueCell);
301         ASSERT(Value);
302         ErrorControl = (PULONG)CmpValueToData(Hive, Value, &Length);
303         ASSERT(ErrorControl);
304         DriverNode->ErrorControl = *ErrorControl;
305     }
306 
307     /* Next, get the group cell */
308     RtlInitUnicodeString(&UnicodeString, L"group");
309     ValueCell = CmpFindValueByName(Hive, Node, &UnicodeString);
310     if (ValueCell == HCELL_NIL)
311     {
312         /* Couldn't find, so set an empty string */
313         RtlInitEmptyUnicodeString(&DriverNode->Group, NULL, 0);
314     }
315     else
316     {
317         /* Found it, read the group value */
318         Value = HvGetCell(Hive, ValueCell);
319         ASSERT(Value);
320 
321         /* Copy it into the node */
322         DriverNode->Group.Buffer = (PWCHAR)CmpValueToData(Hive, Value, &Length);
323         if (!DriverNode->Group.Buffer) return FALSE;
324         DriverNode->Group.Length = (USHORT)Length - sizeof(UNICODE_NULL);
325         DriverNode->Group.MaximumLength = DriverNode->Group.Length;
326     }
327 
328     /* Finally, find the tag */
329     RtlInitUnicodeString(&UnicodeString, L"Tag");
330     TagCell = CmpFindValueByName(Hive, Node, &UnicodeString);
331     if (TagCell == HCELL_NIL)
332     {
333         /* No tag, so load last */
334         DriverNode->Tag = -1;
335     }
336     else
337     {
338         /* Otherwise, decode it based on tag order */
339         DriverNode->Tag = CmpFindTagIndex(Hive,
340                                           TagCell,
341                                           GroupOrderCell,
342                                           &DriverNode->Group);
343     }
344 
345     /* All done! */
346     return TRUE;
347 }
348 
349 BOOLEAN
350 NTAPI
351 INIT_FUNCTION
352 CmpIsLoadType(IN PHHIVE Hive,
353               IN HCELL_INDEX Cell,
354               IN SERVICE_LOAD_TYPE LoadType)
355 {
356     PCM_KEY_NODE Node;
357     HCELL_INDEX ValueCell;
358     UNICODE_STRING ValueString = RTL_CONSTANT_STRING(L"Start");
359     PCM_KEY_VALUE Value;
360     ULONG Length;
361     PLONG Data;
362     ASSERT(Hive->ReleaseCellRoutine == NULL);
363 
364     /* Open the start cell */
365     Node = HvGetCell(Hive, Cell);
366     ASSERT(Node);
367     ValueCell = CmpFindValueByName(Hive, Node, &ValueString);
368     if (ValueCell == HCELL_NIL) return FALSE;
369 
370     /* Read the start value */
371     Value = HvGetCell(Hive, ValueCell);
372     ASSERT(Value);
373     Data = (PLONG)CmpValueToData(Hive, Value, &Length);
374     ASSERT(Data);
375 
376     /* Return if the type matches */
377     return (*Data == LoadType);
378 }
379 
380 BOOLEAN
381 NTAPI
382 INIT_FUNCTION
383 CmpFindDrivers(IN PHHIVE Hive,
384                IN HCELL_INDEX ControlSet,
385                IN SERVICE_LOAD_TYPE LoadType,
386                IN PWCHAR BootFileSystem OPTIONAL,
387                IN PLIST_ENTRY DriverListHead)
388 {
389     HCELL_INDEX ServicesCell, ControlCell, GroupOrderCell, DriverCell;
390     HCELL_INDEX SafeBootCell = HCELL_NIL;
391     UNICODE_STRING Name;
392     ULONG i;
393     WCHAR Buffer[128];
394     UNICODE_STRING UnicodeString, KeyPath;
395     PBOOT_DRIVER_NODE FsNode;
396     PCM_KEY_NODE ControlNode, ServicesNode, Node;
397     ASSERT(Hive->ReleaseCellRoutine == NULL);
398 
399     /* Open the control set key */
400     ControlNode = HvGetCell(Hive, ControlSet);
401     ASSERT(ControlNode);
402 
403     /* Get services cell */
404     RtlInitUnicodeString(&Name, L"Services");
405     ServicesCell = CmpFindSubKeyByName(Hive, ControlNode, &Name);
406     if (ServicesCell == HCELL_NIL) return FALSE;
407 
408     /* Open services key */
409     ServicesNode = HvGetCell(Hive, ServicesCell);
410     ASSERT(ServicesNode);
411 
412     /* Get control cell */
413     RtlInitUnicodeString(&Name, L"Control");
414     ControlCell = CmpFindSubKeyByName(Hive, ControlNode, &Name);
415     if (ControlCell == HCELL_NIL) return FALSE;
416 
417     /* Get the group order cell and read it */
418     RtlInitUnicodeString(&Name, L"GroupOrderList");
419     Node = HvGetCell(Hive, ControlCell);
420     ASSERT(Node);
421     GroupOrderCell = CmpFindSubKeyByName(Hive, Node, &Name);
422     if (GroupOrderCell == HCELL_NIL) return FALSE;
423 
424     /* Get Safe Boot cell */
425     if(InitSafeBootMode)
426     {
427         /* Open the Safe Boot key */
428         RtlInitUnicodeString(&Name, L"SafeBoot");
429         Node = HvGetCell(Hive, ControlCell);
430         ASSERT(Node);
431         SafeBootCell = CmpFindSubKeyByName(Hive, Node, &Name);
432         if (SafeBootCell == HCELL_NIL) return FALSE;
433 
434         /* Open the correct start key (depending on the mode) */
435         Node = HvGetCell(Hive, SafeBootCell);
436         ASSERT(Node);
437         switch(InitSafeBootMode)
438         {
439             /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
440             case 1:
441             case 3: RtlInitUnicodeString(&Name, L"Minimal"); break;
442             case 2: RtlInitUnicodeString(&Name, L"Network"); break;
443             default: return FALSE;
444         }
445         SafeBootCell = CmpFindSubKeyByName(Hive, Node, &Name);
446         if(SafeBootCell == HCELL_NIL) return FALSE;
447     }
448 
449     /* Build the root registry path */
450     RtlInitEmptyUnicodeString(&KeyPath, Buffer, sizeof(Buffer));
451     RtlAppendUnicodeToString(&KeyPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
452 
453     /* Find the first subkey (ie: the first driver or service) */
454     i = 0;
455     DriverCell = CmpFindSubKeyByNumber(Hive, ServicesNode, i);
456     while (DriverCell != HCELL_NIL)
457     {
458         /* Make sure it's a driver of this start type AND is "safe" to load */
459         if (CmpIsLoadType(Hive, DriverCell, LoadType) &&
460             CmpIsSafe(Hive, SafeBootCell, DriverCell))
461         {
462             /* Add it to the list */
463             CmpAddDriverToList(Hive,
464                                DriverCell,
465                                GroupOrderCell,
466                                &KeyPath,
467                                DriverListHead);
468 
469         }
470 
471         /* Try the next subkey */
472         DriverCell = CmpFindSubKeyByNumber(Hive, ServicesNode, ++i);
473     }
474 
475     /* Check if we have a boot file system */
476     if (BootFileSystem)
477     {
478         /* Find it */
479         RtlInitUnicodeString(&UnicodeString, BootFileSystem);
480         DriverCell = CmpFindSubKeyByName(Hive, ServicesNode, &UnicodeString);
481         if (DriverCell != HCELL_NIL)
482         {
483             /* Always add it to the list */
484             CmpAddDriverToList(Hive,
485                                DriverCell,
486                                GroupOrderCell,
487                                &KeyPath,
488                                DriverListHead);
489 
490             /* Mark it as critical so it always loads */
491             FsNode = CONTAINING_RECORD(DriverListHead->Flink,
492                                        BOOT_DRIVER_NODE,
493                                        ListEntry.Link);
494             FsNode->ErrorControl = SERVICE_ERROR_CRITICAL;
495         }
496     }
497 
498     /* We're done! */
499     return TRUE;
500 }
501 
502 BOOLEAN
503 NTAPI
504 INIT_FUNCTION
505 CmpDoSort(IN PLIST_ENTRY DriverListHead,
506           IN PUNICODE_STRING OrderList)
507 {
508     PWCHAR Current, End = NULL;
509     PLIST_ENTRY NextEntry;
510     UNICODE_STRING GroupName;
511     PBOOT_DRIVER_NODE CurrentNode;
512 
513     /* We're going from end to start, so get to the last group and keep going */
514     Current = &OrderList->Buffer[OrderList->Length / sizeof(WCHAR)];
515     while (Current > OrderList->Buffer)
516     {
517         /* Scan the current string */
518         do
519         {
520             if (*Current == UNICODE_NULL) End = Current;
521         } while ((*(--Current - 1) != UNICODE_NULL) && (Current != OrderList->Buffer));
522 
523         /* This is our cleaned up string for this specific group */
524         ASSERT(End != NULL);
525         GroupName.Length = (USHORT)(End - Current) * sizeof(WCHAR);
526         GroupName.MaximumLength = GroupName.Length;
527         GroupName.Buffer = Current;
528 
529         /* Now loop the driver list */
530         NextEntry = DriverListHead->Flink;
531         while (NextEntry != DriverListHead)
532         {
533             /* Get this node */
534             CurrentNode = CONTAINING_RECORD(NextEntry,
535                                             BOOT_DRIVER_NODE,
536                                             ListEntry.Link);
537 
538             /* Get the next entry now since we'll do a relink */
539             NextEntry = CurrentNode->ListEntry.Link.Flink;
540 
541             /* Is there a group name and does it match the current group? */
542             if ((CurrentNode->Group.Buffer) &&
543                 (RtlEqualUnicodeString(&GroupName, &CurrentNode->Group, TRUE)))
544             {
545                 /* Remove from this location and re-link in the new one */
546                 RemoveEntryList(&CurrentNode->ListEntry.Link);
547                 InsertHeadList(DriverListHead, &CurrentNode->ListEntry.Link);
548             }
549         }
550 
551         /* Move on */
552         Current--;
553     }
554 
555     /* All done */
556     return TRUE;
557 }
558 
559 BOOLEAN
560 NTAPI
561 INIT_FUNCTION
562 CmpSortDriverList(IN PHHIVE Hive,
563                   IN HCELL_INDEX ControlSet,
564                   IN PLIST_ENTRY DriverListHead)
565 {
566     HCELL_INDEX Controls, GroupOrder, ListCell;
567     UNICODE_STRING Name, DependList;
568     PCM_KEY_VALUE ListNode;
569     ULONG Length;
570     PCM_KEY_NODE Node;
571     ASSERT(Hive->ReleaseCellRoutine == NULL);
572 
573     /* Open the control key */
574     Node = HvGetCell(Hive, ControlSet);
575     ASSERT(Node);
576     RtlInitUnicodeString(&Name, L"Control");
577     Controls = CmpFindSubKeyByName(Hive, Node, &Name);
578     if (Controls == HCELL_NIL) return FALSE;
579 
580     /* Open the service group order */
581     Node = HvGetCell(Hive, Controls);
582     ASSERT(Node);
583     RtlInitUnicodeString(&Name, L"ServiceGroupOrder");
584     GroupOrder = CmpFindSubKeyByName(Hive, Node, &Name);
585     if (GroupOrder == HCELL_NIL) return FALSE;
586 
587     /* Open the list key */
588     Node = HvGetCell(Hive, GroupOrder);
589     ASSERT(Node);
590     RtlInitUnicodeString(&Name, L"list");
591     ListCell = CmpFindValueByName(Hive, Node, &Name);
592     if (ListCell == HCELL_NIL) return FALSE;
593 
594     /* Now read the actual list */
595     ListNode = HvGetCell(Hive, ListCell);
596     ASSERT(ListNode);
597     if (ListNode->Type != REG_MULTI_SZ) return FALSE;
598 
599     /* Copy it into a buffer */
600     DependList.Buffer = (PWCHAR)CmpValueToData(Hive, ListNode, &Length);
601     if (!DependList.Buffer) return FALSE;
602     DependList.Length = DependList.MaximumLength = (USHORT)Length - sizeof(UNICODE_NULL);
603 
604     /* And start the recurive sort algorithm */
605     return CmpDoSort(DriverListHead, &DependList);
606 }
607 
608 BOOLEAN
609 NTAPI
610 INIT_FUNCTION
611 CmpOrderGroup(IN PBOOT_DRIVER_NODE StartNode,
612               IN PBOOT_DRIVER_NODE EndNode)
613 {
614     PBOOT_DRIVER_NODE CurrentNode, PreviousNode;
615     PLIST_ENTRY ListEntry;
616 
617     /* Base case, nothing to do */
618     if (StartNode == EndNode) return TRUE;
619 
620     /* Loop the nodes */
621     CurrentNode = StartNode;
622     do
623     {
624         /* Save this as the previous node */
625         PreviousNode = CurrentNode;
626 
627         /* And move to the next one */
628         ListEntry = CurrentNode->ListEntry.Link.Flink;
629         CurrentNode = CONTAINING_RECORD(ListEntry,
630                                         BOOT_DRIVER_NODE,
631                                         ListEntry.Link);
632 
633         /* Check if the previous driver had a bigger tag */
634         if (PreviousNode->Tag > CurrentNode->Tag)
635         {
636             /* Check if we need to update the tail */
637             if (CurrentNode == EndNode)
638             {
639                 /* Update the tail */
640                 ListEntry = CurrentNode->ListEntry.Link.Blink;
641                 EndNode = CONTAINING_RECORD(ListEntry,
642                                             BOOT_DRIVER_NODE,
643                                             ListEntry.Link);
644             }
645 
646             /* Remove this driver since we need to move it */
647             RemoveEntryList(&CurrentNode->ListEntry.Link);
648 
649             /* Keep looping until we find a driver with a lower tag than ours */
650             while ((PreviousNode->Tag > CurrentNode->Tag) && (PreviousNode != StartNode))
651             {
652                 /* We'll be re-inserted at this spot */
653                 ListEntry = PreviousNode->ListEntry.Link.Blink;
654                 PreviousNode = CONTAINING_RECORD(ListEntry,
655                                                  BOOT_DRIVER_NODE,
656                                                  ListEntry.Link);
657             }
658 
659             /* Do the insert in the new location */
660             InsertTailList(&PreviousNode->ListEntry.Link, &CurrentNode->ListEntry.Link);
661 
662             /* Update the head, if needed */
663             if (PreviousNode == StartNode) StartNode = CurrentNode;
664         }
665     } while (CurrentNode != EndNode);
666 
667     /* All done */
668     return TRUE;
669 }
670 
671 BOOLEAN
672 NTAPI
673 INIT_FUNCTION
674 CmpResolveDriverDependencies(IN PLIST_ENTRY DriverListHead)
675 {
676     PLIST_ENTRY NextEntry;
677     PBOOT_DRIVER_NODE StartNode, EndNode, CurrentNode;
678 
679     /* Loop the list */
680     NextEntry = DriverListHead->Flink;
681     while (NextEntry != DriverListHead)
682     {
683         /* Find the first entry */
684         StartNode = CONTAINING_RECORD(NextEntry,
685                                       BOOT_DRIVER_NODE,
686                                       ListEntry.Link);
687         do
688         {
689             /* Find the last entry */
690             EndNode = CONTAINING_RECORD(NextEntry,
691                                         BOOT_DRIVER_NODE,
692                                         ListEntry.Link);
693 
694             /* Get the next entry */
695             NextEntry = NextEntry->Flink;
696             CurrentNode = CONTAINING_RECORD(NextEntry,
697                                             BOOT_DRIVER_NODE,
698                                             ListEntry.Link);
699 
700             /* If the next entry is back to the top, break out */
701             if (NextEntry == DriverListHead) break;
702 
703             /* Otherwise, check if this entry is equal */
704             if (!RtlEqualUnicodeString(&StartNode->Group,
705                                        &CurrentNode->Group,
706                                        TRUE))
707             {
708                 /* It is, so we've detected a cycle, break out */
709                 break;
710             }
711         } while (NextEntry != DriverListHead);
712 
713         /* Now we have the correct start and end pointers, so do the sort */
714         CmpOrderGroup(StartNode, EndNode);
715     }
716 
717     /* We're done */
718     return TRUE;
719 }
720 
721 BOOLEAN
722 NTAPI
723 INIT_FUNCTION
724 CmpIsSafe(IN PHHIVE Hive,
725           IN HCELL_INDEX SafeBootCell,
726           IN HCELL_INDEX DriverCell)
727 {
728     PCM_KEY_NODE SafeBootNode;
729     PCM_KEY_NODE DriverNode;
730     PCM_KEY_VALUE KeyValue;
731     HCELL_INDEX CellIndex;
732     ULONG Length = 0;
733     UNICODE_STRING Name;
734     PWCHAR OriginalName;
735     ASSERT(Hive->ReleaseCellRoutine == NULL);
736 
737     /* Driver key node (mandatory) */
738     ASSERT(DriverCell != HCELL_NIL);
739     DriverNode = HvGetCell(Hive, DriverCell);
740     ASSERT(DriverNode);
741 
742     /* Safe boot key node (optional but return TRUE if not present) */
743     if(SafeBootCell == HCELL_NIL) return TRUE;
744     SafeBootNode = HvGetCell(Hive, SafeBootCell);
745     if(!SafeBootNode) return FALSE;
746 
747     /* Search by the name from the group */
748     RtlInitUnicodeString(&Name, L"Group");
749     CellIndex = CmpFindValueByName(Hive, DriverNode, &Name);
750     if(CellIndex != HCELL_NIL)
751     {
752         KeyValue = HvGetCell(Hive, CellIndex);
753         ASSERT(KeyValue);
754         if (KeyValue->Type == REG_SZ || KeyValue->Type == REG_EXPAND_SZ)
755         {
756             /* Compose the search 'key' */
757             Name.Buffer = (PWCHAR)CmpValueToData(Hive, KeyValue, &Length);
758             if (!Name.Buffer) return FALSE;
759             Name.Length = (USHORT)Length - sizeof(UNICODE_NULL);
760             Name.MaximumLength = Name.Length;
761             /* Search for corresponding key in the Safe Boot key */
762             CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name);
763             if(CellIndex != HCELL_NIL) return TRUE;
764         }
765     }
766 
767     /* Group has not been found - find driver name */
768     Name.Length = DriverNode->Flags & KEY_COMP_NAME ?
769                               CmpCompressedNameSize(DriverNode->Name,
770                                                     DriverNode->NameLength) :
771                               DriverNode->NameLength;
772     Name.MaximumLength = Name.Length;
773     /* Now allocate the buffer for it and copy the name */
774     Name.Buffer = CmpAllocate(Name.Length, FALSE, TAG_CM);
775     if (!Name.Buffer) return FALSE;
776     if (DriverNode->Flags & KEY_COMP_NAME)
777     {
778         /* Compressed name */
779         CmpCopyCompressedName(Name.Buffer,
780                               Name.Length,
781                               DriverNode->Name,
782                               DriverNode->NameLength);
783     }
784     else
785     {
786         /* Normal name */
787         RtlCopyMemory(Name.Buffer, DriverNode->Name, DriverNode->NameLength);
788     }
789     CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name);
790     RtlFreeUnicodeString(&Name);
791     if(CellIndex != HCELL_NIL) return TRUE;
792 
793     /* Not group or driver name - search by image name */
794     RtlInitUnicodeString(&Name, L"ImagePath");
795     CellIndex = CmpFindValueByName(Hive, DriverNode, &Name);
796     if(CellIndex != HCELL_NIL)
797     {
798         KeyValue = HvGetCell(Hive, CellIndex);
799         ASSERT(KeyValue);
800         if (KeyValue->Type == REG_SZ || KeyValue->Type == REG_EXPAND_SZ)
801         {
802             /* Compose the search 'key' */
803             OriginalName = (PWCHAR)CmpValueToData(Hive, KeyValue, &Length);
804             if (!OriginalName) return FALSE;
805             /* Get the base image file name */
806             Name.Buffer = wcsrchr(OriginalName, L'\\');
807             if (!Name.Buffer) return FALSE;
808             ++Name.Buffer;
809             /* Length of the base name must be >=1 */
810             Name.Length = (USHORT)Length - (USHORT)((PUCHAR)Name.Buffer - (PUCHAR)OriginalName)
811                                  - sizeof(UNICODE_NULL);
812             if(Name.Length < 1) return FALSE;
813             Name.MaximumLength = Name.Length;
814             /* Search for corresponding key in the Safe Boot key */
815             CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name);
816             if(CellIndex != HCELL_NIL) return TRUE;
817         }
818     }
819     /* Nothing found - nothing else to search */
820     return FALSE;
821 }
822 
823 /* EOF */
824