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