Revert the sync.
[reactos.git] / ntoskrnl / io / iomgr / drvrlist.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/drvrlist.c
5 * PURPOSE: Driver List support for Grouping, Tagging, Sorting, etc.
6 * PROGRAMMERS: <UNKNOWN>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 typedef struct _SERVICE_GROUP
16 {
17 LIST_ENTRY GroupListEntry;
18 UNICODE_STRING GroupName;
19 BOOLEAN ServicesRunning;
20 ULONG TagCount;
21 PULONG TagArray;
22 } SERVICE_GROUP, *PSERVICE_GROUP;
23
24 typedef struct _SERVICE
25 {
26 LIST_ENTRY ServiceListEntry;
27 UNICODE_STRING ServiceName;
28 UNICODE_STRING RegistryPath;
29 UNICODE_STRING ServiceGroup;
30 UNICODE_STRING ImagePath;
31
32 ULONG Start;
33 ULONG Type;
34 ULONG ErrorControl;
35 ULONG Tag;
36
37 /* BOOLEAN ServiceRunning;*/ // needed ??
38 } SERVICE, *PSERVICE;
39
40 #define TAG_RTLREGISTRY 'vrqR'
41
42 /* GLOBALS ********************************************************************/
43
44 LIST_ENTRY GroupListHead = {NULL, NULL};
45 LIST_ENTRY ServiceListHead = {NULL, NULL};
46 extern BOOLEAN NoGuiBoot;
47
48 VOID
49 FASTCALL
50 INIT_FUNCTION
51 IopDisplayLoadingMessage(PUNICODE_STRING ServiceName);
52
53 /* PRIVATE FUNCTIONS **********************************************************/
54
55 static NTSTATUS NTAPI
56 IopGetGroupOrderList(PWSTR ValueName,
57 ULONG ValueType,
58 PVOID ValueData,
59 ULONG ValueLength,
60 PVOID Context,
61 PVOID EntryContext)
62 {
63 PSERVICE_GROUP Group;
64
65 DPRINT("IopGetGroupOrderList(%S, %x, 0x%p, %x, 0x%p, 0x%p)\n",
66 ValueName, ValueType, ValueData, ValueLength, Context, EntryContext);
67
68 if (ValueType == REG_BINARY &&
69 ValueData != NULL &&
70 ValueLength >= sizeof(ULONG) &&
71 ValueLength >= (*(PULONG)ValueData + 1) * sizeof(ULONG))
72 {
73 Group = (PSERVICE_GROUP)Context;
74 Group->TagCount = ((PULONG)ValueData)[0];
75 if (Group->TagCount > 0)
76 {
77 if (ValueLength >= (Group->TagCount + 1) * sizeof(ULONG))
78 {
79 Group->TagArray = ExAllocatePool(NonPagedPool, Group->TagCount * sizeof(ULONG));
80 if (Group->TagArray == NULL)
81 {
82 Group->TagCount = 0;
83 return STATUS_INSUFFICIENT_RESOURCES;
84 }
85 memcpy(Group->TagArray, (PULONG)ValueData + 1, Group->TagCount * sizeof(ULONG));
86 }
87 else
88 {
89 Group->TagCount = 0;
90 return STATUS_UNSUCCESSFUL;
91 }
92 }
93 }
94 return STATUS_SUCCESS;
95 }
96
97 static NTSTATUS NTAPI
98 IopCreateGroupListEntry(PWSTR ValueName,
99 ULONG ValueType,
100 PVOID ValueData,
101 ULONG ValueLength,
102 PVOID Context,
103 PVOID EntryContext)
104 {
105 PSERVICE_GROUP Group;
106 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
107 NTSTATUS Status;
108
109
110 if (ValueType == REG_SZ)
111 {
112 DPRINT("GroupName: '%S'\n", (PWCHAR)ValueData);
113
114 Group = ExAllocatePool(NonPagedPool,
115 sizeof(SERVICE_GROUP));
116 if (Group == NULL)
117 {
118 return(STATUS_INSUFFICIENT_RESOURCES);
119 }
120
121 RtlZeroMemory(Group, sizeof(SERVICE_GROUP));
122
123 if (!RtlCreateUnicodeString(&Group->GroupName, (PWSTR)ValueData))
124 {
125 ExFreePool(Group);
126 return(STATUS_INSUFFICIENT_RESOURCES);
127 }
128
129 RtlZeroMemory(&QueryTable, sizeof(QueryTable));
130 QueryTable[0].Name = (PWSTR)ValueData;
131 QueryTable[0].QueryRoutine = IopGetGroupOrderList;
132
133 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
134 L"GroupOrderList",
135 QueryTable,
136 (PVOID)Group,
137 NULL);
138 DPRINT("%x %d %S\n", Status, Group->TagCount, (PWSTR)ValueData);
139
140 InsertTailList(&GroupListHead,
141 &Group->GroupListEntry);
142 }
143
144 return(STATUS_SUCCESS);
145 }
146
147
148 static NTSTATUS NTAPI
149 IopCreateServiceListEntry(PUNICODE_STRING ServiceName)
150 {
151 RTL_QUERY_REGISTRY_TABLE QueryTable[7];
152 PSERVICE Service;
153 NTSTATUS Status;
154 ULONG DefaultTag = MAXULONG;
155
156 DPRINT("ServiceName: '%wZ'\n", ServiceName);
157
158 /* Allocate service entry */
159 Service = (PSERVICE)ExAllocatePool(NonPagedPool, sizeof(SERVICE));
160 if (Service == NULL)
161 {
162 DPRINT1("ExAllocatePool() failed\n");
163 return(STATUS_INSUFFICIENT_RESOURCES);
164 }
165 RtlZeroMemory(Service, sizeof(SERVICE));
166
167 /* Get service data */
168 RtlZeroMemory(&QueryTable,
169 sizeof(QueryTable));
170
171 QueryTable[0].Name = L"Start";
172 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
173 QueryTable[0].EntryContext = &Service->Start;
174
175 QueryTable[1].Name = L"Type";
176 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
177 QueryTable[1].EntryContext = &Service->Type;
178
179 QueryTable[2].Name = L"ErrorControl";
180 QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
181 QueryTable[2].EntryContext = &Service->ErrorControl;
182
183 QueryTable[3].Name = L"Group";
184 QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
185 QueryTable[3].EntryContext = &Service->ServiceGroup;
186
187 QueryTable[4].Name = L"ImagePath";
188 QueryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
189 QueryTable[4].EntryContext = &Service->ImagePath;
190
191 QueryTable[5].Name = L"Tag";
192 QueryTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
193 QueryTable[5].EntryContext = &Service->Tag;
194 QueryTable[5].DefaultData = &DefaultTag;
195 QueryTable[5].DefaultType = REG_DWORD;
196 QueryTable[5].DefaultLength = sizeof(DefaultTag);
197
198 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
199 ServiceName->Buffer,
200 QueryTable,
201 NULL,
202 NULL);
203 if (!NT_SUCCESS(Status) || Service->Start > 1)
204 {
205 /*
206 * If something goes wrong during RtlQueryRegistryValues
207 * it'll just drop everything on the floor and return,
208 * so you have to check if the buffers were filled.
209 * Luckily we zerofilled the Service.
210 */
211 if (Service->ServiceGroup.Buffer)
212 {
213 ExFreePoolWithTag(Service->ServiceGroup.Buffer, TAG_RTLREGISTRY);
214 }
215 if (Service->ImagePath.Buffer)
216 {
217 ExFreePoolWithTag(Service->ImagePath.Buffer, TAG_RTLREGISTRY);
218 }
219 ExFreePool(Service);
220 return(Status);
221 }
222
223 /* Copy service name */
224 Service->ServiceName.Length = ServiceName->Length;
225 Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
226 Service->ServiceName.Buffer = ExAllocatePool(NonPagedPool,
227 Service->ServiceName.MaximumLength);
228 RtlCopyMemory(Service->ServiceName.Buffer,
229 ServiceName->Buffer,
230 ServiceName->Length);
231 Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
232
233 /* Build registry path */
234 Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
235 Service->RegistryPath.Buffer = ExAllocatePool(NonPagedPool,
236 MAX_PATH * sizeof(WCHAR));
237 wcscpy(Service->RegistryPath.Buffer,
238 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
239 wcscat(Service->RegistryPath.Buffer,
240 Service->ServiceName.Buffer);
241 Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
242
243 DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
244 DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
245 DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
246 DPRINT("ImagePath: '%wZ'\n", &Service->ImagePath);
247 DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
248 Service->Start, Service->Type, Service->Tag, Service->ErrorControl);
249
250 /* Append service entry */
251 InsertTailList(&ServiceListHead,
252 &Service->ServiceListEntry);
253
254 return(STATUS_SUCCESS);
255 }
256
257
258 NTSTATUS INIT_FUNCTION
259 IoCreateDriverList(VOID)
260 {
261 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
262 PKEY_BASIC_INFORMATION KeyInfo = NULL;
263 OBJECT_ATTRIBUTES ObjectAttributes;
264 UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
265 UNICODE_STRING SubKeyName;
266 HANDLE KeyHandle;
267 NTSTATUS Status;
268 ULONG Index;
269
270 ULONG KeyInfoLength = 0;
271 ULONG ReturnedLength;
272
273 DPRINT("IoCreateDriverList() called\n");
274
275 /* Initialize basic variables */
276 InitializeListHead(&GroupListHead);
277 InitializeListHead(&ServiceListHead);
278
279 /* Build group order list */
280 RtlZeroMemory(&QueryTable,
281 sizeof(QueryTable));
282
283 QueryTable[0].Name = L"List";
284 QueryTable[0].QueryRoutine = IopCreateGroupListEntry;
285
286 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
287 L"ServiceGroupOrder",
288 QueryTable,
289 NULL,
290 NULL);
291 if (!NT_SUCCESS(Status))
292 return(Status);
293
294 /* Enumerate services and create the service list */
295 InitializeObjectAttributes(&ObjectAttributes,
296 &ServicesKeyName,
297 OBJ_CASE_INSENSITIVE,
298 NULL,
299 NULL);
300
301 Status = ZwOpenKey(&KeyHandle,
302 KEY_ENUMERATE_SUB_KEYS,
303 &ObjectAttributes);
304 if (!NT_SUCCESS(Status))
305 {
306 return(Status);
307 }
308
309 KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
310 KeyInfo = ExAllocatePool(NonPagedPool, KeyInfoLength);
311 if (KeyInfo == NULL)
312 {
313 ZwClose(KeyHandle);
314 return(STATUS_INSUFFICIENT_RESOURCES);
315 }
316
317 Index = 0;
318 while (TRUE)
319 {
320 Status = ZwEnumerateKey(KeyHandle,
321 Index,
322 KeyBasicInformation,
323 KeyInfo,
324 KeyInfoLength,
325 &ReturnedLength);
326 if (NT_SUCCESS(Status))
327 {
328 if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
329 {
330
331 SubKeyName.Length = (USHORT)KeyInfo->NameLength;
332 SubKeyName.MaximumLength = (USHORT)KeyInfo->NameLength + sizeof(WCHAR);
333 SubKeyName.Buffer = KeyInfo->Name;
334 SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
335
336 DPRINT("KeyName: '%wZ'\n", &SubKeyName);
337 IopCreateServiceListEntry(&SubKeyName);
338 }
339 }
340
341 if (!NT_SUCCESS(Status))
342 break;
343
344 Index++;
345 }
346
347 ExFreePool(KeyInfo);
348 ZwClose(KeyHandle);
349
350 DPRINT("IoCreateDriverList() done\n");
351
352 return(STATUS_SUCCESS);
353 }
354
355 NTSTATUS INIT_FUNCTION
356 IoDestroyDriverList(VOID)
357 {
358 PSERVICE_GROUP CurrentGroup;
359 PSERVICE CurrentService;
360 PLIST_ENTRY NextEntry, TempEntry;
361
362 DPRINT("IoDestroyDriverList() called\n");
363
364 /* Destroy the Group List */
365 for (NextEntry = GroupListHead.Flink, TempEntry = NextEntry->Flink;
366 NextEntry != &GroupListHead;
367 NextEntry = TempEntry, TempEntry = NextEntry->Flink)
368 {
369 /* Get the entry */
370 CurrentGroup = CONTAINING_RECORD(NextEntry,
371 SERVICE_GROUP,
372 GroupListEntry);
373
374 /* Remove it from the list */
375 RemoveEntryList(&CurrentGroup->GroupListEntry);
376
377 /* Free buffers */
378 ExFreePool(CurrentGroup->GroupName.Buffer);
379 if (CurrentGroup->TagArray)
380 ExFreePool(CurrentGroup->TagArray);
381 ExFreePool(CurrentGroup);
382 }
383
384 /* Destroy the Service List */
385 for (NextEntry = ServiceListHead.Flink, TempEntry = NextEntry->Flink;
386 NextEntry != &ServiceListHead;
387 NextEntry = TempEntry, TempEntry = NextEntry->Flink)
388 {
389 /* Get the entry */
390 CurrentService = CONTAINING_RECORD(NextEntry,
391 SERVICE,
392 ServiceListEntry);
393
394 /* Remove it from the list */
395 RemoveEntryList(&CurrentService->ServiceListEntry);
396
397 /* Free buffers */
398 ExFreePool(CurrentService->ServiceName.Buffer);
399 ExFreePool(CurrentService->RegistryPath.Buffer);
400 if (CurrentService->ServiceGroup.Buffer)
401 ExFreePool(CurrentService->ServiceGroup.Buffer);
402 if (CurrentService->ImagePath.Buffer)
403 ExFreePool(CurrentService->ImagePath.Buffer);
404 ExFreePool(CurrentService);
405 }
406
407 DPRINT("IoDestroyDriverList() done\n");
408
409 /* Return success */
410 return STATUS_SUCCESS;
411 }
412
413 static INIT_FUNCTION NTSTATUS
414 IopLoadDriver(PSERVICE Service)
415 {
416 NTSTATUS Status = STATUS_UNSUCCESSFUL;
417 PUNICODE_STRING ImagePath = &Service->ImagePath;
418 PWCHAR ImageName;
419 UNICODE_STRING ImageNameU;
420
421 ImageName = wcsrchr(ImagePath->Buffer, L'\\');
422 if (!ImageName)
423 ImageName = ImagePath->Buffer;
424 else
425 ImageName++;
426
427 RtlInitUnicodeString(&ImageNameU, ImageName);
428
429 IopDisplayLoadingMessage(&ImageNameU);
430
431 Status = ZwLoadDriver(&Service->RegistryPath);
432 IopBootLog(&Service->ImagePath, NT_SUCCESS(Status) ? TRUE : FALSE);
433 if (!NT_SUCCESS(Status))
434 {
435 DPRINT("IopLoadDriver() failed (Status %lx)\n", Status);
436 #if 0
437 if (Service->ErrorControl == 1)
438 {
439 /* Log error */
440 }
441 else if (Service->ErrorControl == 2)
442 {
443 if (IsLastKnownGood == FALSE)
444 {
445 /* Boot last known good configuration */
446 }
447 }
448 else if (Service->ErrorControl == 3)
449 {
450 if (IsLastKnownGood == FALSE)
451 {
452 /* Boot last known good configuration */
453 }
454 else
455 {
456 /* BSOD! */
457 }
458 }
459 #endif
460 }
461 return Status;
462 }
463
464 /*
465 * IopInitializeSystemDrivers
466 *
467 * Load drivers marked as system start.
468 *
469 * Parameters
470 * None
471 *
472 * Return Value
473 * None
474 */
475 VOID
476 FASTCALL
477 IopInitializeSystemDrivers(VOID)
478 {
479 PSERVICE_GROUP CurrentGroup;
480 PSERVICE CurrentService;
481 NTSTATUS Status;
482 ULONG i;
483 PLIST_ENTRY NextGroupEntry, NextServiceEntry;
484
485 DPRINT("IopInitializeSystemDrivers()\n");
486
487 /* Start looping */
488 for (NextGroupEntry = GroupListHead.Flink;
489 NextGroupEntry != &GroupListHead;
490 NextGroupEntry = NextGroupEntry->Flink)
491 {
492 /* Get the entry */
493 CurrentGroup = CONTAINING_RECORD(NextGroupEntry,
494 SERVICE_GROUP,
495 GroupListEntry);
496
497 DPRINT("Group: %wZ\n", &CurrentGroup->GroupName);
498
499 /* Load all drivers with a valid tag */
500 for (i = 0; i < CurrentGroup->TagCount; i++)
501 {
502 /* Start looping */
503 for (NextServiceEntry = ServiceListHead.Flink;
504 NextServiceEntry != &ServiceListHead;
505 NextServiceEntry = NextServiceEntry->Flink)
506 {
507 /* Get the entry */
508 CurrentService = CONTAINING_RECORD(NextServiceEntry,
509 SERVICE,
510 ServiceListEntry);
511
512 if ((!RtlCompareUnicodeString(&CurrentGroup->GroupName,
513 &CurrentService->ServiceGroup,
514 TRUE)) &&
515 (CurrentService->Start == SERVICE_SYSTEM_START) &&
516 (CurrentService->Tag == CurrentGroup->TagArray[i]))
517
518 {
519 DPRINT(" Path: %wZ\n", &CurrentService->RegistryPath);
520 Status = IopLoadDriver(CurrentService);
521 InbvIndicateProgress();
522 }
523 }
524 }
525
526 /* Load all drivers without a tag or with an invalid tag */
527 for (NextServiceEntry = ServiceListHead.Flink;
528 NextServiceEntry != &ServiceListHead;
529 NextServiceEntry = NextServiceEntry->Flink)
530 {
531 /* Get the entry */
532 CurrentService = CONTAINING_RECORD(NextServiceEntry,
533 SERVICE,
534 ServiceListEntry);
535
536 if ((!RtlCompareUnicodeString(&CurrentGroup->GroupName,
537 &CurrentService->ServiceGroup,
538 TRUE)) &&
539 (CurrentService->Start == SERVICE_SYSTEM_START))
540 {
541 for (i = 0; i < CurrentGroup->TagCount; i++)
542 {
543 if (CurrentGroup->TagArray[i] == CurrentService->Tag)
544 {
545 break;
546 }
547 }
548
549 if (i >= CurrentGroup->TagCount)
550 {
551 DPRINT(" Path: %wZ\n", &CurrentService->RegistryPath);
552 Status = IopLoadDriver(CurrentService);
553 InbvIndicateProgress();
554 }
555
556 }
557 }
558 }
559
560 DPRINT("IopInitializeSystemDrivers() done\n");
561 }