Leaked source code of windows server 2003
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.

1359 lines
40 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. pnprlist.c
  5. Abstract:
  6. This module contains routines to manipulate relations list. Relation lists
  7. are used by Plug and Play during the processing of device removal and
  8. ejection.
  9. These routines are all pageable and can't be called at raised IRQL or with
  10. a spinlock held.
  11. Author:
  12. Robert Nelson (robertn) Apr, 1998.
  13. Revision History:
  14. --*/
  15. #include "pnpmgrp.h"
  16. #pragma hdrstop
  17. #ifdef POOL_TAGGING
  18. #undef ExAllocatePool
  19. #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'lrpP')
  20. #endif
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE, IopAddRelationToList)
  23. #pragma alloc_text(PAGE, IopAllocateRelationList)
  24. #pragma alloc_text(PAGE, IopCompressRelationList)
  25. #pragma alloc_text(PAGE, IopEnumerateRelations)
  26. #pragma alloc_text(PAGE, IopFreeRelationList)
  27. #pragma alloc_text(PAGE, IopGetRelationsCount)
  28. #pragma alloc_text(PAGE, IopGetRelationsTaggedCount)
  29. #pragma alloc_text(PAGE, IopIsRelationInList)
  30. #pragma alloc_text(PAGE, IopMergeRelationLists)
  31. #pragma alloc_text(PAGE, IopRemoveIndirectRelationsFromList)
  32. #pragma alloc_text(PAGE, IopRemoveRelationFromList)
  33. #pragma alloc_text(PAGE, IopSetAllRelationsTags)
  34. #pragma alloc_text(PAGE, IopSetRelationsTag)
  35. #endif
  36. #define RELATION_FLAGS 0x00000003
  37. #define RELATION_FLAG_TAGGED 0x00000001
  38. #define RELATION_FLAG_DESCENDANT 0x00000002
  39. NTSTATUS
  40. IopAddRelationToList(
  41. IN PRELATION_LIST List,
  42. IN PDEVICE_OBJECT DeviceObject,
  43. IN BOOLEAN DirectDescendant,
  44. IN BOOLEAN Tagged
  45. )
  46. /*++
  47. Routine Description:
  48. Adds an element to a relation list.
  49. If this is the first DeviceObject of a particular level then a new
  50. RELATION_LIST_ENTRY will be allocated.
  51. This routine should only be called on an uncompressed relation list,
  52. otherwise it is likely that STATUS_INVALID_PARAMETER will be returned.
  53. Arguments:
  54. List Relation list to which the DeviceObject is added.
  55. DeviceObject DeviceObject to be added to List. It must be a
  56. PhysicalDeviceObject (PDO).
  57. DirectDescendant Indicates whether DeviceObject is a direct descendant of
  58. the original target device of this remove.
  59. Tagged Indicates whether DeviceObject should be tagged in List.
  60. Return Value:
  61. STATUS_SUCCESS
  62. The DeviceObject was added successfully.
  63. STATUS_OBJECT_NAME_COLLISION
  64. The DeviceObject already exists in the relation list.
  65. STATUS_INSUFFICIENT_RESOURCES
  66. There isn't enough PagedPool available to allocate a new
  67. RELATION_LIST_ENTRY.
  68. STATUS_INVALID_PARAMETER
  69. The level of the DEVICE_NODE associated with DeviceObject is less than
  70. FirstLevel or greater than the MaxLevel.
  71. STATUS_NO_SUCH_DEVICE
  72. DeviceObject is not a PhysicalDeviceObject (PDO), it doesn't have a
  73. DEVICE_NODE associated with it.
  74. --*/
  75. {
  76. PDEVICE_NODE deviceNode;
  77. PRELATION_LIST_ENTRY entry;
  78. ULONG level;
  79. ULONG index;
  80. ULONG flags;
  81. PAGED_CODE();
  82. flags = 0;
  83. if (Tagged) {
  84. Tagged = 1;
  85. flags |= RELATION_FLAG_TAGGED;
  86. }
  87. if (DirectDescendant) {
  88. flags |= RELATION_FLAG_DESCENDANT;
  89. }
  90. if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
  91. level = deviceNode->Level;
  92. //
  93. // Since this routine is called with the DeviceNode Tree locked and
  94. // List is initially allocated with enough entries to hold the deepest
  95. // DEVICE_NODE this ASSERT should never fire. If it does then either
  96. // the tree is changing or we were given a compressed list.
  97. //
  98. ASSERT(List->FirstLevel <= level && level <= List->MaxLevel);
  99. if (List->FirstLevel <= level && level <= List->MaxLevel) {
  100. if ((entry = List->Entries[ level - List->FirstLevel ]) == NULL) {
  101. //
  102. // This is the first DeviceObject of its level, allocate a new
  103. // RELATION_LIST_ENTRY.
  104. //
  105. entry = ExAllocatePool( PagedPool,
  106. sizeof(RELATION_LIST_ENTRY) +
  107. IopNumberDeviceNodes * sizeof(PDEVICE_OBJECT));
  108. if (entry == NULL) {
  109. return STATUS_INSUFFICIENT_RESOURCES;
  110. }
  111. //
  112. // We always allocate enough Devices to hold the whole tree as
  113. // a simplification. Since each entry is a PDEVICE_OBJECT and
  114. // there is generally under 50 devices on a machine this means
  115. // under 1K for each entry. The excess space will be freed when
  116. // the list is compressed.
  117. //
  118. entry->Count = 0;
  119. entry->MaxCount = IopNumberDeviceNodes;
  120. List->Entries[ level - List->FirstLevel ] = entry;
  121. }
  122. //
  123. // There should always be room for a DeviceObject since the Entry is
  124. // initially dimensioned large enough to hold all the DEVICE_NODES
  125. // in the system.
  126. //
  127. ASSERT(entry->Count < entry->MaxCount);
  128. if (entry->Count < entry->MaxCount) {
  129. //
  130. // Search the list to see if DeviceObject has already been
  131. // added.
  132. //
  133. for (index = 0; index < entry->Count; index++) {
  134. if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
  135. //
  136. // DeviceObject already exists in the list. However
  137. // the Direct Descendant flag may differ. We will
  138. // override it if DirectDescendant is TRUE. This could
  139. // happen if we merged two relation lists.
  140. if (DirectDescendant) {
  141. entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] | RELATION_FLAG_DESCENDANT);
  142. }
  143. return STATUS_OBJECT_NAME_COLLISION;
  144. }
  145. }
  146. } else {
  147. //
  148. // There isn't room in the Entry for another DEVICE_OBJECT, the
  149. // list has probably already been compressed.
  150. //
  151. return STATUS_INVALID_PARAMETER;
  152. }
  153. //
  154. // Take out a reference on DeviceObject, we will release it when we
  155. // free the list or remove the DeviceObject from the list.
  156. //
  157. ObReferenceObject( DeviceObject );
  158. IopDbgPrint((IOP_LOADUNLOAD_INFO_LEVEL,
  159. "%wZ added as a relation %s\n",
  160. &deviceNode->InstancePath,
  161. (DirectDescendant)? "(direct descendant)" : ""));
  162. entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)DeviceObject | flags);
  163. entry->Count++;
  164. List->Count++;
  165. List->TagCount += Tagged;
  166. return STATUS_SUCCESS;
  167. } else {
  168. //
  169. // There isn't an Entry available for the level of this
  170. // DEVICE_OBJECT, the list has probably already been compressed.
  171. //
  172. return STATUS_INVALID_PARAMETER;
  173. }
  174. } else {
  175. //
  176. // DeviceObject is not a PhysicalDeviceObject (PDO).
  177. //
  178. return STATUS_NO_SUCH_DEVICE;
  179. }
  180. }
  181. PRELATION_LIST
  182. IopAllocateRelationList(
  183. IN PLUGPLAY_DEVICE_DELETE_TYPE OperationCode
  184. )
  185. /*++
  186. Routine Description:
  187. Allocate a new Relations List. The list is initially sized large enough to
  188. hold the deepest DEVICE_NODE encountered since the system started.
  189. Arguments:
  190. OperationCode - Type of operation the relation list is being allocated for.
  191. Return Value:
  192. Newly allocated list if enough PagedPool is available, otherwise NULL.
  193. --*/
  194. {
  195. PRELATION_LIST list;
  196. ULONG maxLevel;
  197. ULONG listSize;
  198. PAGED_CODE();
  199. //
  200. // Level number of the deepest DEVICE_NODE allocated since the system
  201. // started.
  202. //
  203. maxLevel = IopMaxDeviceNodeLevel;
  204. listSize = sizeof(RELATION_LIST) + maxLevel * sizeof(PRELATION_LIST_ENTRY);
  205. list = (PRELATION_LIST) PiAllocateCriticalMemory(
  206. OperationCode,
  207. PagedPool,
  208. listSize,
  209. 'rcpP'
  210. );
  211. if (list != NULL) {
  212. RtlZeroMemory(list, listSize);
  213. // list->FirstLevel = 0;
  214. // list->Count = 0;
  215. // list->Tagged = 0;
  216. list->MaxLevel = maxLevel;
  217. }
  218. return list;
  219. }
  220. NTSTATUS
  221. IopCompressRelationList(
  222. IN OUT PRELATION_LIST *List
  223. )
  224. /*++
  225. Routine Description:
  226. Compresses the relation list by reallocating the list and all the entries so
  227. that they a just large enough to hold their current contents.
  228. Once a list has been compressed IopAddRelationToList and
  229. IopMergeRelationLists targetting this list are both likely to fail.
  230. Arguments:
  231. List Relation List to compress.
  232. Return Value:
  233. STATUS_SUCCESS
  234. The list was compressed. Although this routine does allocate memory and
  235. the allocation can fail, the routine itself will never fail. Since the
  236. memory we are allocating is always smaller then the memory it is
  237. replacing we just keep the old memory if the allocation fails.
  238. --*/
  239. {
  240. PRELATION_LIST oldList, newList;
  241. PRELATION_LIST_ENTRY oldEntry, newEntry;
  242. ULONG lowestLevel;
  243. ULONG highestLevel;
  244. ULONG index;
  245. PAGED_CODE();
  246. oldList = *List;
  247. //
  248. // Initialize lowestLevel and highestLevel with illegal values chosen so
  249. // that the first real entry will override them.
  250. //
  251. lowestLevel = oldList->MaxLevel;
  252. highestLevel = oldList->FirstLevel;
  253. //
  254. // Loop through the list looking for allocated entries.
  255. //
  256. for (index = 0; index <= (oldList->MaxLevel - oldList->FirstLevel); index++) {
  257. if ((oldEntry = oldList->Entries[ index ]) != NULL) {
  258. //
  259. // This entry is allocated, update lowestLevel and highestLevel if
  260. // necessary.
  261. //
  262. if (lowestLevel > index) {
  263. lowestLevel = index;
  264. }
  265. if (highestLevel < index) {
  266. highestLevel = index;
  267. }
  268. if (oldEntry->Count < oldEntry->MaxCount) {
  269. //
  270. // This entry is only partially full. Allocate a new entry
  271. // which is just the right size to hold the current number of
  272. // PDEVICE_OBJECTs.
  273. //
  274. newEntry = ExAllocatePool( PagedPool,
  275. sizeof(RELATION_LIST_ENTRY) +
  276. (oldEntry->Count - 1) * sizeof(PDEVICE_OBJECT));
  277. if (newEntry != NULL) {
  278. //
  279. // Initialize Count and MaxCount to the number of
  280. // PDEVICE_OBJECTs in the old entry.
  281. //
  282. newEntry->Count = oldEntry->Count;
  283. newEntry->MaxCount = oldEntry->Count;
  284. //
  285. // Copy the PDEVICE_OBJECTs from the old entry to the new
  286. // one.
  287. //
  288. RtlCopyMemory( newEntry->Devices,
  289. oldEntry->Devices,
  290. oldEntry->Count * sizeof(PDEVICE_OBJECT));
  291. //
  292. // Free the old entry and store the new entry in the list.
  293. //
  294. ExFreePool( oldEntry );
  295. oldList->Entries[ index ] = newEntry;
  296. }
  297. }
  298. }
  299. }
  300. //
  301. // Assert that the old list isn't empty.
  302. //
  303. ASSERT(lowestLevel <= highestLevel);
  304. if (lowestLevel > highestLevel) {
  305. //
  306. // The list is empty - we shouldn't get asked to compress an empty list
  307. // but lets do it anyways.
  308. //
  309. lowestLevel = 0;
  310. highestLevel = 0;
  311. }
  312. //
  313. // Check if the old list had unused entries at the beginning or the end of
  314. // the Entries array.
  315. //
  316. if (lowestLevel != oldList->FirstLevel || highestLevel != oldList->MaxLevel) {
  317. //
  318. // Allocate a new List with just enough Entries to hold those between
  319. // FirstLevel and MaxLevel inclusive.
  320. //
  321. newList = ExAllocatePool( PagedPool,
  322. sizeof(RELATION_LIST) +
  323. (highestLevel - lowestLevel) * sizeof(PRELATION_LIST_ENTRY));
  324. if (newList != NULL) {
  325. //
  326. // Copy the old list to the new list and return it to the caller.
  327. //
  328. newList->Count = oldList->Count;
  329. newList->TagCount = oldList->TagCount;
  330. newList->FirstLevel = lowestLevel;
  331. newList->MaxLevel = highestLevel;
  332. RtlCopyMemory( newList->Entries,
  333. &oldList->Entries[ lowestLevel ],
  334. (highestLevel - lowestLevel + 1) * sizeof(PRELATION_LIST_ENTRY));
  335. ExFreePool( oldList );
  336. *List = newList;
  337. }
  338. }
  339. return STATUS_SUCCESS;
  340. }
  341. BOOLEAN
  342. IopEnumerateRelations(
  343. IN PRELATION_LIST List,
  344. IN OUT PULONG Marker,
  345. OUT PDEVICE_OBJECT *DeviceObject,
  346. OUT BOOLEAN *DirectDescendant OPTIONAL,
  347. OUT BOOLEAN *Tagged OPTIONAL,
  348. IN BOOLEAN Reverse
  349. )
  350. /*++
  351. Routine Description:
  352. Enumerates the relations in a list.
  353. Arguments:
  354. List Relation list to be enumerated.
  355. Marker Cookie used to maintain current place in the list. It
  356. must be initialized to 0 the first time
  357. IopEnumerateRelations is called.
  358. DeviceObject Returned Relation.
  359. DirectDescendant If specified then it is set if the relation is a direct
  360. descendant of the original target device of this remove.
  361. Tagged If specified then it is set if the relation is tagged
  362. otherwise it is cleared.
  363. Reverse Direction of traversal, TRUE means from deepest to
  364. closest to the root, FALSE means from the root down.
  365. If Reverse changes on a subsequent call then the
  366. previously enumerated relation is skipped. For example,
  367. given the sequence A, B, C, D, E. If
  368. IopEnumerateRelations is called thrice with Reverse set
  369. to FALSE and then called repeatedly with Reverse set to
  370. TRUE until it returns FALSE, the sequence would be: A,
  371. B, C, B, A.
  372. Once the end has been reached it is not possible to
  373. change directions.
  374. Return Value:
  375. TRUE - DeviceObject and optionally Tagged have been set to the next
  376. relation.
  377. FALSE - There are no more relations.
  378. --*/
  379. {
  380. PRELATION_LIST_ENTRY entry;
  381. LONG levelIndex;
  382. ULONG entryIndex;
  383. PAGED_CODE();
  384. //
  385. // The basic assumptions of our use of Marker is that there will never be
  386. // more than 16M DeviceNodes at any one level and that the tree will never
  387. // be more than 127 deep.
  388. //
  389. // The format of Marker is
  390. // Bit 31 = Valid (used to distinguish the initial call
  391. // Bit 30-24 = Current index into entries
  392. // Bit 23-0 = Current index into devices, 0xFFFFFF means last
  393. //
  394. if (*Marker == ~0U) {
  395. //
  396. // We've reached the end.
  397. //
  398. return FALSE;
  399. }
  400. if (*Marker == 0) {
  401. //
  402. // This is the initial call to IopEnumerateRelations
  403. //
  404. if (Reverse) {
  405. //
  406. // Initialize levelIndex to the last element of Entries
  407. //
  408. levelIndex = List->MaxLevel - List->FirstLevel;
  409. } else {
  410. //
  411. // Initialize levelIndex to the first element of Entries
  412. //
  413. levelIndex = 0;
  414. }
  415. //
  416. // Initialize entryIndex to unknown element of Devices. If we are going
  417. // in reverse then this will appear to be beyond the last element and
  418. // we'll adjust it the last one. If we are going forward then this will
  419. // appear to be just prior to the first element so when we increment it,
  420. // it will become zero.
  421. //
  422. entryIndex = ~0U;
  423. } else {
  424. //
  425. // Bit 31 is our valid bit, used to distinguish level 0, device 0 from
  426. // the first time call.
  427. //
  428. ASSERT(*Marker & ((ULONG)1 << 31));
  429. //
  430. // Current level stored in bits 30-24.
  431. //
  432. levelIndex = (*Marker >> 24) & 0x7F;
  433. //
  434. // Current device stored in bits 23-0.
  435. //
  436. entryIndex = *Marker & 0x00FFFFFF;
  437. }
  438. if (Reverse) {
  439. //
  440. // We are traversing the list bottom up, from the deepest device towards
  441. // the root.
  442. //
  443. for ( ; levelIndex >= 0; levelIndex--) {
  444. //
  445. // Since the Entries array can be sparse find the next allocated
  446. // Entry.
  447. //
  448. if ((entry = List->Entries[ levelIndex ]) != NULL) {
  449. if (entryIndex > entry->Count) {
  450. //
  451. // entryIndex (the current one) is greater than Count, this
  452. // will be the case where it is 0xFFFFFF, in other words
  453. // unspecified. Adjust it so that it is one past the last
  454. // one in this Entry.
  455. //
  456. entryIndex = entry->Count;
  457. }
  458. if (entryIndex > 0) {
  459. //
  460. // The current entry is beyond the first entry so the next
  461. // entry (which is the one we are looking for is immediately
  462. // prior, adjust entryIndex.
  463. //
  464. entryIndex--;
  465. //
  466. // Get the device object and remove the tag.
  467. //
  468. *DeviceObject = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS);
  469. if (Tagged != NULL) {
  470. //
  471. // The caller is interested in the tag value.
  472. //
  473. *Tagged = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_TAGGED);
  474. }
  475. if (DirectDescendant != NULL) {
  476. //
  477. // The caller is interested in the DirectDescendant value.
  478. //
  479. *DirectDescendant = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_DESCENDANT);
  480. }
  481. //
  482. // Update the marker (info for current device)
  483. //
  484. *Marker = ((ULONG)1 << 31) | (levelIndex << 24) | (entryIndex & 0x00FFFFFF);
  485. return TRUE;
  486. }
  487. }
  488. //
  489. // The current device object has been deleted or the current
  490. // device object is the first one in this Entry.
  491. // We need to continue to search backwards through the other
  492. // Entries.
  493. //
  494. entryIndex = ~0U;
  495. }
  496. } else {
  497. for ( ; levelIndex <= (LONG)(List->MaxLevel - List->FirstLevel); levelIndex++) {
  498. //
  499. // Since the Entries array can be sparse find the next allocated
  500. // Entry.
  501. //
  502. if ((entry = List->Entries[ levelIndex ]) != NULL) {
  503. //
  504. // entryIndex is the index of the current device or 0xFFFFFFFF
  505. // if this is the first time we have been called or the current
  506. // current device is the last one in its Entry. Increment the
  507. // index to point to the next device.
  508. //
  509. entryIndex++;
  510. if (entryIndex < entry->Count) {
  511. //
  512. // The next device is within this entry.
  513. //
  514. //
  515. // Get the device object and remove the tag.
  516. //
  517. *DeviceObject = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS);
  518. if (Tagged != NULL) {
  519. //
  520. // The caller is interested in the tag value.
  521. //
  522. *Tagged = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_TAGGED);
  523. }
  524. if (DirectDescendant != NULL) {
  525. //
  526. // The caller is interested in the DirectDescendant value.
  527. //
  528. *DirectDescendant = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_DESCENDANT);
  529. }
  530. //
  531. // Update the marker (info for current device)
  532. //
  533. *Marker = ((ULONG)1 << 31) | (levelIndex << 24) | (entryIndex & 0x00FFFFFF);
  534. return TRUE;
  535. }
  536. }
  537. //
  538. // The current device has been removed or we have processed the
  539. // last device in the current entry.
  540. // Set entryIndex so that it is just before the first device in
  541. // the next entry. Continue the search looking for the next
  542. // allocated Entry.
  543. //
  544. entryIndex = ~0U;
  545. }
  546. }
  547. //
  548. // We are at the end of the list
  549. //
  550. *Marker = ~0U;
  551. *DeviceObject = NULL;
  552. if (Tagged != NULL) {
  553. *Tagged = FALSE;
  554. }
  555. if (DirectDescendant != NULL) {
  556. *DirectDescendant = FALSE;
  557. }
  558. return FALSE;
  559. }
  560. VOID
  561. IopFreeRelationList(
  562. IN PRELATION_LIST List
  563. )
  564. /*++
  565. Routine Description:
  566. Free a relation list allocated by IopAllocateRelationList.
  567. Arguments:
  568. List The list to be freed.
  569. Return Value:
  570. NONE.
  571. --*/
  572. {
  573. PRELATION_LIST_ENTRY entry;
  574. ULONG levelIndex;
  575. ULONG entryIndex;
  576. PAGED_CODE();
  577. //
  578. // Search the list looking for allocated Entries.
  579. //
  580. for (levelIndex = 0; levelIndex <= (List->MaxLevel - List->FirstLevel); levelIndex++) {
  581. if ((entry = List->Entries[ levelIndex ]) != NULL) {
  582. //
  583. // This entry has been allocated.
  584. //
  585. for (entryIndex = 0; entryIndex < entry->Count; entryIndex++) {
  586. //
  587. // Dereference all the Devices in the entry.
  588. //
  589. ObDereferenceObject((PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS));
  590. }
  591. //
  592. // Free the Entry.
  593. //
  594. ExFreePool( entry );
  595. }
  596. }
  597. //
  598. // Free the list. It isn't necessary to dereference the DeviceObject that
  599. // was the original target that caused the list to be created. This
  600. // DeviceObject is also in one of the Entries and its reference is taken
  601. // and released there.
  602. //
  603. ExFreePool( List );
  604. }
  605. ULONG
  606. IopGetRelationsCount(
  607. PRELATION_LIST List
  608. )
  609. /*++
  610. Routine Description:
  611. Returns the total number of relations (Device Objects) in all the entries.
  612. Arguments:
  613. List Relation List.
  614. Return Value:
  615. Count of relations (Device Objects).
  616. --*/
  617. {
  618. PAGED_CODE();
  619. return List->Count;
  620. }
  621. ULONG
  622. IopGetRelationsTaggedCount(
  623. PRELATION_LIST List
  624. )
  625. /*++
  626. Routine Description:
  627. Returns the total number of relations (Device Objects) in all the entries
  628. which are tagged.
  629. Arguments:
  630. List Relation List.
  631. Return Value:
  632. Count of tagged relations (Device Objects).
  633. --*/
  634. {
  635. PAGED_CODE();
  636. return List->TagCount;
  637. }
  638. BOOLEAN
  639. IopIsRelationInList(
  640. PRELATION_LIST List,
  641. PDEVICE_OBJECT DeviceObject
  642. )
  643. /*++
  644. Routine Description:
  645. Checks if a relation (Device Object) exists in the specified relation list.
  646. Arguments:
  647. List Relation list to check.
  648. DeviceObject Relation to be checked.
  649. Return Value:
  650. TRUE
  651. Relation exists.
  652. FALSE
  653. Relation is not in the list.
  654. --*/
  655. {
  656. PDEVICE_NODE deviceNode;
  657. PRELATION_LIST_ENTRY entry;
  658. ULONG level;
  659. ULONG index;
  660. PAGED_CODE();
  661. if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
  662. //
  663. // The device object is a PDO.
  664. //
  665. level = deviceNode->Level;
  666. if (List->FirstLevel <= level && level <= List->MaxLevel) {
  667. //
  668. // The level is within the range of levels stored in this list.
  669. //
  670. if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
  671. //
  672. // There is an Entry for this level.
  673. //
  674. for (index = 0; index < entry->Count; index++) {
  675. //
  676. // For each Device in the entry, compare it to the given
  677. // DeviceObject
  678. if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
  679. //
  680. // It matches
  681. //
  682. return TRUE;
  683. }
  684. }
  685. }
  686. }
  687. }
  688. //
  689. // It wasn't a PDO
  690. // or the level wasn't in the range of levels in this list
  691. // or there are no DeviceObjects at the same level in this list
  692. // or the DeviceObject isn't in the Entry for its level in this list
  693. //
  694. return FALSE;
  695. }
  696. NTSTATUS
  697. IopMergeRelationLists(
  698. IN OUT PRELATION_LIST TargetList,
  699. IN PRELATION_LIST SourceList,
  700. IN BOOLEAN Tagged
  701. )
  702. /*++
  703. Routine Description:
  704. Merges two relation lists by copying all the relations from the source list
  705. to the target list. Source list remains unchanged.
  706. Arguments:
  707. TargetList List to which the relations from Sourcelist are added.
  708. SourceList List of relations to be added to TargetList.
  709. Tagged TRUE if relations from SourceList should be tagged when added to
  710. TargetList. If FALSE then relations added from SourceList are
  711. untagged.
  712. Return Value:
  713. STATUS_SUCCESS
  714. All the relations in SourceList were added to TargetList successfully.
  715. STATUS_OBJECT_NAME_COLLISION
  716. One of the relations in SourceList already exists in TargetList. This
  717. is a fatal error and TargetList may already have some of the relations
  718. from SourceList added. This could be dealt with more gracefully if
  719. necessary but the current callers of IopMergeRelationLists avoid this
  720. situation.
  721. STATUS_INSUFFICIENT_RESOURCES
  722. There isn't enough PagedPool available to allocate a new
  723. RELATION_LIST_ENTRY.
  724. STATUS_INVALID_PARAMETER
  725. The level of one of the relations in SourceList is less than FirstLevel
  726. or greater than the MaxLevel. This is a fatal error and TargetList may
  727. already have some of the relations from SourceList added. The only way
  728. this could happen is if the tree lock isn't held or if TargetList has
  729. been compressed by IopCompressRelationList. Both situations would be
  730. bugs in the caller.
  731. STATUS_NO_SUCH_DEVICE
  732. One of the relations in SourceList is not a PhysicalDeviceObject (PDO),
  733. it doesn't have a DEVICE_NODE associated with it. This is a fatal error
  734. and TargetList may already have some of the relations from SourceList
  735. added. This should never happen since it was a PDO when it was added to
  736. SourceList.
  737. --*/
  738. {
  739. PRELATION_LIST_ENTRY entry;
  740. LONG levelIndex;
  741. LONG entryIndex;
  742. LONG change;
  743. LONG maxIndex;
  744. NTSTATUS status;
  745. NTSTATUS finalStatus;
  746. PAGED_CODE();
  747. finalStatus = STATUS_SUCCESS;
  748. change = 1;
  749. levelIndex = 0;
  750. maxIndex = SourceList->MaxLevel - SourceList->FirstLevel;
  751. for ( ; ; ) {
  752. //
  753. // Stop at maxIndex if moving forward or at 0 otherwise.
  754. //
  755. if ( (change == 1 && levelIndex > maxIndex) ||
  756. (change == -1 && levelIndex < 0)) {
  757. break;
  758. }
  759. entry = SourceList->Entries[levelIndex];
  760. if (entry) {
  761. entryIndex = (change == 1)? 0 : entry->Count - 1;
  762. for ( ; ; ) {
  763. if (change == 1) {
  764. //
  765. // Stop if we added all DOs in this entry.
  766. //
  767. if (entryIndex >= (LONG)entry->Count) {
  768. break;
  769. }
  770. //
  771. // For each Device in the Entry, add it to the target List.
  772. //
  773. status = IopAddRelationToList( TargetList,
  774. (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[entryIndex] & ~RELATION_FLAGS),
  775. FALSE,
  776. Tagged);
  777. if (!NT_SUCCESS(status)) {
  778. //
  779. // We need to undo the damage on failure by unwinding and removing DOs we added..
  780. //
  781. finalStatus = status;
  782. change = -1;
  783. }
  784. } else {
  785. //
  786. // Stop at 0 if we are unwinding.
  787. //
  788. if (entryIndex < 0) {
  789. break;
  790. }
  791. status = IopRemoveRelationFromList( TargetList,
  792. (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[entryIndex] & ~RELATION_FLAGS));
  793. ASSERT(NT_SUCCESS(status));
  794. }
  795. entryIndex += change;
  796. }
  797. }
  798. levelIndex += change;
  799. }
  800. return finalStatus;
  801. }
  802. NTSTATUS
  803. IopRemoveIndirectRelationsFromList(
  804. IN PRELATION_LIST List
  805. )
  806. /*++
  807. Routine Description:
  808. Removes all the relations without the DirectDescendant flag from a relation
  809. list.
  810. Arguments:
  811. List List from which to remove the relations.
  812. Return Value:
  813. STATUS_SUCCESS
  814. The relations were removed successfully.
  815. --*/
  816. {
  817. PDEVICE_OBJECT deviceObject;
  818. PRELATION_LIST_ENTRY entry;
  819. ULONG level;
  820. LONG index;
  821. PAGED_CODE();
  822. //
  823. // For each Entry in the list.
  824. //
  825. for (level = List->FirstLevel; level <= List->MaxLevel; level++) {
  826. //
  827. // If the entry is allocated.
  828. //
  829. if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
  830. //
  831. // For each Device in the list.
  832. //
  833. for (index = entry->Count - 1; index >= 0; index--) {
  834. if (!((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_DESCENDANT)) {
  835. deviceObject = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS);
  836. ObDereferenceObject( deviceObject );
  837. if ((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_TAGGED) {
  838. List->TagCount--;
  839. }
  840. if (index < ((LONG)entry->Count - 1)) {
  841. RtlMoveMemory( &entry->Devices[ index ],
  842. &entry->Devices[ index + 1 ],
  843. (entry->Count - index - 1) * sizeof(PDEVICE_OBJECT));
  844. }
  845. if (--entry->Count == 0) {
  846. List->Entries[ level - List->FirstLevel ] = NULL;
  847. ExFreePool(entry);
  848. }
  849. List->Count--;
  850. }
  851. }
  852. }
  853. }
  854. return STATUS_SUCCESS;
  855. }
  856. NTSTATUS
  857. IopRemoveRelationFromList(
  858. PRELATION_LIST List,
  859. PDEVICE_OBJECT DeviceObject
  860. )
  861. /*++
  862. Routine Description:
  863. Removes a relation from a relation list.
  864. Arguments:
  865. List List from which to remove the relation.
  866. DeviceObject Relation to remove.
  867. Return Value:
  868. STATUS_SUCCESS
  869. The relation was removed successfully.
  870. STATUS_NO_SUCH_DEVICE
  871. The relation doesn't exist in the list.
  872. --*/
  873. {
  874. PDEVICE_NODE deviceNode;
  875. PRELATION_LIST_ENTRY entry;
  876. ULONG level;
  877. LONG index;
  878. PAGED_CODE();
  879. if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
  880. level = deviceNode->Level;
  881. ASSERT(List->FirstLevel <= level && level <= List->MaxLevel);
  882. if (List->FirstLevel <= level && level <= List->MaxLevel) {
  883. if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
  884. for (index = entry->Count - 1; index >= 0; index--) {
  885. if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
  886. ObDereferenceObject( DeviceObject );
  887. if (((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_TAGGED) != 0) {
  888. List->TagCount--;
  889. }
  890. if (index < ((LONG)entry->Count - 1)) {
  891. RtlMoveMemory( &entry->Devices[ index ],
  892. &entry->Devices[ index + 1 ],
  893. (entry->Count - index - 1) * sizeof(PDEVICE_OBJECT));
  894. }
  895. if (--entry->Count == 0) {
  896. List->Entries[ level - List->FirstLevel ] = NULL;
  897. ExFreePool(entry);
  898. }
  899. List->Count--;
  900. return STATUS_SUCCESS;
  901. }
  902. }
  903. }
  904. }
  905. }
  906. return STATUS_NO_SUCH_DEVICE;
  907. }
  908. VOID
  909. IopSetAllRelationsTags(
  910. PRELATION_LIST List,
  911. BOOLEAN Tagged
  912. )
  913. /*++
  914. Routine Description:
  915. Tags or untags all the relations in a relations list.
  916. Arguments:
  917. List Relation list containing relations to be tagged or untagged.
  918. Tagged TRUE if the relations should be tagged, FALSE if they are to be
  919. untagged.
  920. Return Value:
  921. NONE
  922. --*/
  923. {
  924. PRELATION_LIST_ENTRY entry;
  925. ULONG level;
  926. ULONG index;
  927. PAGED_CODE();
  928. //
  929. // For each Entry in the list.
  930. //
  931. for (level = List->FirstLevel; level <= List->MaxLevel; level++) {
  932. //
  933. // If the entry is allocated.
  934. //
  935. if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
  936. //
  937. // For each Device in the list.
  938. //
  939. for (index = 0; index < entry->Count; index++) {
  940. //
  941. // Set or clear the tag based on the argument Tagged.
  942. //
  943. if (Tagged) {
  944. entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] | RELATION_FLAG_TAGGED);
  945. } else {
  946. entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAG_TAGGED);
  947. }
  948. }
  949. }
  950. }
  951. //
  952. // If we are setting the tags then update the TagCount to the number of
  953. // relations in the list. Otherwise reset it to zero.
  954. //
  955. List->TagCount = Tagged ? List->Count : 0;
  956. }
  957. NTSTATUS
  958. IopSetRelationsTag(
  959. IN PRELATION_LIST List,
  960. IN PDEVICE_OBJECT DeviceObject,
  961. IN BOOLEAN Tagged
  962. )
  963. /*++
  964. Routine Description:
  965. Sets or clears a tag on a specified relation in a relations list. This
  966. routine is also used by some callers to determine if a relation exists in
  967. a list and if so to set the tag.
  968. Arguments:
  969. List List containing relation to be tagged or untagged.
  970. DeviceObject Relation to be tagged or untagged.
  971. Tagged TRUE if relation is to be tagged, FALSE if it is to be
  972. untagged.
  973. Return Value:
  974. STATUS_SUCCESS
  975. The relation was tagged successfully.
  976. STATUS_NO_SUCH_DEVICE
  977. The relation doesn't exist in the list.
  978. --*/
  979. {
  980. PDEVICE_NODE deviceNode;
  981. PRELATION_LIST_ENTRY entry;
  982. ULONG level;
  983. LONG index;
  984. PAGED_CODE();
  985. if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
  986. //
  987. // DeviceObject is a PhysicalDeviceObject (PDO), get its level.
  988. //
  989. level = deviceNode->Level;
  990. if (List->FirstLevel <= level && level <= List->MaxLevel) {
  991. //
  992. // The level is within the range of levels in this List.
  993. //
  994. if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
  995. //
  996. // The Entry for this level is allocated. Search each device
  997. // in the Entry looking for a match.
  998. //
  999. for (index = entry->Count - 1; index >= 0; index--) {
  1000. if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
  1001. //
  1002. // We found a match
  1003. //
  1004. if ((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_TAGGED) {
  1005. //
  1006. // The relation is already tagged so to simplify the
  1007. // logic below decrement the TagCount. We'll
  1008. // increment it later if the caller still wants it
  1009. // to be tagged.
  1010. //
  1011. List->TagCount--;
  1012. }
  1013. if (Tagged) {
  1014. //
  1015. // Set the tag and increment the number of tagged
  1016. // relations.
  1017. //
  1018. entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] | RELATION_FLAG_TAGGED);
  1019. List->TagCount++;
  1020. } else {
  1021. //
  1022. // Clear the tag.
  1023. //
  1024. entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAG_TAGGED);
  1025. }
  1026. return STATUS_SUCCESS;
  1027. }
  1028. }
  1029. }
  1030. }
  1031. }
  1032. //
  1033. // It wasn't a PDO
  1034. // or the level wasn't in the range of levels in this list
  1035. // or there are no DeviceObjects at the same level in this list
  1036. // or the DeviceObject isn't in the Entry for its level in this list
  1037. //
  1038. return STATUS_NO_SUCH_DEVICE;
  1039. }