Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1200 lines
33 KiB

  1. /*++
  2. Copyright (c) 1991-2000 Microsoft Corporation
  3. Module Name:
  4. DRIVER.CXX
  5. Abstract:
  6. Functions for Loading, Maintaining, and Unloading Device Drivers.
  7. ScLoadDeviceDriver
  8. ScControlDriver
  9. ScGetDriverStatus
  10. ScGetObjectName
  11. ScUnloadDriver
  12. ScIsPnPDriver
  13. Author:
  14. Dan Lafferty (danl) 27-Apr-1991
  15. Environment:
  16. User Mode -Win32
  17. Notes:
  18. Revision History:
  19. 22-Feb-1999 jschwart
  20. Prevent stopping of PnP drivers via ControlService (since it bluescreens
  21. the machine and is easier to fix here than the REAL fix in NtUnloadDriver)
  22. 02-Oct-1997 AnirudhS
  23. Removed IOCTL to NDIS for NDIS driver arrivals.
  24. 08-Jan-1997 AnirudhS
  25. ScGetDriverStatus, ScGetObjectName, etc: Instead of expecting a
  26. shared lock on the service database and upgrading it to exclusive,
  27. which is broken, just expect an exclusive lock.
  28. 03-Jan-1997 AnirudhS
  29. Temporary fix for QFE bug 66887: When a driver stops, don't free its
  30. object name. Free it only when the driver is reconfigured or deleted.
  31. The changes are marked by "QFE 66887" comments.
  32. 19-Jan-1996 AnirudhS
  33. Add IOCTLs to NDIS for TDI and NDIS driver arrivals.
  34. 30-Oct-1995 AnirudhS
  35. ScLoadDeviceDriver: Turn STATUS_IMAGE_ALREADY_LOADED into
  36. ERROR_SERVICE_ALREADY_RUNNING.
  37. 05-Aug-1993 Danl
  38. ScGetObjectName: It is possible to read an empty-string object
  39. name from the registry. If we do, we need to treat this the same
  40. as if the ObjectName value were not in the registry.
  41. 01-Jun-1993 Danl
  42. GetDriverStatus: When state moves from STOPPED to RUNNING,
  43. then the service record is updated so that STOP is accepted as
  44. a control.
  45. 10-Jul-1992 Danl
  46. Changed RegCloseKey to ScRegCloseKey
  47. 27-Apr-1991 danl
  48. created
  49. --*/
  50. //
  51. // INCLUDES
  52. //
  53. #include "precomp.hxx"
  54. #include <ntddndis.h> // NDIS IOCTL codes
  55. #include <stdlib.h> // wide character c runtimes.
  56. #include <cfgmgr32.h> // PNP manager functions
  57. #include <pnp.h> // PNP manager functions, server side
  58. #include "scconfig.h" // ScReadStartName
  59. #include "driver.h" // ScGetDriverStatus()
  60. #include "depend.h" // ScGetDependentsStopped()
  61. #include "scsec.h" // ScGetPrivilege, ScReleasePrivilege
  62. #include <debugfmt.h> // FORMAT_LPWSTR
  63. //
  64. // DEFINES
  65. //
  66. #define OBJ_DIR_INFO_SIZE 4096L
  67. #define SERVICE_PATH L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
  68. #define FILE_SYSTEM_OBJ_NAME L"\\FileSystem\\"
  69. #define DRIVER_OBJ_NAME L"\\Driver\\"
  70. //
  71. // This is a string version of GUID_DEVCLASS_LEGACYDRIVER in devguid.h
  72. //
  73. #define LEGACYDRIVER_STRING L"{8ECC055D-047F-11D1-A537-0000F8753ED1}"
  74. //
  75. // LOCAL FUNCTION PROTOTYPES
  76. //
  77. DWORD
  78. ScGetObjectName(
  79. LPSERVICE_RECORD ServiceRecord
  80. );
  81. BOOL
  82. ScIsPnPDriver(
  83. IN LPSERVICE_RECORD Service
  84. );
  85. DWORD
  86. ScLoadDeviceDriver(
  87. LPSERVICE_RECORD ServiceRecord
  88. )
  89. /*++
  90. Routine Description:
  91. This function attempts to load a device driver. If the NtLoadDriver
  92. call is successful, we know that the driver is running (since this
  93. is a synchronous operation). If the call fails, the appropriate
  94. windows error code is returned.
  95. NOTE: It is expected that the Database Lock will be held with
  96. exclusive access upon entry to this routine.
  97. WARNING: This routine releases and acquires the Database Lock.
  98. Arguments:
  99. ServiceRecord - This is pointer to a service record for the Device
  100. Driver that is being started.
  101. Return Value:
  102. --*/
  103. {
  104. DWORD status = NO_ERROR;
  105. NTSTATUS ntStatus;
  106. LPWSTR regKeyPath;
  107. UNICODE_STRING regKeyPathString;
  108. ULONG privileges[1];
  109. #if DBG
  110. DWORD dwLoadTime;
  111. #endif
  112. SC_LOG1(TRACE,"In ScLoadDeviceDriver for "FORMAT_LPWSTR" Driver\n",
  113. ServiceRecord->ServiceName);
  114. SC_ASSERT(ScServiceListLock.Have());
  115. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  116. //
  117. // If the ObjectName does not exist yet, create one.
  118. //
  119. if (ServiceRecord->ObjectName == NULL) {
  120. status = ScGetObjectName(ServiceRecord);
  121. if (status != NO_ERROR) {
  122. goto CleanExit;
  123. }
  124. }
  125. //
  126. // Create the Registry Key Path for this driver name.
  127. //
  128. regKeyPath = (LPWSTR)LocalAlloc(
  129. LMEM_FIXED,
  130. sizeof(SERVICE_PATH) +
  131. WCSSIZE(ServiceRecord->ServiceName));
  132. if (regKeyPath == NULL) {
  133. status = ERROR_NOT_ENOUGH_MEMORY;
  134. goto CleanExit;
  135. }
  136. wcscpy(regKeyPath, SERVICE_PATH);
  137. wcscat(regKeyPath, ServiceRecord->ServiceName);
  138. //
  139. // Load the Driver
  140. // (but first get SeLoadDriverPrivilege)
  141. //
  142. RtlInitUnicodeString(&regKeyPathString, regKeyPath);
  143. privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
  144. status = ScGetPrivilege(1,privileges);
  145. if (status != NO_ERROR) {
  146. goto CleanExit;
  147. }
  148. SC_ASSERT(ServiceRecord->ServiceStatus.dwCurrentState == SERVICE_STOPPED);
  149. //
  150. // Release and reacquire the lock around the NtLoadDriver call so that
  151. // a driver can generate a PnP event during its load routine (in NT5,
  152. // the ClusDisk driver can synchronously generate a dismount event while
  153. // it's loading)
  154. //
  155. ScServiceRecordLock.Release();
  156. #if DBG
  157. dwLoadTime = GetTickCount();
  158. #endif
  159. ntStatus = NtLoadDriver(&regKeyPathString);
  160. #if DBG
  161. dwLoadTime = GetTickCount() - dwLoadTime;
  162. if (dwLoadTime > 5000)
  163. {
  164. SC_LOG2(ERROR,
  165. " **** NtLoadDriver(%ws) took %lu ms!\n",
  166. ServiceRecord->ServiceName,
  167. dwLoadTime);
  168. }
  169. #endif
  170. ScServiceRecordLock.GetExclusive();
  171. ScReleasePrivilege();
  172. LocalFree(regKeyPath);
  173. if (NT_SUCCESS(ntStatus)) {
  174. SC_LOG1(TRACE,"ScLoadDeviceDriver: NtLoadDriver Success for "
  175. FORMAT_LPWSTR " \n",ServiceRecord->ServiceName);
  176. }
  177. else if (ntStatus == STATUS_IMAGE_ALREADY_LOADED) {
  178. SC_LOG1(TRACE,"ScLoadDeviceDriver: Driver " FORMAT_LPWSTR
  179. " is already running\n",ServiceRecord->ServiceName);
  180. status = ERROR_SERVICE_ALREADY_RUNNING;
  181. }
  182. else {
  183. SC_LOG2(WARNING,"ScLoadDeviceDriver: NtLoadDriver(%ws) Failed 0x%lx\n",
  184. ServiceRecord->ServiceName,
  185. ntStatus);
  186. if (ntStatus == STATUS_NO_SUCH_DEVICE) {
  187. status = ERROR_BAD_UNIT;
  188. }
  189. else {
  190. status = RtlNtStatusToDosError(ntStatus);
  191. }
  192. goto CleanExit;
  193. }
  194. //
  195. // Update the Service Record with this driver's start information.
  196. //
  197. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_RUNNING;
  198. ServiceRecord->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  199. ServiceRecord->ServiceStatus.dwWin32ExitCode = NO_ERROR;
  200. ServiceRecord->ServiceStatus.dwServiceSpecificExitCode = 0;
  201. ServiceRecord->ServiceStatus.dwCheckPoint = 0;
  202. ServiceRecord->ServiceStatus.dwWaitHint = 0;
  203. ServiceRecord->UseCount++;
  204. SC_LOG2(USECOUNT, "ScLoadDeviceDriver: " FORMAT_LPWSTR
  205. " increment USECOUNT=%lu\n", ServiceRecord->ServiceName, ServiceRecord->UseCount);
  206. //
  207. // Tell NDIS to issue the PNP notifications about this driver's arrival,
  208. // if necessary
  209. //
  210. ScNotifyNdis(ServiceRecord);
  211. CleanExit:
  212. return(status);
  213. }
  214. VOID
  215. ScNotifyNdis(
  216. LPSERVICE_RECORD ServiceRecord
  217. )
  218. /*++
  219. Routine Description:
  220. This function issues Plug-and-Play notifications to NDIS about the
  221. arrival of certain types of drivers and services.
  222. Arguments:
  223. ServiceRecord - Service or driver that just started successfully.
  224. Return Value:
  225. None. Errors are written to the event log.
  226. --*/
  227. {
  228. HANDLE hDevice;
  229. BOOL fResult;
  230. DWORD cb;
  231. DWORD IoControlCode;
  232. //
  233. // TDI GROUP SPECIAL: Drivers in group TDI are assumed to be PNP-unaware
  234. // network transport drivers. Win32 services in group TDI are assumed to
  235. // be in that group because they start PNP-unaware transport drivers.
  236. // (PNP-aware transports are in group PNP_TDI.)
  237. // Such a transport doesn't notify the higher-level network components
  238. // (TDI clients) that it has arrived. So we issue an IOCTL to NDIS to ask
  239. // it to read the transport's bindings from the registry and notify the
  240. // clients on this transport's behalf.
  241. //
  242. if (ServiceRecord->MemberOfGroup == ScGlobalTDIGroup)
  243. {
  244. IoControlCode = IOCTL_NDIS_ADD_TDI_DEVICE;
  245. }
  246. else
  247. {
  248. return;
  249. }
  250. hDevice = CreateFile(
  251. L"\\\\.\\NDIS",
  252. GENERIC_READ | GENERIC_WRITE,
  253. 0, // sharing mode - not significant
  254. NULL, // security attributes
  255. OPEN_EXISTING,
  256. 0, // file attributes and flags
  257. NULL // handle to template file
  258. );
  259. if (hDevice == INVALID_HANDLE_VALUE)
  260. {
  261. SC_LOG(WARNING, "Couldn't open handle to NDIS for IOCTL, error %lu\n",
  262. GetLastError());
  263. return;
  264. }
  265. fResult = DeviceIoControl(
  266. hDevice,
  267. IoControlCode,
  268. ServiceRecord->ServiceName, // input buffer
  269. (DWORD) WCSSIZE(ServiceRecord->ServiceName), // input buffer size
  270. NULL, // output buffer
  271. 0, // output buffer size
  272. &cb, // bytes returned
  273. NULL); // OVERLAPPED structure
  274. CloseHandle(hDevice);
  275. if (!fResult)
  276. {
  277. SC_LOG3(WARNING, "IOCTL %#lx to NDIS for %ws failed, error %lu\n",
  278. IoControlCode, ServiceRecord->ServiceName, GetLastError());
  279. }
  280. }
  281. DWORD
  282. ScControlDriver(
  283. DWORD ControlCode,
  284. LPSERVICE_RECORD ServiceRecord,
  285. LPSERVICE_STATUS lpServiceStatus
  286. )
  287. /*++
  288. Routine Description:
  289. This function checks controls that are passed to device drivers. Only
  290. two controls are accepted.
  291. stop - This function attemps to unload the driver. The driver
  292. state is set to STOP_PENDING since unload is an
  293. asynchronous operation. We have to wait until another
  294. call is made that will return the status of this driver
  295. before we can query the driver object to see if it is
  296. still there.
  297. interrogate - This function attempts to query the driver object
  298. to see if it is still there.
  299. WARNING: This function should only be called with a pointer to
  300. a ServiceRecord that belongs to a DRIVER.
  301. Arguments:
  302. ControlCode - This is the control request that is being sent to
  303. control the driver.
  304. ServiceRecord - This is a pointer to the service record for the
  305. driver that is to be controlled.
  306. lpServiceStatus - This is a pointer to a buffer that upon exit will
  307. contain the latest service status.
  308. Return Value:
  309. --*/
  310. {
  311. DWORD status;
  312. SC_LOG1(TRACE,"In ScControlDriver for "FORMAT_LPWSTR" Driver\n",
  313. ServiceRecord->ServiceName);
  314. CServiceRecordExclusiveLock Lock;
  315. switch(ControlCode) {
  316. case SERVICE_CONTROL_INTERROGATE:
  317. //
  318. // On interrogate, we need to see if the service is still there.
  319. // Then we update the status accordingly.
  320. //
  321. status = ScGetDriverStatus(ServiceRecord, lpServiceStatus);
  322. if (status == NO_ERROR) {
  323. //
  324. // Based on the state, return the appropriate error code
  325. // so driver return codes match with those of services
  326. //
  327. switch(lpServiceStatus->dwCurrentState) {
  328. case SERVICE_STOPPED:
  329. status = ERROR_SERVICE_NOT_ACTIVE;
  330. break;
  331. case SERVICE_STOP_PENDING:
  332. status = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
  333. break;
  334. }
  335. }
  336. break;
  337. case SERVICE_CONTROL_STOP:
  338. //
  339. // This operation is invalid on PnP drivers
  340. //
  341. if (ScIsPnPDriver(ServiceRecord)) {
  342. status = ERROR_INVALID_SERVICE_CONTROL;
  343. break;
  344. }
  345. //
  346. // Find out if the driver is still running.
  347. //
  348. status = ScGetDriverStatus(ServiceRecord, lpServiceStatus);
  349. if (status != NO_ERROR) {
  350. break;
  351. }
  352. if (ServiceRecord->ServiceStatus.dwCurrentState != SERVICE_RUNNING) {
  353. //
  354. // If the driver is not running, then it cannot accept the
  355. // STOP control request. Drivers do not accept STOP requests
  356. // when in the START_PENDING state. Make these return codes
  357. // match with those of services based on the driver's state
  358. //
  359. switch(lpServiceStatus->dwCurrentState) {
  360. case SERVICE_STOPPED:
  361. status = ERROR_SERVICE_NOT_ACTIVE;
  362. break;
  363. case SERVICE_STOP_PENDING:
  364. status = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
  365. break;
  366. default:
  367. status = ERROR_INVALID_SERVICE_CONTROL;
  368. break;
  369. }
  370. goto CleanExit;
  371. }
  372. //
  373. // Check for dependent services still running
  374. //
  375. if (! ScDependentsStopped(ServiceRecord)) {
  376. status = ERROR_DEPENDENT_SERVICES_RUNNING;
  377. goto CleanExit;
  378. }
  379. status = ScUnloadDriver(ServiceRecord);
  380. if (status == ERROR_INVALID_SERVICE_CONTROL) {
  381. //
  382. // If the driver fails to unload with this error,
  383. // then it must be one that cannot be stopped.
  384. // We want to mark it as such, and return an error.
  385. //
  386. SC_LOG0(TRACE,"ScControlDriver: Marking driver as non-stoppable\n");
  387. ServiceRecord->ServiceStatus.dwControlsAccepted = 0L;
  388. goto CleanExit;
  389. }
  390. //
  391. // Set the Current State to STOP_PENDING, and get the
  392. // current status (again);
  393. //
  394. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  395. status = ScGetDriverStatus(ServiceRecord, lpServiceStatus);
  396. break;
  397. default:
  398. status = ERROR_INVALID_SERVICE_CONTROL;
  399. }
  400. CleanExit:
  401. return(status);
  402. }
  403. DWORD
  404. ScGetDriverStatus(
  405. IN OUT LPSERVICE_RECORD ServiceRecord,
  406. OUT LPSERVICE_STATUS lpServiceStatus OPTIONAL
  407. )
  408. /*++
  409. Routine Description:
  410. This function determines the correct current status for a device driver.
  411. The updated status is only returned if NO_ERROR is returned.
  412. WARNING: This function expects the EXCLUSIVE database lock to be held.
  413. CODEWORK: For greater parallelism while enumerating driver status, get
  414. the exclusive lock only if the driver status has changed.
  415. NOTE: The ServiceRecord passed in MUST be for a DeviceDriver.
  416. Arguments:
  417. ServiceRecord - This is a pointer to the Service Record for the
  418. Device Driver for which the status is desired.
  419. lpServiceStatus - This is a pointer to a buffer that upon exit will
  420. contain the latest service status.
  421. Return Value:
  422. NO_ERROR - The operation completed successfully.
  423. ERROR_NOT_ENOUGH_MEMORY - If the local alloc failed.
  424. Or any unexpected errors from the NtOpenDirectoryObject or
  425. NtQueryDirectoryObject.
  426. --*/
  427. {
  428. NTSTATUS ntStatus;
  429. DWORD status;
  430. HANDLE DirectoryHandle;
  431. OBJECT_ATTRIBUTES Obja;
  432. ULONG Context;
  433. ULONG ReturnLength;
  434. BOOLEAN restartScan;
  435. UNICODE_STRING ObjectPathString;
  436. UNICODE_STRING ObjectNameString;
  437. LPWSTR pObjectPath;
  438. LPWSTR pDeviceName;
  439. BOOL found = FALSE;
  440. SC_LOG1(TRACE,"In ScGetDriverStatus for "FORMAT_LPWSTR" Driver\n",
  441. ServiceRecord->ServiceName);
  442. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  443. SC_ASSERT(ServiceRecord->ServiceStatus.dwServiceType & SERVICE_DRIVER);
  444. //
  445. // If the ObjectName does not exist yet, create one.
  446. //
  447. if (ServiceRecord->ObjectName == NULL) {
  448. status = ScGetObjectName(ServiceRecord);
  449. if (status != NO_ERROR) {
  450. return(status);
  451. }
  452. }
  453. //
  454. // Take the ObjectPathName apart such that the path is in one
  455. // string, and the device name is in another string.
  456. //
  457. // First copy the Object Path string into a new buffer.
  458. //
  459. pObjectPath = (LPWSTR)LocalAlloc(
  460. LMEM_FIXED,
  461. WCSSIZE(ServiceRecord->ObjectName));
  462. if(pObjectPath == NULL) {
  463. return(ERROR_NOT_ENOUGH_MEMORY);
  464. }
  465. wcscpy(pObjectPath, ServiceRecord->ObjectName);
  466. //
  467. // Find the last occurrence of '\'. The Device name follows that.
  468. // replace the '\' with a NULL terminator. Now we have two strings.
  469. //
  470. pDeviceName = wcsrchr(pObjectPath, L'\\');
  471. if (pDeviceName == NULL) {
  472. SC_LOG0(ERROR,"ScGetDriverStatus: DeviceName not in object path name\n");
  473. LocalFree(pObjectPath);
  474. return(ERROR_PATH_NOT_FOUND);
  475. }
  476. *pDeviceName = L'\0';
  477. pDeviceName++;
  478. //
  479. // Open the directory object by name
  480. //
  481. RtlInitUnicodeString(&ObjectPathString,pObjectPath);
  482. InitializeObjectAttributes(&Obja,&ObjectPathString,0,NULL,NULL);
  483. ntStatus = NtOpenDirectoryObject (
  484. &DirectoryHandle,
  485. DIRECTORY_TRAVERSE | DIRECTORY_QUERY,
  486. &Obja);
  487. if (!NT_SUCCESS(ntStatus)) {
  488. LocalFree(pObjectPath);
  489. if (ntStatus == STATUS_OBJECT_PATH_NOT_FOUND) {
  490. //
  491. // If a driver uses a non-standard object path, the path may
  492. // not exist if the driver is not running. We want to treat
  493. // this as if the driver is not running.
  494. //
  495. goto CleanExit;
  496. }
  497. SC_LOG1(ERROR,"ScGetDriverStatus: NtOpenDirectoryObject failed 0x%lx\n",
  498. ntStatus);
  499. return(RtlNtStatusToDosError(ntStatus));
  500. }
  501. RtlInitUnicodeString(&ObjectNameString,pDeviceName);
  502. restartScan = TRUE;
  503. do {
  504. BYTE Buffer[OBJ_DIR_INFO_SIZE];
  505. POBJECT_DIRECTORY_INFORMATION pObjInfo;
  506. //
  507. // Query the Directory Object to enumerate all object names
  508. // in that object directory.
  509. //
  510. ntStatus = NtQueryDirectoryObject (
  511. DirectoryHandle,
  512. Buffer,
  513. OBJ_DIR_INFO_SIZE,
  514. FALSE,
  515. restartScan,
  516. &Context,
  517. &ReturnLength);
  518. if (!NT_SUCCESS(ntStatus)) {
  519. SC_LOG1(ERROR,"ScGetDriverStatus:NtQueryDirectoryObject Failed 0x%lx\n",
  520. ntStatus);
  521. LocalFree(pObjectPath);
  522. NtClose(DirectoryHandle);
  523. return(RtlNtStatusToDosError(ntStatus));
  524. }
  525. //
  526. // Now check to see if the device name that we are interested in is
  527. // in the enumerated data.
  528. //
  529. for (pObjInfo = (POBJECT_DIRECTORY_INFORMATION) Buffer;
  530. pObjInfo->Name.Length != 0;
  531. pObjInfo++) {
  532. if (RtlCompareUnicodeString( &(pObjInfo->Name), &ObjectNameString, TRUE) == 0) {
  533. found = TRUE;
  534. break;
  535. }
  536. }
  537. restartScan = FALSE;
  538. } while ((ntStatus == STATUS_MORE_ENTRIES) && (found == FALSE));
  539. NtClose(DirectoryHandle);
  540. LocalFree(pObjectPath);
  541. CleanExit:
  542. if (found) {
  543. DWORD PreviousState;
  544. PreviousState = ServiceRecord->ServiceStatus.dwCurrentState;
  545. if (PreviousState != SERVICE_STOP_PENDING) {
  546. //
  547. // The driver IS running.
  548. //
  549. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_RUNNING;
  550. if (PreviousState == SERVICE_STOPPED) {
  551. //
  552. // It used to be stopped but now it is running.
  553. //
  554. ServiceRecord->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  555. ServiceRecord->ServiceStatus.dwWin32ExitCode = NO_ERROR;
  556. ServiceRecord->ServiceStatus.dwServiceSpecificExitCode = 0;
  557. ServiceRecord->ServiceStatus.dwCheckPoint = 0;
  558. ServiceRecord->ServiceStatus.dwWaitHint = 0;
  559. ServiceRecord->UseCount++;
  560. SC_LOG2(USECOUNT, "ScGetDriverStatus: " FORMAT_LPWSTR
  561. " increment USECOUNT=%lu\n", ServiceRecord->ServiceName, ServiceRecord->UseCount);
  562. }
  563. if (ServiceRecord->ServiceStatus.dwWin32ExitCode ==
  564. ERROR_SERVICE_NEVER_STARTED) {
  565. ServiceRecord->ServiceStatus.dwWin32ExitCode = NO_ERROR;
  566. }
  567. SC_LOG1(TRACE,"ScGetDriverStatus: "FORMAT_LPWSTR" Driver is "
  568. "RUNNING\n", ServiceRecord->ServiceName);
  569. }
  570. }
  571. else {
  572. //
  573. // The driver is NOT running.
  574. //
  575. SC_LOG1(TRACE,"ScGetDriverStatus: "FORMAT_LPWSTR" Driver is "
  576. "NOT RUNNING\n", ServiceRecord->ServiceName);
  577. switch(ServiceRecord->ServiceStatus.dwCurrentState) {
  578. case SERVICE_STOP_PENDING:
  579. //
  580. // If the old state was STOP_PENDING, then we can consider
  581. // it stopped.
  582. //
  583. LocalFree(ServiceRecord->ObjectName);
  584. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  585. ServiceRecord->ServiceStatus.dwControlsAccepted = 0;
  586. ServiceRecord->ServiceStatus.dwCheckPoint = 0;
  587. ServiceRecord->ServiceStatus.dwWaitHint = 0;
  588. ServiceRecord->ServiceStatus.dwWin32ExitCode = NO_ERROR;
  589. ServiceRecord->ObjectName = NULL;
  590. //
  591. // Since the service is no longer running, we need to decrement
  592. // the use count. If the count is decremented to zero, and
  593. // the service is marked for deletion, it will get deleted.
  594. //
  595. ScDecrementUseCountAndDelete(ServiceRecord);
  596. break;
  597. case SERVICE_STOPPED:
  598. //
  599. // We are not likely to query this driver's status again soon,
  600. // so free its object name.
  601. //
  602. LocalFree(ServiceRecord->ObjectName);
  603. ServiceRecord->ObjectName = NULL;
  604. break;
  605. default:
  606. //
  607. // The driver stopped without being requested to do so.
  608. //
  609. LocalFree(ServiceRecord->ObjectName);
  610. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  611. ServiceRecord->ServiceStatus.dwControlsAccepted = 0;
  612. ServiceRecord->ServiceStatus.dwCheckPoint = 0;
  613. ServiceRecord->ServiceStatus.dwWaitHint = 0;
  614. ServiceRecord->ServiceStatus.dwWin32ExitCode = ERROR_GEN_FAILURE;
  615. ServiceRecord->ObjectName = NULL;
  616. //
  617. // Since the service is no longer running, we need to decrement
  618. // the use count. If the count is decremented to zero, and
  619. // the service is marked for deletion, it will get deleted.
  620. //
  621. ScDecrementUseCountAndDelete(ServiceRecord);
  622. break;
  623. }
  624. }
  625. if (ARGUMENT_PRESENT(lpServiceStatus)) {
  626. RtlCopyMemory(
  627. lpServiceStatus,
  628. &(ServiceRecord->ServiceStatus),
  629. sizeof(SERVICE_STATUS));
  630. }
  631. return(NO_ERROR);
  632. }
  633. DWORD
  634. ScGetObjectName(
  635. LPSERVICE_RECORD ServiceRecord
  636. )
  637. /*++
  638. Routine Description:
  639. This function gets a directory object path name for a driver by looking
  640. it up in the registry, or, if it isn't specified in the registry, by
  641. computing it from the driver name and type. It allocates storage
  642. for this name, and passes back the pointer to it. The Pointer to
  643. the object name string is stored in the ServiceRecord->ObjectName
  644. location.
  645. WARNING: This function expects the EXCLUSIVE database lock to be held.
  646. Arguments:
  647. ServiceRecord - This is a pointer to the ServiceRecord for the Driver.
  648. Return Value:
  649. NO_ERROR - The operation was successful.
  650. ERROR_NOT_ENOUGH_MEMORY - If there wasn't enough memory available for
  651. the ObjectName.
  652. or any error from ScOpenServiceConfigKey.
  653. --*/
  654. {
  655. DWORD status;
  656. DWORD bufferSize;
  657. LPWSTR objectNamePath;
  658. HKEY serviceKey;
  659. LPWSTR pObjectName;
  660. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  661. if (ServiceRecord->ObjectName != NULL) {
  662. //
  663. // Some other thread beat us to it
  664. //
  665. return(NO_ERROR);
  666. }
  667. //
  668. // Open the Registry Key for this driver name.
  669. //
  670. status = ScOpenServiceConfigKey(
  671. ServiceRecord->ServiceName,
  672. KEY_READ,
  673. FALSE,
  674. &serviceKey);
  675. if (status != NO_ERROR) {
  676. SC_LOG1(ERROR,"ScGetObjectName: ScOpenServiceConfigKey Failed %d\n",
  677. status);
  678. return(status);
  679. }
  680. //
  681. // Get the NT Object Name from the registry.
  682. //
  683. status = ScReadStartName(
  684. serviceKey,
  685. &pObjectName);
  686. ScRegCloseKey(serviceKey);
  687. //
  688. // Make sure we read a value with length greater than 0
  689. //
  690. if (status == NO_ERROR && pObjectName != NULL)
  691. {
  692. if (*pObjectName != '\0') {
  693. ServiceRecord->ObjectName = pObjectName;
  694. return(NO_ERROR);
  695. }
  696. else {
  697. LocalFree(pObjectName);
  698. }
  699. }
  700. //
  701. // There must not be a name in the ObjectName value field.
  702. // In this case, we must build the name from the type info and
  703. // the ServiceName. Names will take the following form:
  704. // "\\FileSystem\\Rdr" example of a file system driver
  705. // "\\Driver\\Parallel" example of a kernel driver
  706. //
  707. //
  708. SC_LOG1(TRACE,"ScGetObjectName: ScReadStartName Failed(%d). Build the"
  709. "name instead\n",status);
  710. if (ServiceRecord->ServiceStatus.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER) {
  711. bufferSize = sizeof(FILE_SYSTEM_OBJ_NAME);
  712. objectNamePath = FILE_SYSTEM_OBJ_NAME;
  713. }
  714. else {
  715. bufferSize = sizeof(DRIVER_OBJ_NAME);
  716. objectNamePath = DRIVER_OBJ_NAME;
  717. }
  718. bufferSize += (DWORD) WCSSIZE(ServiceRecord->ServiceName);
  719. pObjectName = (LPWSTR)LocalAlloc(LMEM_FIXED, (UINT) bufferSize);
  720. if (pObjectName == NULL) {
  721. SC_LOG0(ERROR,"ScGetObjectName: LocalAlloc Failed\n");
  722. return(ERROR_NOT_ENOUGH_MEMORY);
  723. }
  724. wcscpy(pObjectName, objectNamePath);
  725. wcscat(pObjectName, ServiceRecord->ServiceName);
  726. ServiceRecord->ObjectName = pObjectName;
  727. return(NO_ERROR);
  728. }
  729. DWORD
  730. ScUnloadDriver(
  731. LPSERVICE_RECORD ServiceRecord
  732. )
  733. /*++
  734. Routine Description:
  735. This function attempts to unload the driver whose service record
  736. is passed in.
  737. NOTE: Make sure the ServiceRecord is for a driver and not a service
  738. before calling this routine.
  739. Arguments:
  740. ServiceRecord - This is a pointer to the service record for a driver.
  741. This routine assumes that the service record is for a driver and
  742. not a service.
  743. Return Value:
  744. NO_ERROR - if successful.
  745. ERROR_INVALID_SERVICE_CONTROL - This is returned if the driver is
  746. not unloadable.
  747. otherwise, an error code is returned.
  748. Note:
  749. --*/
  750. {
  751. NTSTATUS ntStatus = STATUS_SUCCESS;
  752. DWORD status;
  753. LPWSTR regKeyPath;
  754. UNICODE_STRING regKeyPathString;
  755. ULONG privileges[1];
  756. //
  757. // Create the Registry Key Path for this driver name.
  758. //
  759. regKeyPath = (LPWSTR)LocalAlloc(
  760. LMEM_FIXED,
  761. sizeof(SERVICE_PATH) +
  762. WCSSIZE(ServiceRecord->ServiceName));
  763. if (regKeyPath == NULL) {
  764. status = ERROR_NOT_ENOUGH_MEMORY;
  765. return(status);
  766. }
  767. wcscpy(regKeyPath, SERVICE_PATH);
  768. wcscat(regKeyPath, ServiceRecord->ServiceName);
  769. //
  770. // Unload the Driver
  771. // (but first get SeLoadDriverPrivilege)
  772. //
  773. RtlInitUnicodeString(&regKeyPathString, regKeyPath);
  774. privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
  775. status = ScGetPrivilege(1,privileges);
  776. if (status != NO_ERROR) {
  777. LocalFree(regKeyPath);
  778. return(status);
  779. }
  780. ntStatus = NtUnloadDriver (&regKeyPathString);
  781. (VOID)ScReleasePrivilege();
  782. LocalFree(regKeyPath);
  783. if (!NT_SUCCESS(ntStatus)) {
  784. if (ntStatus == STATUS_INVALID_DEVICE_REQUEST) {
  785. status = ERROR_INVALID_SERVICE_CONTROL;
  786. return(status);
  787. }
  788. SC_LOG1(ERROR,"ScControlDriver: NtUnloadDriver Failed 0x%lx\n",ntStatus);
  789. status = RtlNtStatusToDosError(ntStatus);
  790. return(status);
  791. }
  792. SC_LOG1(TRACE,"ScLoadDeviceDriver: NtUnloadDriver Success for "
  793. ""FORMAT_LPWSTR "\n",ServiceRecord->ServiceName);
  794. return(NO_ERROR);
  795. }
  796. BOOL
  797. ScIsPnPDriver(
  798. IN LPSERVICE_RECORD Service
  799. )
  800. /*++
  801. Routine Description:
  802. This function checks whether a specified driver is a PnP driver
  803. Arguments:
  804. Service - Specifies the driver of interest.
  805. Return Value:
  806. TRUE - if the driver is a PnP driver or if this cannot be determined.
  807. FALSE - if the service is not a PnP driver.
  808. --*/
  809. {
  810. CONFIGRET Status;
  811. BOOL fRetStatus = TRUE;
  812. WCHAR * pBuffer;
  813. ULONG cchLen, cchTransferLen, ulRegDataType;
  814. WCHAR szClassGuid[MAX_GUID_STRING_LEN];
  815. //
  816. // Allocate a buffer for the list of device instances associated with
  817. // this service
  818. //
  819. Status = PNP_GetDeviceListSize(
  820. NULL, // hBinding
  821. Service->ServiceName, // pszFilter
  822. &cchLen, // list length in wchars
  823. CM_GETIDLIST_FILTER_SERVICE); // filter is a service name
  824. if (Status != CR_SUCCESS)
  825. {
  826. SC_LOG2(WARNING, "PNP_GetDeviceListSize failed %#lx for service %ws\n",
  827. Status, Service->ServiceName);
  828. return TRUE;
  829. }
  830. pBuffer = (WCHAR *) LocalAlloc(0, cchLen * sizeof(WCHAR));
  831. if (pBuffer == NULL)
  832. {
  833. SC_LOG(ERROR, "Couldn't allocate buffer for device list, error %lu\n",
  834. GetLastError());
  835. return TRUE;
  836. }
  837. //
  838. // Initialize parameters for PNP_GetDeviceList, the same way as is
  839. // normally done in the client side of the API
  840. //
  841. pBuffer[0] = L'\0';
  842. //
  843. // Get the list of device instances that are associated with this service
  844. //
  845. // (For legacy and PNP-aware services, we could get an empty device list.)
  846. //
  847. Status = PNP_GetDeviceList(
  848. NULL, // binding handle
  849. Service->ServiceName, // pszFilter
  850. pBuffer, // buffer for device list
  851. &cchLen, // buffer length in wchars
  852. CM_GETIDLIST_FILTER_SERVICE | // filter is a service name
  853. CM_GETIDLIST_DONOTGENERATE // do not generate an instance if none exists
  854. );
  855. if (Status != CR_SUCCESS)
  856. {
  857. SC_LOG2(ERROR, "PNP_GetDeviceList failed %#lx for service %ws\n",
  858. Status, Service->ServiceName);
  859. LocalFree(pBuffer);
  860. return TRUE;
  861. }
  862. //
  863. // If there are no devnodes, this is not a PnP driver
  864. //
  865. if (pBuffer[0] == L'\0' || cchLen == 0)
  866. {
  867. SC_LOG1(TRACE, "ScIsPnPDriver: %ws is not a PnP driver (no devnodes)\n",
  868. Service->ServiceName);
  869. LocalFree(pBuffer);
  870. return FALSE;
  871. }
  872. //
  873. // If there's more than one devnode, this is a PnP driver
  874. //
  875. if (*(pBuffer + wcslen(pBuffer) + 1) != L'\0')
  876. {
  877. SC_LOG1(TRACE, "ScIsPnPDriver: %ws is a PnP driver (more than 1 devnode)\n",
  878. Service->ServiceName);
  879. LocalFree(pBuffer);
  880. return TRUE;
  881. }
  882. //
  883. // Get the class GUID of this driver
  884. //
  885. cchLen = cchTransferLen = sizeof(szClassGuid);
  886. Status = PNP_GetDeviceRegProp(
  887. NULL, // binding handle
  888. pBuffer, // device instance
  889. CM_DRP_CLASSGUID, // property to get
  890. &ulRegDataType, // pointer to REG_* type
  891. (LPBYTE) szClassGuid, // buffer for property
  892. &cchTransferLen, // transfer length
  893. &cchLen, // buffer length in bytes
  894. 0 // flags
  895. );
  896. if (Status != CR_SUCCESS)
  897. {
  898. SC_ASSERT(Status != CR_BUFFER_SMALL);
  899. SC_LOG2(ERROR, "PNP_GetDeviceRegProp failed %#lx for service %ws\n",
  900. Status, Service->ServiceName);
  901. LocalFree(pBuffer);
  902. return TRUE;
  903. }
  904. //
  905. // If the single devnode's class is LegacyDriver,
  906. // this is not a PnP driver
  907. //
  908. fRetStatus = (_wcsicmp(szClassGuid, LEGACYDRIVER_STRING) != 0);
  909. SC_LOG2(TRACE, "ScIsPnPDriver: %ws %ws a PnP driver\n",
  910. Service->ServiceName, fRetStatus ? L"is" : L"is not");
  911. LocalFree(pBuffer);
  912. return fRetStatus;
  913. }