* Sync with recent trunk (r52669).
[reactos.git] / ntoskrnl / io / iomgr / driver.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/driver.c
5 * PURPOSE: Driver Object Management
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (navaraf@reactos.org)
8 * Hervé Poussineau (hpoussin@reactos.org)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 LIST_ENTRY DriverReinitListHead;
20 KSPIN_LOCK DriverReinitListLock;
21 PLIST_ENTRY DriverReinitTailEntry;
22
23 PLIST_ENTRY DriverBootReinitTailEntry;
24 LIST_ENTRY DriverBootReinitListHead;
25 KSPIN_LOCK DriverBootReinitListLock;
26
27 UNICODE_STRING IopHardwareDatabaseKey =
28 RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\SYSTEM");
29
30 POBJECT_TYPE IoDriverObjectType = NULL;
31
32 #define TAG_RTLREGISTRY 'vrqR'
33
34 extern BOOLEAN ExpInTextModeSetup;
35 extern BOOLEAN PnpSystemInit;
36
37 USHORT IopGroupIndex;
38 PLIST_ENTRY IopGroupTable;
39
40 /* PRIVATE FUNCTIONS **********************************************************/
41
42 NTSTATUS NTAPI
43 IopInvalidDeviceRequest(
44 PDEVICE_OBJECT DeviceObject,
45 PIRP Irp)
46 {
47 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
48 Irp->IoStatus.Information = 0;
49 IoCompleteRequest(Irp, IO_NO_INCREMENT);
50 return STATUS_INVALID_DEVICE_REQUEST;
51 }
52
53 VOID
54 NTAPI
55 IopDeleteDriver(IN PVOID ObjectBody)
56 {
57 PDRIVER_OBJECT DriverObject = ObjectBody;
58 PIO_CLIENT_EXTENSION DriverExtension, NextDriverExtension;
59 PAGED_CODE();
60
61 /* Get the extension and loop them */
62 DriverExtension = IoGetDrvObjExtension(DriverObject)->
63 ClientDriverExtension;
64 while (DriverExtension)
65 {
66 /* Get the next one */
67 NextDriverExtension = DriverExtension->NextExtension;
68 ExFreePoolWithTag(DriverExtension, TAG_DRIVER_EXTENSION);
69
70 /* Move on */
71 DriverExtension = NextDriverExtension;
72 }
73
74 /* Check if the driver image is still loaded */
75 if (DriverObject->DriverSection)
76 {
77 /* Unload it */
78 MmUnloadSystemImage(DriverObject->DriverSection);
79 }
80
81 /* Check if it has a name */
82 if (DriverObject->DriverName.Buffer)
83 {
84 /* Free it */
85 ExFreePool(DriverObject->DriverName.Buffer);
86 }
87
88 #if 0 /* See a bit of hack in IopCreateDriver */
89 /* Check if it has a service key name */
90 if (DriverObject->DriverExtension->ServiceKeyName.Buffer)
91 {
92 /* Free it */
93 ExFreePool(DriverObject->DriverExtension->ServiceKeyName.Buffer);
94 }
95 #endif
96 }
97
98 NTSTATUS FASTCALL
99 IopGetDriverObject(
100 PDRIVER_OBJECT *DriverObject,
101 PUNICODE_STRING ServiceName,
102 BOOLEAN FileSystem)
103 {
104 PDRIVER_OBJECT Object;
105 WCHAR NameBuffer[MAX_PATH];
106 UNICODE_STRING DriverName;
107 NTSTATUS Status;
108
109 DPRINT("IopGetDriverObject(%p '%wZ' %x)\n",
110 DriverObject, ServiceName, FileSystem);
111
112 *DriverObject = NULL;
113
114 /* Create ModuleName string */
115 if (ServiceName == NULL || ServiceName->Buffer == NULL)
116 /* We don't know which DriverObject we have to open */
117 return STATUS_INVALID_PARAMETER_2;
118
119 DriverName.Buffer = NameBuffer;
120 DriverName.Length = 0;
121 DriverName.MaximumLength = sizeof(NameBuffer);
122
123 if (FileSystem == TRUE)
124 RtlAppendUnicodeToString(&DriverName, FILESYSTEM_ROOT_NAME);
125 else
126 RtlAppendUnicodeToString(&DriverName, DRIVER_ROOT_NAME);
127 RtlAppendUnicodeStringToString(&DriverName, ServiceName);
128
129 DPRINT("Driver name: '%wZ'\n", &DriverName);
130
131 /* Open driver object */
132 Status = ObReferenceObjectByName(
133 &DriverName,
134 OBJ_OPENIF | OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, /* Attributes */
135 NULL, /* PassedAccessState */
136 0, /* DesiredAccess */
137 IoDriverObjectType,
138 KernelMode,
139 NULL, /* ParseContext */
140 (PVOID*)&Object);
141
142 if (!NT_SUCCESS(Status))
143 {
144 DPRINT("Failed to reference driver object, status=0x%08x\n", Status);
145 return Status;
146 }
147
148 *DriverObject = Object;
149
150 DPRINT("Driver Object: %p\n", Object);
151
152 return STATUS_SUCCESS;
153 }
154
155 /*
156 * RETURNS
157 * TRUE if String2 contains String1 as a suffix.
158 */
159 BOOLEAN
160 NTAPI
161 IopSuffixUnicodeString(
162 IN PCUNICODE_STRING String1,
163 IN PCUNICODE_STRING String2)
164 {
165 PWCHAR pc1;
166 PWCHAR pc2;
167 ULONG Length;
168
169 if (String2->Length < String1->Length)
170 return FALSE;
171
172 Length = String1->Length / 2;
173 pc1 = String1->Buffer;
174 pc2 = &String2->Buffer[String2->Length / sizeof(WCHAR) - Length];
175
176 if (pc1 && pc2)
177 {
178 while (Length--)
179 {
180 if( *pc1++ != *pc2++ )
181 return FALSE;
182 }
183 return TRUE;
184 }
185 return FALSE;
186 }
187
188 /*
189 * IopDisplayLoadingMessage
190 *
191 * Display 'Loading XXX...' message.
192 */
193
194 VOID
195 FASTCALL
196 INIT_FUNCTION
197 IopDisplayLoadingMessage(PUNICODE_STRING ServiceName)
198 {
199 CHAR TextBuffer[256];
200 UNICODE_STRING DotSys = RTL_CONSTANT_STRING(L".SYS");
201
202 if (ExpInTextModeSetup) return;
203 if (!KeLoaderBlock) return;
204 RtlUpcaseUnicodeString(ServiceName, ServiceName, FALSE);
205 snprintf(TextBuffer, sizeof(TextBuffer),
206 "%s%sSystem32\\Drivers\\%wZ%s\n",
207 KeLoaderBlock->ArcBootDeviceName,
208 KeLoaderBlock->NtBootPathName,
209 ServiceName,
210 IopSuffixUnicodeString(&DotSys, ServiceName) ? "" : ".SYS");
211 HalDisplayString(TextBuffer);
212 }
213
214 /*
215 * IopNormalizeImagePath
216 *
217 * Normalize an image path to contain complete path.
218 *
219 * Parameters
220 * ImagePath
221 * The input path and on exit the result path. ImagePath.Buffer
222 * must be allocated by ExAllocatePool on input. Caller is responsible
223 * for freeing the buffer when it's no longer needed.
224 *
225 * ServiceName
226 * Name of the service that ImagePath belongs to.
227 *
228 * Return Value
229 * Status
230 *
231 * Remarks
232 * The input image path isn't freed on error.
233 */
234
235 NTSTATUS FASTCALL
236 IopNormalizeImagePath(
237 IN OUT PUNICODE_STRING ImagePath,
238 IN PUNICODE_STRING ServiceName)
239 {
240 UNICODE_STRING InputImagePath;
241
242 RtlCopyMemory(
243 &InputImagePath,
244 ImagePath,
245 sizeof(UNICODE_STRING));
246
247 if (InputImagePath.Length == 0)
248 {
249 ImagePath->Length = 0;
250 ImagePath->MaximumLength =
251 (33 * sizeof(WCHAR)) + ServiceName->Length + sizeof(UNICODE_NULL);
252 ImagePath->Buffer = ExAllocatePool(NonPagedPool, ImagePath->MaximumLength);
253 if (ImagePath->Buffer == NULL)
254 return STATUS_NO_MEMORY;
255
256 RtlAppendUnicodeToString(ImagePath, L"\\SystemRoot\\system32\\drivers\\");
257 RtlAppendUnicodeStringToString(ImagePath, ServiceName);
258 RtlAppendUnicodeToString(ImagePath, L".sys");
259 } else
260 if (InputImagePath.Buffer[0] != L'\\')
261 {
262 ImagePath->Length = 0;
263 ImagePath->MaximumLength =
264 12 * sizeof(WCHAR) + InputImagePath.Length + sizeof(UNICODE_NULL);
265 ImagePath->Buffer = ExAllocatePool(NonPagedPool, ImagePath->MaximumLength);
266 if (ImagePath->Buffer == NULL)
267 return STATUS_NO_MEMORY;
268
269 RtlAppendUnicodeToString(ImagePath, L"\\SystemRoot\\");
270 RtlAppendUnicodeStringToString(ImagePath, &InputImagePath);
271
272 /* Free caller's string */
273 ExFreePoolWithTag(InputImagePath.Buffer, TAG_RTLREGISTRY);
274 }
275
276 return STATUS_SUCCESS;
277 }
278
279 /*
280 * IopLoadServiceModule
281 *
282 * Load a module specified by registry settings for service.
283 *
284 * Parameters
285 * ServiceName
286 * Name of the service to load.
287 *
288 * Return Value
289 * Status
290 */
291
292 NTSTATUS FASTCALL
293 IopLoadServiceModule(
294 IN PUNICODE_STRING ServiceName,
295 OUT PLDR_DATA_TABLE_ENTRY *ModuleObject)
296 {
297 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
298 ULONG ServiceStart;
299 UNICODE_STRING ServiceImagePath, CCSName;
300 NTSTATUS Status;
301 HANDLE CCSKey, ServiceKey;
302 PVOID BaseAddress;
303
304 DPRINT("IopLoadServiceModule(%wZ, 0x%p)\n", ServiceName, ModuleObject);
305
306 /* FIXME: This check may be removed once the bug is fixed */
307 if (ServiceName->Buffer == NULL)
308 {
309 DPRINT1("If you see this, please report to Fireball or hpoussin!\n");
310 return STATUS_UNSUCCESSFUL;
311 }
312
313 if (ExpInTextModeSetup)
314 {
315 /* We have no registry, but luckily we know where all the drivers are */
316
317 /* ServiceStart < 4 is all that matters */
318 ServiceStart = 0;
319
320 /* IopNormalizeImagePath will do all of the work for us if we give it an empty string */
321 ServiceImagePath.Length = ServiceImagePath.MaximumLength = 0;
322 ServiceImagePath.Buffer = NULL;
323 }
324 else
325 {
326 /* Open CurrentControlSet */
327 RtlInitUnicodeString(&CCSName,
328 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
329 Status = IopOpenRegistryKeyEx(&CCSKey, NULL, &CCSName, KEY_READ);
330 if (!NT_SUCCESS(Status))
331 {
332 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
333 return Status;
334 }
335
336 /* Open service key */
337 Status = IopOpenRegistryKeyEx(&ServiceKey, CCSKey, ServiceName, KEY_READ);
338 if (!NT_SUCCESS(Status))
339 {
340 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
341 ZwClose(CCSKey);
342 return Status;
343 }
344
345 /*
346 * Get information about the service.
347 */
348
349 RtlZeroMemory(QueryTable, sizeof(QueryTable));
350
351 RtlInitUnicodeString(&ServiceImagePath, NULL);
352
353 QueryTable[0].Name = L"Start";
354 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
355 QueryTable[0].EntryContext = &ServiceStart;
356
357 QueryTable[1].Name = L"ImagePath";
358 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
359 QueryTable[1].EntryContext = &ServiceImagePath;
360
361 Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
362 (PWSTR)ServiceKey, QueryTable, NULL, NULL);
363
364 ZwClose(ServiceKey);
365 ZwClose(CCSKey);
366
367 if (!NT_SUCCESS(Status))
368 {
369 DPRINT1("RtlQueryRegistryValues() failed (Status %x)\n", Status);
370 return Status;
371 }
372 }
373
374 /*
375 * Normalize the image path for all later processing.
376 */
377
378 Status = IopNormalizeImagePath(&ServiceImagePath, ServiceName);
379
380 if (!NT_SUCCESS(Status))
381 {
382 DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);
383 return Status;
384 }
385
386 /*
387 * Case for disabled drivers
388 */
389
390 if (ServiceStart >= 4)
391 {
392 /* FIXME: Check if it is the right status code */
393 Status = STATUS_PLUGPLAY_NO_DEVICE;
394 }
395 else
396 {
397 DPRINT("Loading module\n");
398 Status = MmLoadSystemImage(&ServiceImagePath, NULL, NULL, 0, (PVOID)ModuleObject, &BaseAddress);
399 }
400
401 ExFreePool(ServiceImagePath.Buffer);
402
403 /*
404 * Now check if the module was loaded successfully.
405 */
406
407 if (!NT_SUCCESS(Status))
408 {
409 DPRINT("Module loading failed (Status %x)\n", Status);
410 }
411
412 DPRINT("Module loading (Status %x)\n", Status);
413
414 return Status;
415 }
416
417 /*
418 * IopInitializeDriverModule
419 *
420 * Initalize a loaded driver.
421 *
422 * Parameters
423 * DeviceNode
424 * Pointer to device node.
425 *
426 * ModuleObject
427 * Module object representing the driver. It can be retrieve by
428 * IopLoadServiceModule.
429 *
430 * ServiceName
431 * Name of the service (as in registry).
432 *
433 * FileSystemDriver
434 * Set to TRUE for file system drivers.
435 *
436 * DriverObject
437 * On successful return this contains the driver object representing
438 * the loaded driver.
439 */
440
441 NTSTATUS FASTCALL
442 IopInitializeDriverModule(
443 IN PDEVICE_NODE DeviceNode,
444 IN PLDR_DATA_TABLE_ENTRY ModuleObject,
445 IN PUNICODE_STRING ServiceName,
446 IN BOOLEAN FileSystemDriver,
447 OUT PDRIVER_OBJECT *DriverObject)
448 {
449 const WCHAR ServicesKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
450 WCHAR NameBuffer[MAX_PATH];
451 UNICODE_STRING DriverName;
452 UNICODE_STRING RegistryKey;
453 PDRIVER_INITIALIZE DriverEntry;
454 PDRIVER_OBJECT Driver;
455 NTSTATUS Status;
456
457 DriverEntry = ModuleObject->EntryPoint;
458
459 if (ServiceName != NULL && ServiceName->Length != 0)
460 {
461 RegistryKey.Length = 0;
462 RegistryKey.MaximumLength = sizeof(ServicesKeyName) + ServiceName->Length;
463 RegistryKey.Buffer = ExAllocatePool(PagedPool, RegistryKey.MaximumLength);
464 if (RegistryKey.Buffer == NULL)
465 {
466 return STATUS_INSUFFICIENT_RESOURCES;
467 }
468 RtlAppendUnicodeToString(&RegistryKey, ServicesKeyName);
469 RtlAppendUnicodeStringToString(&RegistryKey, ServiceName);
470 }
471 else
472 {
473 RtlInitUnicodeString(&RegistryKey, NULL);
474 }
475
476 /* Create ModuleName string */
477 if (ServiceName && ServiceName->Length > 0)
478 {
479 if (FileSystemDriver == TRUE)
480 wcscpy(NameBuffer, FILESYSTEM_ROOT_NAME);
481 else
482 wcscpy(NameBuffer, DRIVER_ROOT_NAME);
483
484 RtlInitUnicodeString(&DriverName, NameBuffer);
485 DriverName.MaximumLength = sizeof(NameBuffer);
486
487 RtlAppendUnicodeStringToString(&DriverName, ServiceName);
488
489 DPRINT("Driver name: '%wZ'\n", &DriverName);
490 }
491 else
492 DriverName.Length = 0;
493
494 Status = IopCreateDriver(
495 DriverName.Length > 0 ? &DriverName : NULL,
496 DriverEntry,
497 &RegistryKey,
498 ModuleObject,
499 &Driver);
500 RtlFreeUnicodeString(&RegistryKey);
501
502 *DriverObject = Driver;
503 if (!NT_SUCCESS(Status))
504 {
505 DPRINT("IopCreateDriver() failed (Status 0x%08lx)\n", Status);
506 return Status;
507 }
508
509 /* Set the driver as initialized */
510 IopReadyDeviceObjects(Driver);
511
512 if (PnpSystemInit) IopReinitializeDrivers();
513
514 return STATUS_SUCCESS;
515 }
516
517 /*
518 * IopAttachFilterDriversCallback
519 *
520 * Internal routine used by IopAttachFilterDrivers.
521 */
522
523 NTSTATUS NTAPI
524 IopAttachFilterDriversCallback(
525 PWSTR ValueName,
526 ULONG ValueType,
527 PVOID ValueData,
528 ULONG ValueLength,
529 PVOID Context,
530 PVOID EntryContext)
531 {
532 PDEVICE_NODE DeviceNode = Context;
533 UNICODE_STRING ServiceName;
534 PWCHAR Filters;
535 PLDR_DATA_TABLE_ENTRY ModuleObject;
536 PDRIVER_OBJECT DriverObject;
537 NTSTATUS Status;
538
539 for (Filters = ValueData;
540 ((ULONG_PTR)Filters - (ULONG_PTR)ValueData) < ValueLength &&
541 *Filters != 0;
542 Filters += (ServiceName.Length / sizeof(WCHAR)) + 1)
543 {
544 DPRINT("Filter Driver: %S (%wZ)\n", Filters, &DeviceNode->InstancePath);
545 ServiceName.Buffer = Filters;
546 ServiceName.MaximumLength =
547 ServiceName.Length = wcslen(Filters) * sizeof(WCHAR);
548
549 /* Load and initialize the filter driver */
550 Status = IopLoadServiceModule(&ServiceName, &ModuleObject);
551 if (Status != STATUS_IMAGE_ALREADY_LOADED)
552 {
553 if (!NT_SUCCESS(Status))
554 continue;
555
556 Status = IopInitializeDriverModule(DeviceNode, ModuleObject, &ServiceName,
557 FALSE, &DriverObject);
558 if (!NT_SUCCESS(Status))
559 continue;
560 }
561 else
562 {
563 /* get existing DriverObject pointer */
564 Status = IopGetDriverObject(
565 &DriverObject,
566 &ServiceName,
567 FALSE);
568 if (!NT_SUCCESS(Status))
569 {
570 DPRINT1("IopGetDriverObject() returned status 0x%08x!\n", Status);
571 continue;
572 }
573 }
574
575 Status = IopInitializeDevice(DeviceNode, DriverObject);
576 if (!NT_SUCCESS(Status))
577 continue;
578 }
579
580 return STATUS_SUCCESS;
581 }
582
583 /*
584 * IopAttachFilterDrivers
585 *
586 * Load filter drivers for specified device node.
587 *
588 * Parameters
589 * Lower
590 * Set to TRUE for loading lower level filters or FALSE for upper
591 * level filters.
592 */
593
594 NTSTATUS FASTCALL
595 IopAttachFilterDrivers(
596 PDEVICE_NODE DeviceNode,
597 BOOLEAN Lower)
598 {
599 RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { NULL, 0, NULL, NULL, 0, NULL, 0 }, };
600 UNICODE_STRING Class;
601 WCHAR ClassBuffer[40];
602 UNICODE_STRING EnumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
603 HANDLE EnumRootKey, SubKey;
604 NTSTATUS Status;
605
606 /* Open enumeration root key */
607 Status = IopOpenRegistryKeyEx(&EnumRootKey, NULL,
608 &EnumRoot, KEY_READ);
609 if (!NT_SUCCESS(Status))
610 {
611 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
612 return Status;
613 }
614
615 /* Open subkey */
616 Status = IopOpenRegistryKeyEx(&SubKey, EnumRootKey,
617 &DeviceNode->InstancePath, KEY_READ);
618 if (!NT_SUCCESS(Status))
619 {
620 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
621 ZwClose(EnumRootKey);
622 return Status;
623 }
624
625 /*
626 * First load the device filters
627 */
628 QueryTable[0].QueryRoutine = IopAttachFilterDriversCallback;
629 if (Lower)
630 QueryTable[0].Name = L"LowerFilters";
631 else
632 QueryTable[0].Name = L"UpperFilters";
633 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
634
635 RtlQueryRegistryValues(
636 RTL_REGISTRY_HANDLE,
637 (PWSTR)SubKey,
638 QueryTable,
639 DeviceNode,
640 NULL);
641
642 /*
643 * Now get the class GUID
644 */
645 Class.Length = 0;
646 Class.MaximumLength = 40 * sizeof(WCHAR);
647 Class.Buffer = ClassBuffer;
648 QueryTable[0].QueryRoutine = NULL;
649 QueryTable[0].Name = L"ClassGUID";
650 QueryTable[0].EntryContext = &Class;
651 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT;
652
653 Status = RtlQueryRegistryValues(
654 RTL_REGISTRY_HANDLE,
655 (PWSTR)SubKey,
656 QueryTable,
657 DeviceNode,
658 NULL);
659
660 /* Close handles */
661 ZwClose(SubKey);
662 ZwClose(EnumRootKey);
663
664 /*
665 * Load the class filter driver
666 */
667 if (NT_SUCCESS(Status))
668 {
669 UNICODE_STRING ControlClass = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class");
670
671 Status = IopOpenRegistryKeyEx(&EnumRootKey, NULL,
672 &ControlClass, KEY_READ);
673 if (!NT_SUCCESS(Status))
674 {
675 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
676 return Status;
677 }
678
679 /* Open subkey */
680 Status = IopOpenRegistryKeyEx(&SubKey, EnumRootKey,
681 &Class, KEY_READ);
682 if (!NT_SUCCESS(Status))
683 {
684 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
685 ZwClose(EnumRootKey);
686 return Status;
687 }
688
689 QueryTable[0].QueryRoutine = IopAttachFilterDriversCallback;
690 if (Lower)
691 QueryTable[0].Name = L"LowerFilters";
692 else
693 QueryTable[0].Name = L"UpperFilters";
694 QueryTable[0].EntryContext = NULL;
695 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
696
697 RtlQueryRegistryValues(
698 RTL_REGISTRY_HANDLE,
699 (PWSTR)SubKey,
700 QueryTable,
701 DeviceNode,
702 NULL);
703
704 /* Clean up */
705 ZwClose(SubKey);
706 ZwClose(EnumRootKey);
707 }
708
709 return STATUS_SUCCESS;
710 }
711
712 NTSTATUS
713 NTAPI
714 MiResolveImageReferences(IN PVOID ImageBase,
715 IN PUNICODE_STRING ImageFileDirectory,
716 IN PUNICODE_STRING NamePrefix OPTIONAL,
717 OUT PCHAR *MissingApi,
718 OUT PWCHAR *MissingDriver,
719 OUT PLOAD_IMPORTS *LoadImports);
720
721 //
722 // Used for images already loaded (boot drivers)
723 //
724 NTSTATUS
725 NTAPI
726 INIT_FUNCTION
727 LdrProcessDriverModule(PLDR_DATA_TABLE_ENTRY LdrEntry,
728 PUNICODE_STRING FileName,
729 PLDR_DATA_TABLE_ENTRY *ModuleObject)
730 {
731 NTSTATUS Status;
732 UNICODE_STRING BaseName, BaseDirectory;
733 PLOAD_IMPORTS LoadedImports = (PVOID)-2;
734 PCHAR MissingApiName, Buffer;
735 PWCHAR MissingDriverName;
736 PVOID DriverBase = LdrEntry->DllBase;
737
738 /* Allocate a buffer we'll use for names */
739 Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
740 if (!Buffer)
741 {
742 /* Fail */
743 return STATUS_INSUFFICIENT_RESOURCES;
744 }
745
746 /* Check for a separator */
747 if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
748 {
749 PWCHAR p;
750 ULONG BaseLength;
751
752 /* Loop the path until we get to the base name */
753 p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
754 while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
755
756 /* Get the length */
757 BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
758 BaseLength *= sizeof(WCHAR);
759
760 /* Setup the string */
761 BaseName.Length = (USHORT)BaseLength;
762 BaseName.Buffer = p;
763 }
764 else
765 {
766 /* Otherwise, we already have a base name */
767 BaseName.Length = FileName->Length;
768 BaseName.Buffer = FileName->Buffer;
769 }
770
771 /* Setup the maximum length */
772 BaseName.MaximumLength = BaseName.Length;
773
774 /* Now compute the base directory */
775 BaseDirectory = *FileName;
776 BaseDirectory.Length -= BaseName.Length;
777 BaseDirectory.MaximumLength = BaseDirectory.Length;
778
779 /* Resolve imports */
780 MissingApiName = Buffer;
781 Status = MiResolveImageReferences(DriverBase,
782 &BaseDirectory,
783 NULL,
784 &MissingApiName,
785 &MissingDriverName,
786 &LoadedImports);
787 if (!NT_SUCCESS(Status)) return Status;
788
789 /* Return */
790 *ModuleObject = LdrEntry;
791 return STATUS_SUCCESS;
792 }
793
794 /*
795 * IopInitializeBuiltinDriver
796 *
797 * Initialize a driver that is already loaded in memory.
798 */
799
800 NTSTATUS
801 NTAPI
802 INIT_FUNCTION
803 IopInitializeBuiltinDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
804 {
805 PDEVICE_NODE DeviceNode;
806 PDRIVER_OBJECT DriverObject;
807 NTSTATUS Status;
808 PWCHAR FileNameWithoutPath;
809 LPWSTR FileExtension;
810 PUNICODE_STRING ModuleName = &LdrEntry->BaseDllName;
811 UNICODE_STRING ServiceName;
812
813 /*
814 * Display 'Loading XXX...' message
815 */
816 IopDisplayLoadingMessage(ModuleName);
817 InbvIndicateProgress();
818
819 /*
820 * Generate filename without path (not needed by freeldr)
821 */
822 FileNameWithoutPath = wcsrchr(ModuleName->Buffer, L'\\');
823 if (FileNameWithoutPath == NULL)
824 {
825 FileNameWithoutPath = ModuleName->Buffer;
826 }
827 else
828 {
829 FileNameWithoutPath++;
830 }
831
832 /*
833 * Strip the file extension from ServiceName
834 */
835 RtlCreateUnicodeString(&ServiceName, FileNameWithoutPath);
836 FileExtension = wcsrchr(ServiceName.Buffer, '.');
837 if (FileExtension != NULL)
838 {
839 ServiceName.Length -= wcslen(FileExtension) * sizeof(WCHAR);
840 FileExtension[0] = 0;
841 }
842
843 /*
844 * Determine the right device object
845 */
846 /* Use IopRootDeviceNode for now */
847 Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &ServiceName, &DeviceNode);
848 if (!NT_SUCCESS(Status))
849 {
850 DPRINT1("Driver '%wZ' load failed, status (%x)\n", ModuleName, Status);
851 return(Status);
852 }
853
854 /*
855 * Initialize the driver
856 */
857 Status = IopInitializeDriverModule(DeviceNode, LdrEntry,
858 &DeviceNode->ServiceName, FALSE, &DriverObject);
859
860 if (!NT_SUCCESS(Status))
861 {
862 IopFreeDeviceNode(DeviceNode);
863 return Status;
864 }
865
866 Status = IopInitializeDevice(DeviceNode, DriverObject);
867 if (NT_SUCCESS(Status))
868 {
869 Status = IopStartDevice(DeviceNode);
870 }
871
872 return Status;
873 }
874
875 /*
876 * IopInitializeBootDrivers
877 *
878 * Initialize boot drivers and free memory for boot files.
879 *
880 * Parameters
881 * None
882 *
883 * Return Value
884 * None
885 */
886 VOID
887 FASTCALL
888 INIT_FUNCTION
889 IopInitializeBootDrivers(VOID)
890 {
891 PLIST_ENTRY ListHead, NextEntry, NextEntry2;
892 PLDR_DATA_TABLE_ENTRY LdrEntry;
893 PDEVICE_NODE DeviceNode;
894 PDRIVER_OBJECT DriverObject;
895 LDR_DATA_TABLE_ENTRY ModuleObject;
896 NTSTATUS Status;
897 UNICODE_STRING DriverName;
898 ULONG i, Index;
899 PDRIVER_INFORMATION DriverInfo, DriverInfoTag;
900 HANDLE KeyHandle;
901 PBOOT_DRIVER_LIST_ENTRY BootEntry;
902 DPRINT("IopInitializeBootDrivers()\n");
903
904 /* Use IopRootDeviceNode for now */
905 Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, NULL, &DeviceNode);
906 if (!NT_SUCCESS(Status)) return;
907
908 /* Setup the module object for the RAW FS Driver */
909 ModuleObject.DllBase = NULL;
910 ModuleObject.SizeOfImage = 0;
911 ModuleObject.EntryPoint = RawFsDriverEntry;
912 RtlInitUnicodeString(&DriverName, L"RAW");
913
914 /* Initialize it */
915 Status = IopInitializeDriverModule(DeviceNode,
916 &ModuleObject,
917 &DriverName,
918 TRUE,
919 &DriverObject);
920 if (!NT_SUCCESS(Status))
921 {
922 /* Fail */
923 IopFreeDeviceNode(DeviceNode);
924 return;
925 }
926
927 /* Now initialize the associated device */
928 Status = IopInitializeDevice(DeviceNode, DriverObject);
929 if (!NT_SUCCESS(Status))
930 {
931 /* Fail */
932 IopFreeDeviceNode(DeviceNode);
933 return;
934 }
935
936 /* Start it up */
937 Status = IopStartDevice(DeviceNode);
938 if (!NT_SUCCESS(Status))
939 {
940 /* Fail */
941 IopFreeDeviceNode(DeviceNode);
942 return;
943 }
944
945 /* Get highest group order index */
946 IopGroupIndex = PpInitGetGroupOrderIndex(NULL);
947 if (IopGroupIndex == 0xFFFF) ASSERT(FALSE);
948
949 /* Allocate the group table */
950 IopGroupTable = ExAllocatePoolWithTag(PagedPool,
951 IopGroupIndex * sizeof(LIST_ENTRY),
952 TAG_IO);
953 if (IopGroupTable == NULL) ASSERT(FALSE);
954
955 /* Initialize the group table lists */
956 for (i = 0; i < IopGroupIndex; i++) InitializeListHead(&IopGroupTable[i]);
957
958 /* Loop the boot modules */
959 ListHead = &KeLoaderBlock->LoadOrderListHead;
960 NextEntry = ListHead->Flink;
961 while (ListHead != NextEntry)
962 {
963 /* Get the entry */
964 LdrEntry = CONTAINING_RECORD(NextEntry,
965 LDR_DATA_TABLE_ENTRY,
966 InLoadOrderLinks);
967
968 /* Check if the DLL needs to be initialized */
969 if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
970 {
971 /* Call its entrypoint */
972 MmCallDllInitialize(LdrEntry, NULL);
973 }
974
975 /* Go to the next driver */
976 NextEntry = NextEntry->Flink;
977 }
978
979 /* Loop the boot drivers */
980 ListHead = &KeLoaderBlock->BootDriverListHead;
981 NextEntry = ListHead->Flink;
982 while (ListHead != NextEntry)
983 {
984 /* Get the entry */
985 BootEntry = CONTAINING_RECORD(NextEntry,
986 BOOT_DRIVER_LIST_ENTRY,
987 Link);
988
989 /* Get the driver loader entry */
990 LdrEntry = BootEntry->LdrEntry;
991
992 /* Allocate our internal accounting structure */
993 DriverInfo = ExAllocatePoolWithTag(PagedPool,
994 sizeof(DRIVER_INFORMATION),
995 TAG_IO);
996 if (DriverInfo)
997 {
998 /* Zero it and initialize it */
999 RtlZeroMemory(DriverInfo, sizeof(DRIVER_INFORMATION));
1000 InitializeListHead(&DriverInfo->Link);
1001 DriverInfo->DataTableEntry = BootEntry;
1002
1003 /* Open the registry key */
1004 Status = IopOpenRegistryKeyEx(&KeyHandle,
1005 NULL,
1006 &BootEntry->RegistryPath,
1007 KEY_READ);
1008 if ((NT_SUCCESS(Status)) || /* ReactOS HACK for SETUPLDR */
1009 ((KeLoaderBlock->SetupLdrBlock) && ((KeyHandle = (PVOID)1)))) // yes, it's an assignment!
1010 {
1011 /* Save the handle */
1012 DriverInfo->ServiceHandle = KeyHandle;
1013
1014 /* Get the group oder index */
1015 Index = PpInitGetGroupOrderIndex(KeyHandle);
1016
1017 /* Get the tag position */
1018 DriverInfo->TagPosition = PipGetDriverTagPriority(KeyHandle);
1019
1020 /* Insert it into the list, at the right place */
1021 ASSERT(Index < IopGroupIndex);
1022 NextEntry2 = IopGroupTable[Index].Flink;
1023 while (NextEntry2 != &IopGroupTable[Index])
1024 {
1025 /* Get the driver info */
1026 DriverInfoTag = CONTAINING_RECORD(NextEntry2,
1027 DRIVER_INFORMATION,
1028 Link);
1029
1030 /* Check if we found the right tag position */
1031 if (DriverInfoTag->TagPosition > DriverInfo->TagPosition)
1032 {
1033 /* We're done */
1034 break;
1035 }
1036
1037 /* Next entry */
1038 NextEntry2 = NextEntry2->Flink;
1039 }
1040
1041 /* Insert us right before the next entry */
1042 NextEntry2 = NextEntry2->Blink;
1043 InsertHeadList(NextEntry2, &DriverInfo->Link);
1044 }
1045 }
1046
1047 /* Go to the next driver */
1048 NextEntry = NextEntry->Flink;
1049 }
1050
1051 /* Loop each group index */
1052 for (i = 0; i < IopGroupIndex; i++)
1053 {
1054 /* Loop each group table */
1055 NextEntry = IopGroupTable[i].Flink;
1056 while (NextEntry != &IopGroupTable[i])
1057 {
1058 /* Get the entry */
1059 DriverInfo = CONTAINING_RECORD(NextEntry,
1060 DRIVER_INFORMATION,
1061 Link);
1062
1063 /* Get the driver loader entry */
1064 LdrEntry = DriverInfo->DataTableEntry->LdrEntry;
1065
1066 /* Initialize it */
1067 IopInitializeBuiltinDriver(LdrEntry);
1068
1069 /* Next entry */
1070 NextEntry = NextEntry->Flink;
1071 }
1072 }
1073
1074 /* In old ROS, the loader list became empty after this point. Simulate. */
1075 InitializeListHead(&KeLoaderBlock->LoadOrderListHead);
1076 }
1077
1078 VOID
1079 FASTCALL
1080 INIT_FUNCTION
1081 IopInitializeSystemDrivers(VOID)
1082 {
1083 PUNICODE_STRING *DriverList, *SavedList;
1084
1085 /* No system drivers on the boot cd */
1086 if (KeLoaderBlock->SetupLdrBlock) return;
1087
1088 /* Get the driver list */
1089 SavedList = DriverList = CmGetSystemDriverList();
1090 ASSERT(DriverList);
1091
1092 /* Loop it */
1093 while (*DriverList)
1094 {
1095 /* Load the driver */
1096 ZwLoadDriver(*DriverList);
1097
1098 /* Free the entry */
1099 RtlFreeUnicodeString(*DriverList);
1100 ExFreePool(*DriverList);
1101
1102 /* Next entry */
1103 InbvIndicateProgress();
1104 DriverList++;
1105 }
1106
1107 /* Free the list */
1108 ExFreePool(SavedList);
1109 }
1110
1111 /*
1112 * IopUnloadDriver
1113 *
1114 * Unloads a device driver.
1115 *
1116 * Parameters
1117 * DriverServiceName
1118 * Name of the service to unload (registry key).
1119 *
1120 * UnloadPnpDrivers
1121 * Whether to unload Plug & Plug or only legacy drivers. If this
1122 * parameter is set to FALSE, the routine will unload only legacy
1123 * drivers.
1124 *
1125 * Return Value
1126 * Status
1127 *
1128 * To do
1129 * Guard the whole function by SEH.
1130 */
1131
1132 NTSTATUS NTAPI
1133 IopUnloadDriver(PUNICODE_STRING DriverServiceName, BOOLEAN UnloadPnpDrivers)
1134 {
1135 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1136 UNICODE_STRING ImagePath;
1137 UNICODE_STRING ServiceName;
1138 UNICODE_STRING ObjectName;
1139 PDRIVER_OBJECT DriverObject;
1140 PDEVICE_OBJECT DeviceObject;
1141 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1142 LOAD_UNLOAD_PARAMS LoadParams;
1143 NTSTATUS Status;
1144 LPWSTR Start;
1145 BOOLEAN SafeToUnload = TRUE;
1146
1147 DPRINT("IopUnloadDriver('%wZ', %d)\n", DriverServiceName, UnloadPnpDrivers);
1148
1149 PAGED_CODE();
1150
1151 /*
1152 * Get the service name from the registry key name
1153 */
1154
1155 Start = wcsrchr(DriverServiceName->Buffer, L'\\');
1156 if (Start == NULL)
1157 Start = DriverServiceName->Buffer;
1158 else
1159 Start++;
1160
1161 RtlInitUnicodeString(&ServiceName, Start);
1162
1163 /*
1164 * Construct the driver object name
1165 */
1166
1167 ObjectName.Length = (wcslen(Start) + 8) * sizeof(WCHAR);
1168 ObjectName.MaximumLength = ObjectName.Length + sizeof(WCHAR);
1169 ObjectName.Buffer = ExAllocatePool(PagedPool, ObjectName.MaximumLength);
1170 if (!ObjectName.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
1171 wcscpy(ObjectName.Buffer, L"\\Driver\\");
1172 memcpy(ObjectName.Buffer + 8, Start, ObjectName.Length - 8 * sizeof(WCHAR));
1173 ObjectName.Buffer[ObjectName.Length/sizeof(WCHAR)] = 0;
1174
1175 /*
1176 * Find the driver object
1177 */
1178 Status = ObReferenceObjectByName(&ObjectName,
1179 0,
1180 0,
1181 0,
1182 IoDriverObjectType,
1183 KernelMode,
1184 0,
1185 (PVOID*)&DriverObject);
1186
1187 if (!NT_SUCCESS(Status))
1188 {
1189 DPRINT1("Can't locate driver object for %wZ\n", &ObjectName);
1190 ExFreePool(ObjectName.Buffer);
1191 return Status;
1192 }
1193
1194 /*
1195 * Free the buffer for driver object name
1196 */
1197 ExFreePool(ObjectName.Buffer);
1198
1199 /* Check that driver is not already unloading */
1200 if (DriverObject->Flags & DRVO_UNLOAD_INVOKED)
1201 {
1202 DPRINT1("Driver deletion pending\n");
1203 ObDereferenceObject(DriverObject);
1204 return STATUS_DELETE_PENDING;
1205 }
1206
1207 /*
1208 * Get path of service...
1209 */
1210
1211 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1212
1213 RtlInitUnicodeString(&ImagePath, NULL);
1214
1215 QueryTable[0].Name = L"ImagePath";
1216 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1217 QueryTable[0].EntryContext = &ImagePath;
1218
1219 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1220 DriverServiceName->Buffer, QueryTable, NULL, NULL);
1221
1222 if (!NT_SUCCESS(Status))
1223 {
1224 DPRINT1("RtlQueryRegistryValues() failed (Status %x)\n", Status);
1225 ObDereferenceObject(DriverObject);
1226 return Status;
1227 }
1228
1229 /*
1230 * Normalize the image path for all later processing.
1231 */
1232
1233 Status = IopNormalizeImagePath(&ImagePath, &ServiceName);
1234
1235 if (!NT_SUCCESS(Status))
1236 {
1237 DPRINT1("IopNormalizeImagePath() failed (Status %x)\n", Status);
1238 ObDereferenceObject(DriverObject);
1239 return Status;
1240 }
1241
1242 /*
1243 * Free the service path
1244 */
1245
1246 ExFreePool(ImagePath.Buffer);
1247
1248 /*
1249 * Unload the module and release the references to the device object
1250 */
1251
1252 /* Call the load/unload routine, depending on current process */
1253 if (DriverObject->DriverUnload && DriverObject->DriverSection)
1254 {
1255 /* Loop through each device object of the driver
1256 and set DOE_UNLOAD_PENDING flag */
1257 DeviceObject = DriverObject->DeviceObject;
1258 while (DeviceObject)
1259 {
1260 /* Set the unload pending flag for the device */
1261 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1262 DeviceExtension->ExtensionFlags |= DOE_UNLOAD_PENDING;
1263
1264 /* Make sure there are no attached devices or no reference counts */
1265 if ((DeviceObject->ReferenceCount) || (DeviceObject->AttachedDevice))
1266 {
1267 /* Not safe to unload */
1268 DPRINT1("Drivers device object is referenced or has attached devices\n");
1269
1270 SafeToUnload = FALSE;
1271 }
1272
1273 DeviceObject = DeviceObject->NextDevice;
1274 }
1275
1276 /* If not safe to unload, then return success */
1277 if (!SafeToUnload)
1278 {
1279 ObDereferenceObject(DriverObject);
1280 return STATUS_SUCCESS;
1281 }
1282
1283 /* Set the unload invoked flag */
1284 DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
1285
1286 if (PsGetCurrentProcess() == PsInitialSystemProcess)
1287 {
1288 /* Just call right away */
1289 (*DriverObject->DriverUnload)(DriverObject);
1290 }
1291 else
1292 {
1293 /* Load/Unload must be called from system process */
1294
1295 /* Prepare parameters block */
1296 LoadParams.DriverObject = DriverObject;
1297 KeInitializeEvent(&LoadParams.Event, NotificationEvent, FALSE);
1298
1299 ExInitializeWorkItem(&LoadParams.WorkItem,
1300 (PWORKER_THREAD_ROUTINE)IopLoadUnloadDriver,
1301 (PVOID)&LoadParams);
1302
1303 /* Queue it */
1304 ExQueueWorkItem(&LoadParams.WorkItem, DelayedWorkQueue);
1305
1306 /* And wait when it completes */
1307 KeWaitForSingleObject(&LoadParams.Event, UserRequest, KernelMode,
1308 FALSE, NULL);
1309 }
1310
1311 /* Mark the driver object temporary, so it could be deleted later */
1312 ObMakeTemporaryObject(DriverObject);
1313
1314 /* Dereference it 2 times */
1315 ObDereferenceObject(DriverObject);
1316 ObDereferenceObject(DriverObject);
1317
1318 return STATUS_SUCCESS;
1319 }
1320 else
1321 {
1322 /* Dereference one time (refd inside this function) */
1323 ObDereferenceObject(DriverObject);
1324
1325 /* Return unloading failure */
1326 return STATUS_INVALID_DEVICE_REQUEST;
1327 }
1328 }
1329
1330 VOID
1331 NTAPI
1332 IopReinitializeDrivers(VOID)
1333 {
1334 PDRIVER_REINIT_ITEM ReinitItem;
1335 PLIST_ENTRY Entry;
1336
1337 /* Get the first entry and start looping */
1338 Entry = ExInterlockedRemoveHeadList(&DriverReinitListHead,
1339 &DriverReinitListLock);
1340 while (Entry)
1341 {
1342 /* Get the item*/
1343 ReinitItem = CONTAINING_RECORD(Entry, DRIVER_REINIT_ITEM, ItemEntry);
1344
1345 /* Increment reinitialization counter */
1346 ReinitItem->DriverObject->DriverExtension->Count++;
1347
1348 /* Remove the device object flag */
1349 ReinitItem->DriverObject->Flags &= ~DRVO_REINIT_REGISTERED;
1350
1351 /* Call the routine */
1352 ReinitItem->ReinitRoutine(ReinitItem->DriverObject,
1353 ReinitItem->Context,
1354 ReinitItem->DriverObject->
1355 DriverExtension->Count);
1356
1357 /* Free the entry */
1358 ExFreePool(Entry);
1359
1360 /* Move to the next one */
1361 Entry = ExInterlockedRemoveHeadList(&DriverReinitListHead,
1362 &DriverReinitListLock);
1363 }
1364 }
1365
1366 VOID
1367 NTAPI
1368 IopReinitializeBootDrivers(VOID)
1369 {
1370 PDRIVER_REINIT_ITEM ReinitItem;
1371 PLIST_ENTRY Entry;
1372
1373 /* Get the first entry and start looping */
1374 Entry = ExInterlockedRemoveHeadList(&DriverBootReinitListHead,
1375 &DriverBootReinitListLock);
1376 while (Entry)
1377 {
1378 /* Get the item*/
1379 ReinitItem = CONTAINING_RECORD(Entry, DRIVER_REINIT_ITEM, ItemEntry);
1380
1381 /* Increment reinitialization counter */
1382 ReinitItem->DriverObject->DriverExtension->Count++;
1383
1384 /* Remove the device object flag */
1385 ReinitItem->DriverObject->Flags &= ~DRVO_BOOTREINIT_REGISTERED;
1386
1387 /* Call the routine */
1388 ReinitItem->ReinitRoutine(ReinitItem->DriverObject,
1389 ReinitItem->Context,
1390 ReinitItem->DriverObject->
1391 DriverExtension->Count);
1392
1393 /* Free the entry */
1394 ExFreePool(Entry);
1395
1396 /* Move to the next one */
1397 Entry = ExInterlockedRemoveHeadList(&DriverBootReinitListHead,
1398 &DriverBootReinitListLock);
1399 }
1400 }
1401
1402 NTSTATUS
1403 NTAPI
1404 IopCreateDriver(IN PUNICODE_STRING DriverName OPTIONAL,
1405 IN PDRIVER_INITIALIZE InitializationFunction,
1406 IN PUNICODE_STRING RegistryPath,
1407 PLDR_DATA_TABLE_ENTRY ModuleObject,
1408 OUT PDRIVER_OBJECT *pDriverObject)
1409 {
1410 WCHAR NameBuffer[100];
1411 USHORT NameLength;
1412 UNICODE_STRING LocalDriverName;
1413 NTSTATUS Status;
1414 OBJECT_ATTRIBUTES ObjectAttributes;
1415 ULONG ObjectSize;
1416 PDRIVER_OBJECT DriverObject;
1417 UNICODE_STRING ServiceKeyName;
1418 HANDLE hDriver;
1419 ULONG i, RetryCount = 0;
1420
1421 try_again:
1422 /* First, create a unique name for the driver if we don't have one */
1423 if (!DriverName)
1424 {
1425 /* Create a random name and set up the string*/
1426 NameLength = (USHORT)swprintf(NameBuffer,
1427 L"\\Driver\\%08u",
1428 KeTickCount);
1429 LocalDriverName.Length = NameLength * sizeof(WCHAR);
1430 LocalDriverName.MaximumLength = LocalDriverName.Length + sizeof(UNICODE_NULL);
1431 LocalDriverName.Buffer = NameBuffer;
1432 }
1433 else
1434 {
1435 /* So we can avoid another code path, use a local var */
1436 LocalDriverName = *DriverName;
1437 }
1438
1439 /* Initialize the Attributes */
1440 ObjectSize = sizeof(DRIVER_OBJECT) + sizeof(EXTENDED_DRIVER_EXTENSION);
1441 InitializeObjectAttributes(&ObjectAttributes,
1442 &LocalDriverName,
1443 OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
1444 NULL,
1445 NULL);
1446
1447 /* Create the Object */
1448 Status = ObCreateObject(KernelMode,
1449 IoDriverObjectType,
1450 &ObjectAttributes,
1451 KernelMode,
1452 NULL,
1453 ObjectSize,
1454 0,
1455 0,
1456 (PVOID*)&DriverObject);
1457 if (!NT_SUCCESS(Status)) return Status;
1458
1459 DPRINT("IopCreateDriver(): created DO %p\n", DriverObject);
1460
1461 /* Set up the Object */
1462 RtlZeroMemory(DriverObject, ObjectSize);
1463 DriverObject->Type = IO_TYPE_DRIVER;
1464 DriverObject->Size = sizeof(DRIVER_OBJECT);
1465 DriverObject->Flags = DRVO_LEGACY_DRIVER;//DRVO_BUILTIN_DRIVER;
1466 DriverObject->DriverExtension = (PDRIVER_EXTENSION)(DriverObject + 1);
1467 DriverObject->DriverExtension->DriverObject = DriverObject;
1468 DriverObject->DriverInit = InitializationFunction;
1469 DriverObject->DriverSection = ModuleObject;
1470 /* Loop all Major Functions */
1471 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1472 {
1473 /* Invalidate each function */
1474 DriverObject->MajorFunction[i] = IopInvalidDeviceRequest;
1475 }
1476
1477 /* Set up the service key name buffer */
1478 ServiceKeyName.Buffer = ExAllocatePoolWithTag(PagedPool,
1479 LocalDriverName.Length +
1480 sizeof(WCHAR),
1481 TAG_IO);
1482 if (!ServiceKeyName.Buffer)
1483 {
1484 /* Fail */
1485 ObMakeTemporaryObject(DriverObject);
1486 ObDereferenceObject(DriverObject);
1487 return STATUS_INSUFFICIENT_RESOURCES;
1488 }
1489
1490 /* Fill out the key data and copy the buffer */
1491 ServiceKeyName.Length = LocalDriverName.Length;
1492 ServiceKeyName.MaximumLength = LocalDriverName.MaximumLength;
1493 RtlCopyMemory(ServiceKeyName.Buffer,
1494 LocalDriverName.Buffer,
1495 LocalDriverName.Length);
1496
1497 /* Null-terminate it and set it */
1498 ServiceKeyName.Buffer[ServiceKeyName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1499 DriverObject->DriverExtension->ServiceKeyName = ServiceKeyName;
1500
1501 /* Also store it in the Driver Object. This is a bit of a hack. */
1502 RtlCopyMemory(&DriverObject->DriverName,
1503 &ServiceKeyName,
1504 sizeof(UNICODE_STRING));
1505
1506 /* Add the Object and get its handle */
1507 Status = ObInsertObject(DriverObject,
1508 NULL,
1509 FILE_READ_DATA,
1510 0,
1511 NULL,
1512 &hDriver);
1513
1514 /* Eliminate small possibility when this function is called more than
1515 once in a row, and KeTickCount doesn't get enough time to change */
1516 if (!DriverName && (Status == STATUS_OBJECT_NAME_COLLISION) && (RetryCount < 100))
1517 {
1518 RetryCount++;
1519 goto try_again;
1520 }
1521
1522 if (!NT_SUCCESS(Status)) return Status;
1523
1524 /* Now reference it */
1525 Status = ObReferenceObjectByHandle(hDriver,
1526 0,
1527 IoDriverObjectType,
1528 KernelMode,
1529 (PVOID*)&DriverObject,
1530 NULL);
1531 if (!NT_SUCCESS(Status))
1532 {
1533 /* Fail */
1534 ObMakeTemporaryObject(DriverObject);
1535 ObDereferenceObject(DriverObject);
1536 return Status;
1537 }
1538
1539 /* Close the extra handle */
1540 ZwClose(hDriver);
1541
1542 DriverObject->HardwareDatabase = &IopHardwareDatabaseKey;
1543 DriverObject->DriverStart = ModuleObject ? ModuleObject->DllBase : 0;
1544 DriverObject->DriverSize = ModuleObject ? ModuleObject->SizeOfImage : 0;
1545
1546 /* Finally, call its init function */
1547 DPRINT("RegistryKey: %wZ\n", RegistryPath);
1548 DPRINT("Calling driver entrypoint at %p\n", InitializationFunction);
1549 Status = (*InitializationFunction)(DriverObject, RegistryPath);
1550 if (!NT_SUCCESS(Status))
1551 {
1552 /* If it didn't work, then kill the object */
1553 DPRINT1("'%wZ' initialization failed, status (0x%08lx)\n", DriverName, Status);
1554 DriverObject->DriverSection = NULL;
1555 ObMakeTemporaryObject(DriverObject);
1556 ObDereferenceObject(DriverObject);
1557 }
1558 else
1559 {
1560 /* Returns to caller the object */
1561 *pDriverObject = DriverObject;
1562 }
1563
1564 /* Loop all Major Functions */
1565 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1566 {
1567 /*
1568 * Make sure the driver didn't set any dispatch entry point to NULL!
1569 * Doing so is illegal; drivers shouldn't touch entry points they
1570 * do not implement.
1571 */
1572
1573 /* Check if it did so anyway */
1574 if (!DriverObject->MajorFunction[i])
1575 {
1576 /* Print a warning in the debug log */
1577 DPRINT1("Driver <%wZ> set DriverObject->MajorFunction[%d] to NULL!\n",
1578 &DriverObject->DriverName, i);
1579
1580 /* Fix it up */
1581 DriverObject->MajorFunction[i] = IopInvalidDeviceRequest;
1582 }
1583 }
1584
1585 /* Return the Status */
1586 return Status;
1587 }
1588
1589 /* PUBLIC FUNCTIONS ***********************************************************/
1590
1591 /*
1592 * @implemented
1593 */
1594 NTSTATUS
1595 NTAPI
1596 IoCreateDriver(IN PUNICODE_STRING DriverName OPTIONAL,
1597 IN PDRIVER_INITIALIZE InitializationFunction)
1598 {
1599 PDRIVER_OBJECT DriverObject;
1600 return IopCreateDriver(DriverName, InitializationFunction, NULL, NULL, &DriverObject);
1601 }
1602
1603 /*
1604 * @implemented
1605 */
1606 VOID
1607 NTAPI
1608 IoDeleteDriver(IN PDRIVER_OBJECT DriverObject)
1609 {
1610 /* Simply dereference the Object */
1611 ObDereferenceObject(DriverObject);
1612 }
1613
1614 /*
1615 * @implemented
1616 */
1617 VOID
1618 NTAPI
1619 IoRegisterBootDriverReinitialization(IN PDRIVER_OBJECT DriverObject,
1620 IN PDRIVER_REINITIALIZE ReinitRoutine,
1621 IN PVOID Context)
1622 {
1623 PDRIVER_REINIT_ITEM ReinitItem;
1624
1625 /* Allocate the entry */
1626 ReinitItem = ExAllocatePoolWithTag(NonPagedPool,
1627 sizeof(DRIVER_REINIT_ITEM),
1628 TAG_REINIT);
1629 if (!ReinitItem) return;
1630
1631 /* Fill it out */
1632 ReinitItem->DriverObject = DriverObject;
1633 ReinitItem->ReinitRoutine = ReinitRoutine;
1634 ReinitItem->Context = Context;
1635
1636 /* Set the Driver Object flag and insert the entry into the list */
1637 DriverObject->Flags |= DRVO_BOOTREINIT_REGISTERED;
1638 ExInterlockedInsertTailList(&DriverBootReinitListHead,
1639 &ReinitItem->ItemEntry,
1640 &DriverBootReinitListLock);
1641 }
1642
1643 /*
1644 * @implemented
1645 */
1646 VOID
1647 NTAPI
1648 IoRegisterDriverReinitialization(IN PDRIVER_OBJECT DriverObject,
1649 IN PDRIVER_REINITIALIZE ReinitRoutine,
1650 IN PVOID Context)
1651 {
1652 PDRIVER_REINIT_ITEM ReinitItem;
1653
1654 /* Allocate the entry */
1655 ReinitItem = ExAllocatePoolWithTag(NonPagedPool,
1656 sizeof(DRIVER_REINIT_ITEM),
1657 TAG_REINIT);
1658 if (!ReinitItem) return;
1659
1660 /* Fill it out */
1661 ReinitItem->DriverObject = DriverObject;
1662 ReinitItem->ReinitRoutine = ReinitRoutine;
1663 ReinitItem->Context = Context;
1664
1665 /* Set the Driver Object flag and insert the entry into the list */
1666 DriverObject->Flags |= DRVO_REINIT_REGISTERED;
1667 ExInterlockedInsertTailList(&DriverReinitListHead,
1668 &ReinitItem->ItemEntry,
1669 &DriverReinitListLock);
1670 }
1671
1672 /*
1673 * @implemented
1674 */
1675 NTSTATUS
1676 NTAPI
1677 IoAllocateDriverObjectExtension(IN PDRIVER_OBJECT DriverObject,
1678 IN PVOID ClientIdentificationAddress,
1679 IN ULONG DriverObjectExtensionSize,
1680 OUT PVOID *DriverObjectExtension)
1681 {
1682 KIRQL OldIrql;
1683 PIO_CLIENT_EXTENSION DriverExtensions, NewDriverExtension;
1684 BOOLEAN Inserted = FALSE;
1685
1686 /* Assume failure */
1687 *DriverObjectExtension = NULL;
1688
1689 /* Allocate the extension */
1690 NewDriverExtension = ExAllocatePoolWithTag(NonPagedPool,
1691 sizeof(IO_CLIENT_EXTENSION) +
1692 DriverObjectExtensionSize,
1693 TAG_DRIVER_EXTENSION);
1694 if (!NewDriverExtension) return STATUS_INSUFFICIENT_RESOURCES;
1695
1696 /* Clear the extension for teh caller */
1697 RtlZeroMemory(NewDriverExtension,
1698 sizeof(IO_CLIENT_EXTENSION) + DriverObjectExtensionSize);
1699
1700 /* Acqure lock */
1701 OldIrql = KeRaiseIrqlToDpcLevel();
1702
1703 /* Fill out the extension */
1704 NewDriverExtension->ClientIdentificationAddress = ClientIdentificationAddress;
1705
1706 /* Loop the current extensions */
1707 DriverExtensions = IoGetDrvObjExtension(DriverObject)->
1708 ClientDriverExtension;
1709 while (DriverExtensions)
1710 {
1711 /* Check if the identifier matches */
1712 if (DriverExtensions->ClientIdentificationAddress ==
1713 ClientIdentificationAddress)
1714 {
1715 /* We have a collision, break out */
1716 break;
1717 }
1718
1719 /* Go to the next one */
1720 DriverExtensions = DriverExtensions->NextExtension;
1721 }
1722
1723 /* Check if we didn't collide */
1724 if (!DriverExtensions)
1725 {
1726 /* Link this one in */
1727 NewDriverExtension->NextExtension =
1728 IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
1729 IoGetDrvObjExtension(DriverObject)->ClientDriverExtension =
1730 NewDriverExtension;
1731 Inserted = TRUE;
1732 }
1733
1734 /* Release the lock */
1735 KeLowerIrql(OldIrql);
1736
1737 /* Check if insertion failed */
1738 if (!Inserted)
1739 {
1740 /* Free the entry and fail */
1741 ExFreePool(NewDriverExtension);
1742 return STATUS_OBJECT_NAME_COLLISION;
1743 }
1744
1745 /* Otherwise, return the pointer */
1746 *DriverObjectExtension = NewDriverExtension + 1;
1747 return STATUS_SUCCESS;
1748 }
1749
1750 /*
1751 * @implemented
1752 */
1753 PVOID
1754 NTAPI
1755 IoGetDriverObjectExtension(IN PDRIVER_OBJECT DriverObject,
1756 IN PVOID ClientIdentificationAddress)
1757 {
1758 KIRQL OldIrql;
1759 PIO_CLIENT_EXTENSION DriverExtensions;
1760
1761 /* Acquire lock */
1762 OldIrql = KeRaiseIrqlToDpcLevel();
1763
1764 /* Loop the list until we find the right one */
1765 DriverExtensions = IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
1766 while (DriverExtensions)
1767 {
1768 /* Check for a match */
1769 if (DriverExtensions->ClientIdentificationAddress ==
1770 ClientIdentificationAddress)
1771 {
1772 /* Break out */
1773 break;
1774 }
1775
1776 /* Keep looping */
1777 DriverExtensions = DriverExtensions->NextExtension;
1778 }
1779
1780 /* Release lock */
1781 KeLowerIrql(OldIrql);
1782
1783 /* Return nothing or the extension */
1784 if (!DriverExtensions) return NULL;
1785 return DriverExtensions + 1;
1786 }
1787
1788 VOID NTAPI
1789 IopLoadUnloadDriver(PLOAD_UNLOAD_PARAMS LoadParams)
1790 {
1791 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
1792 UNICODE_STRING ImagePath;
1793 UNICODE_STRING ServiceName;
1794 NTSTATUS Status;
1795 ULONG Type;
1796 PDEVICE_NODE DeviceNode;
1797 PDRIVER_OBJECT DriverObject;
1798 PLDR_DATA_TABLE_ENTRY ModuleObject;
1799 PVOID BaseAddress;
1800 WCHAR *cur;
1801
1802 /* Check if it's an unload request */
1803 if (LoadParams->DriverObject)
1804 {
1805 (*LoadParams->DriverObject->DriverUnload)(LoadParams->DriverObject);
1806
1807 /* Return success and signal the event */
1808 LoadParams->Status = STATUS_SUCCESS;
1809 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1810 return;
1811 }
1812
1813 RtlInitUnicodeString(&ImagePath, NULL);
1814
1815 /*
1816 * Get the service name from the registry key name.
1817 */
1818 ASSERT(LoadParams->ServiceName->Length >= sizeof(WCHAR));
1819
1820 ServiceName = *LoadParams->ServiceName;
1821 cur = LoadParams->ServiceName->Buffer +
1822 (LoadParams->ServiceName->Length / sizeof(WCHAR)) - 1;
1823 while (LoadParams->ServiceName->Buffer != cur)
1824 {
1825 if(*cur == L'\\')
1826 {
1827 ServiceName.Buffer = cur + 1;
1828 ServiceName.Length = LoadParams->ServiceName->Length -
1829 (USHORT)((ULONG_PTR)ServiceName.Buffer -
1830 (ULONG_PTR)LoadParams->ServiceName->Buffer);
1831 break;
1832 }
1833 cur--;
1834 }
1835
1836 /*
1837 * Get service type.
1838 */
1839
1840 RtlZeroMemory(&QueryTable, sizeof(QueryTable));
1841
1842 RtlInitUnicodeString(&ImagePath, NULL);
1843
1844 QueryTable[0].Name = L"Type";
1845 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
1846 QueryTable[0].EntryContext = &Type;
1847
1848 QueryTable[1].Name = L"ImagePath";
1849 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
1850 QueryTable[1].EntryContext = &ImagePath;
1851
1852 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1853 LoadParams->ServiceName->Buffer, QueryTable, NULL, NULL);
1854
1855 if (!NT_SUCCESS(Status))
1856 {
1857 DPRINT("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
1858 if (ImagePath.Buffer)
1859 ExFreePool(ImagePath.Buffer);
1860 LoadParams->Status = Status;
1861 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1862 return;
1863 }
1864
1865 /*
1866 * Normalize the image path for all later processing.
1867 */
1868
1869 Status = IopNormalizeImagePath(&ImagePath, &ServiceName);
1870
1871 if (!NT_SUCCESS(Status))
1872 {
1873 DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);
1874 LoadParams->Status = Status;
1875 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1876 return;
1877 }
1878
1879 DPRINT("FullImagePath: '%wZ'\n", &ImagePath);
1880 DPRINT("Type: %lx\n", Type);
1881
1882 /* Get existing DriverObject pointer (in case the driver has
1883 already been loaded and initialized) */
1884 Status = IopGetDriverObject(
1885 &DriverObject,
1886 &ServiceName,
1887 (Type == 2 /* SERVICE_FILE_SYSTEM_DRIVER */ ||
1888 Type == 8 /* SERVICE_RECOGNIZER_DRIVER */));
1889
1890 if (!NT_SUCCESS(Status))
1891 {
1892 /*
1893 * Load the driver module
1894 */
1895
1896 Status = MmLoadSystemImage(&ImagePath, NULL, NULL, 0, (PVOID)&ModuleObject, &BaseAddress);
1897
1898 if (!NT_SUCCESS(Status) && Status != STATUS_IMAGE_ALREADY_LOADED)
1899 {
1900 DPRINT("MmLoadSystemImage() failed (Status %lx)\n", Status);
1901 LoadParams->Status = Status;
1902 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1903 return;
1904 }
1905
1906 /*
1907 * Initialize the driver module if it's loaded for the first time
1908 */
1909 if (Status != STATUS_IMAGE_ALREADY_LOADED)
1910 {
1911 Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &ServiceName, &DeviceNode);
1912
1913 if (!NT_SUCCESS(Status))
1914 {
1915 DPRINT("IopCreateDeviceNode() failed (Status %lx)\n", Status);
1916 MmUnloadSystemImage(ModuleObject);
1917 LoadParams->Status = Status;
1918 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1919 return;
1920 }
1921
1922 IopDisplayLoadingMessage(&DeviceNode->ServiceName);
1923
1924 Status = IopInitializeDriverModule(
1925 DeviceNode,
1926 ModuleObject,
1927 &DeviceNode->ServiceName,
1928 (Type == 2 /* SERVICE_FILE_SYSTEM_DRIVER */ ||
1929 Type == 8 /* SERVICE_RECOGNIZER_DRIVER */),
1930 &DriverObject);
1931
1932 if (!NT_SUCCESS(Status))
1933 {
1934 DPRINT("IopInitializeDriver() failed (Status %lx)\n", Status);
1935 MmUnloadSystemImage(ModuleObject);
1936 IopFreeDeviceNode(DeviceNode);
1937 LoadParams->Status = Status;
1938 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1939 return;
1940 }
1941
1942 /* Initialize and start device */
1943 IopInitializeDevice(DeviceNode, DriverObject);
1944 Status = IopStartDevice(DeviceNode);
1945 }
1946 }
1947 else
1948 {
1949 DPRINT("DriverObject already exist in ObjectManager\n");
1950
1951 /* IopGetDriverObject references the DriverObject, so dereference it */
1952 ObDereferenceObject(DriverObject);
1953 }
1954
1955 /* Pass status to the caller and signal the event */
1956 LoadParams->Status = Status;
1957 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1958 }
1959
1960 /*
1961 * NtLoadDriver
1962 *
1963 * Loads a device driver.
1964 *
1965 * Parameters
1966 * DriverServiceName
1967 * Name of the service to load (registry key).
1968 *
1969 * Return Value
1970 * Status
1971 *
1972 * Status
1973 * implemented
1974 */
1975 NTSTATUS NTAPI
1976 NtLoadDriver(IN PUNICODE_STRING DriverServiceName)
1977 {
1978 UNICODE_STRING CapturedDriverServiceName = { 0, 0, NULL };
1979 KPROCESSOR_MODE PreviousMode;
1980 LOAD_UNLOAD_PARAMS LoadParams;
1981 NTSTATUS Status;
1982
1983 PAGED_CODE();
1984
1985 PreviousMode = KeGetPreviousMode();
1986
1987 /*
1988 * Check security privileges
1989 */
1990
1991 /* FIXME: Uncomment when privileges will be correctly implemented. */
1992 #if 0
1993 if (!SeSinglePrivilegeCheck(SeLoadDriverPrivilege, PreviousMode))
1994 {
1995 DPRINT("Privilege not held\n");
1996 return STATUS_PRIVILEGE_NOT_HELD;
1997 }
1998 #endif
1999
2000 Status = ProbeAndCaptureUnicodeString(&CapturedDriverServiceName,
2001 PreviousMode,
2002 DriverServiceName);
2003 if (!NT_SUCCESS(Status))
2004 {
2005 return Status;
2006 }
2007
2008 DPRINT("NtLoadDriver('%wZ')\n", &CapturedDriverServiceName);
2009
2010 LoadParams.ServiceName = &CapturedDriverServiceName;
2011 LoadParams.DriverObject = NULL;
2012 KeInitializeEvent(&LoadParams.Event, NotificationEvent, FALSE);
2013
2014 /* Call the load/unload routine, depending on current process */
2015 if (PsGetCurrentProcess() == PsInitialSystemProcess)
2016 {
2017 /* Just call right away */
2018 IopLoadUnloadDriver(&LoadParams);
2019 }
2020 else
2021 {
2022 /* Load/Unload must be called from system process */
2023 ExInitializeWorkItem(&LoadParams.WorkItem,
2024 (PWORKER_THREAD_ROUTINE)IopLoadUnloadDriver,
2025 (PVOID)&LoadParams);
2026
2027 /* Queue it */
2028 ExQueueWorkItem(&LoadParams.WorkItem, DelayedWorkQueue);
2029
2030 /* And wait when it completes */
2031 KeWaitForSingleObject(&LoadParams.Event, UserRequest, KernelMode,
2032 FALSE, NULL);
2033 }
2034
2035 ReleaseCapturedUnicodeString(&CapturedDriverServiceName,
2036 PreviousMode);
2037
2038 return LoadParams.Status;
2039 }
2040
2041 /*
2042 * NtUnloadDriver
2043 *
2044 * Unloads a legacy device driver.
2045 *
2046 * Parameters
2047 * DriverServiceName
2048 * Name of the service to unload (registry key).
2049 *
2050 * Return Value
2051 * Status
2052 *
2053 * Status
2054 * implemented
2055 */
2056
2057 NTSTATUS NTAPI
2058 NtUnloadDriver(IN PUNICODE_STRING DriverServiceName)
2059 {
2060 return IopUnloadDriver(DriverServiceName, FALSE);
2061 }
2062
2063 /* EOF */