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.

1019 lines
23 KiB

  1. /****
  2. Cache.cpp
  3. The schema caching routines to improve browsing performance.
  4. Locking Note:
  5. This schema cache design allows for rudimentary multi-thread
  6. protection via the lookup routines and the ReleaseRef routines.
  7. To date, we have not needed this type of protection, so it is
  8. not implemented. All locking rules should be obeyed, however,
  9. in case this protection is later needed.
  10. ****/
  11. #include "stdafx.h"
  12. #include "macros.h"
  13. USE_HANDLE_MACROS("SCHMMGMT(cache.cpp)")
  14. #include "resource.h"
  15. #include "cache.h"
  16. #include "schmutil.h"
  17. #include "compdata.h"
  18. //
  19. // The Schema Object.
  20. //
  21. SchemaObject::SchemaObject() {
  22. //
  23. // Initialize the list heads to NULL.
  24. //
  25. pNext = NULL;
  26. pSortedListFlink = NULL;
  27. pSortedListBlink = NULL;
  28. isDefunct = FALSE;
  29. dwClassType = (DWORD) -1; // invalid number
  30. systemMayContain = NULL;
  31. mayContain = NULL;
  32. systemMustContain = NULL;
  33. mustContain = NULL;
  34. systemAuxiliaryClass = NULL;
  35. auxiliaryClass = NULL;
  36. SyntaxOrdinal = UINT_MAX; // invalid number
  37. }
  38. SchemaObject::~SchemaObject() {
  39. ListEntry *pEntry, *pNextEntry;
  40. //
  41. // Empty any non-zero lists.
  42. //
  43. pEntry = systemMayContain;
  44. while ( pEntry ) {
  45. pNextEntry = pEntry->pNext;
  46. delete pEntry;
  47. pEntry = pNextEntry;
  48. }
  49. pEntry = mayContain;
  50. while ( pEntry ) {
  51. pNextEntry = pEntry->pNext;
  52. delete pEntry;
  53. pEntry = pNextEntry;
  54. }
  55. pEntry = systemMustContain;
  56. while ( pEntry ) {
  57. pNextEntry = pEntry->pNext;
  58. delete pEntry;
  59. pEntry = pNextEntry;
  60. }
  61. pEntry = mustContain;
  62. while ( pEntry ) {
  63. pNextEntry = pEntry->pNext;
  64. delete pEntry;
  65. pEntry = pNextEntry;
  66. }
  67. pEntry = systemAuxiliaryClass;
  68. while ( pEntry ) {
  69. pNextEntry = pEntry->pNext;
  70. delete pEntry;
  71. pEntry = pNextEntry;
  72. }
  73. pEntry = auxiliaryClass;
  74. while ( pEntry ) {
  75. pNextEntry = pEntry->pNext;
  76. delete pEntry;
  77. pEntry = pNextEntry;
  78. }
  79. //
  80. // Done (the CStrings will clean themselves up).
  81. //
  82. return;
  83. }
  84. //
  85. // The Schema Object cache.
  86. //
  87. SchemaObjectCache::SchemaObjectCache() {
  88. pScopeControl = NULL;
  89. //
  90. // Initialize the hash table.
  91. //
  92. buckets = HASH_TABLE_SIZE;
  93. hash_table = (SchemaObject**) LocalAlloc( LMEM_ZEROINIT,
  94. sizeof( SchemaObject* ) * buckets );
  95. if (hash_table != NULL)
  96. {
  97. memset(
  98. hash_table,
  99. 0,
  100. sizeof( SchemaObject* ) * buckets );
  101. }
  102. pSortedClasses = NULL;
  103. pSortedAttribs = NULL;
  104. fInitialized = FALSE;
  105. }
  106. SchemaObjectCache::~SchemaObjectCache() {
  107. //
  108. // Clear the hash table.
  109. //
  110. FreeAll();
  111. LocalFree( hash_table );
  112. hash_table = NULL;
  113. }
  114. VOID
  115. SchemaObjectCache::FreeAll() {
  116. SchemaObject *current, *next;
  117. DebugTrace( L"SchemaObjectCache::FreeAll()\n" );
  118. for ( UINT i = 0 ; i < buckets ; i++ ) {
  119. current = hash_table[i];
  120. while ( current ) {
  121. next = current->pNext;
  122. delete current;
  123. current = next;
  124. }
  125. }
  126. memset(
  127. &(hash_table[0]),
  128. 0,
  129. sizeof( SchemaObject* ) * buckets );
  130. pSortedClasses = NULL;
  131. pSortedAttribs = NULL;
  132. fInitialized = FALSE;
  133. return;
  134. }
  135. UINT
  136. SchemaObjectCache::CalculateHashKey(
  137. CString HashKey
  138. ) {
  139. int len = HashKey.GetLength();
  140. LPCTSTR current = (LPCTSTR)HashKey;
  141. int hash = 0;
  142. for ( int i = 0 ; i < len ; i++ ) {
  143. hash += (i+1) * ( (TCHAR) CharLower((LPTSTR) current[i]) );
  144. }
  145. hash %= buckets;
  146. DebugTrace( L"SchemaObjectCache::CalculateHashKey %ls (len %li) == %li\n",
  147. const_cast<LPWSTR>((LPCTSTR)HashKey),
  148. len,
  149. hash );
  150. return hash;
  151. }
  152. HRESULT
  153. SchemaObjectCache::InsertSchemaObject(
  154. SchemaObject* Object
  155. ) {
  156. SchemaObject* chain;
  157. int bucket = CalculateHashKey( Object->commonName );
  158. chain = hash_table[bucket];
  159. hash_table[bucket] = Object;
  160. Object->pNext = chain;
  161. DebugTrace( L"Insert: %ls, %ls, %ls, --> %li\n",
  162. const_cast<LPWSTR>((LPCTSTR)Object->ldapDisplayName),
  163. const_cast<LPWSTR>((LPCTSTR)Object->commonName),
  164. const_cast<LPWSTR>((LPCTSTR)Object->description),
  165. bucket );
  166. return S_OK;
  167. }
  168. HRESULT
  169. SchemaObjectCache::InsertSortedSchemaObject(
  170. SchemaObject* Object
  171. ) {
  172. SchemaObject *pCurrent = NULL;
  173. SchemaObject *pHead = NULL;
  174. BOOLEAN ChangeHead = TRUE;
  175. if ( Object->schemaObjectType == SCHMMGMT_CLASS ) {
  176. pCurrent = pSortedClasses;
  177. } else {
  178. ASSERT( Object->schemaObjectType == SCHMMGMT_ATTRIBUTE );
  179. pCurrent = pSortedAttribs;
  180. }
  181. //
  182. // If we haven't built the sorted list yet, then we
  183. // don't need to insert this element into it.
  184. //
  185. if ( !pCurrent ) {
  186. return S_OK;
  187. }
  188. //
  189. // The sorted list is circular.
  190. //
  191. while ( ( 0 < ( Object->commonName.CompareNoCase(
  192. pCurrent->commonName ) ) ) &&
  193. ( pCurrent != pHead ) ) {
  194. if ( ChangeHead ) {
  195. pHead = pCurrent;
  196. ChangeHead = FALSE;
  197. }
  198. pCurrent = pCurrent->pSortedListFlink;
  199. }
  200. pCurrent->pSortedListBlink->pSortedListFlink = Object;
  201. Object->pSortedListBlink = pCurrent->pSortedListBlink;
  202. Object->pSortedListFlink = pCurrent;
  203. pCurrent->pSortedListBlink = Object;
  204. if ( ChangeHead ) {
  205. if ( Object->schemaObjectType == SCHMMGMT_CLASS ) {
  206. pSortedClasses = Object;
  207. } else {
  208. pSortedAttribs = Object;
  209. }
  210. }
  211. return S_OK;
  212. }
  213. // This functions behavior has been modified to support schema delete.
  214. // Previously this function would return the first match to the ldapDisplayName,
  215. // Now it will return the first match to the ldapDisplayName that is not defunct
  216. SchemaObject*
  217. SchemaObjectCache::LookupSchemaObject(
  218. CString ldapDisplayName,
  219. SchmMgmtObjectType ObjectType
  220. ) {
  221. if ( !fInitialized ) {
  222. LoadCache();
  223. }
  224. SchemaObject * pHead = 0;
  225. if ( ObjectType == SCHMMGMT_ATTRIBUTE)
  226. {
  227. pHead = pSortedAttribs;
  228. }
  229. else
  230. {
  231. pHead = pSortedClasses;
  232. }
  233. SchemaObject * pObject = pHead;
  234. BOOL fFound = FALSE;
  235. ASSERT( pObject );
  236. do {
  237. if( ObjectType == pObject->schemaObjectType &&
  238. !pObject->isDefunct &&
  239. !pObject->ldapDisplayName.CompareNoCase(ldapDisplayName) )
  240. {
  241. fFound = TRUE;
  242. break;
  243. }
  244. pObject = pObject->pSortedListFlink;
  245. } while ( pObject != pHead );
  246. if (!fFound)
  247. {
  248. pObject = 0;
  249. }
  250. return pObject;
  251. /*
  252. int length = 0;
  253. int bucket = CalculateHashKey( ldapDisplayName );
  254. SchemaObject* chain = hash_table[bucket];
  255. if ( !fInitialized ) {
  256. LoadCache();
  257. }
  258. while ( chain ) {
  259. if ( ( ObjectType == chain->schemaObjectType ) &&
  260. !chain->isDefunct &&
  261. !ldapDisplayName.CompareNoCase( chain->ldapDisplayName ) ) {
  262. DebugTrace( L"SchemaObjectCache::LookupSchemaObject %ls, chain depth %li.\n",
  263. const_cast<LPWSTR>((LPCTSTR)ldapDisplayName),
  264. length );
  265. return chain;
  266. } else {
  267. chain = chain->pNext;
  268. length++;
  269. }
  270. }
  271. DebugTrace( L"SchemaObjectCache::LookupSchemaObject %ls (NO HIT), chain depth %li.\n",
  272. const_cast<LPWSTR>((LPCTSTR)ldapDisplayName),
  273. length );
  274. //
  275. // LOCKING NOTE: The simple ref counting and locking is not
  276. // currently implemented. See note at the head of the file.
  277. //
  278. return NULL;
  279. */
  280. }
  281. //
  282. // sequential search of the entire cache for an object with the given CN
  283. //
  284. // objectType is given to slightly speed-up the process.
  285. //
  286. SchemaObject*
  287. SchemaObjectCache::LookupSchemaObjectByCN( LPCTSTR pszCN,
  288. SchmMgmtObjectType objectType )
  289. {
  290. if ( !fInitialized ) {
  291. LoadCache();
  292. }
  293. SchemaObject * pHead = 0;
  294. if ( objectType == SCHMMGMT_ATTRIBUTE)
  295. {
  296. pHead = pSortedAttribs;
  297. }
  298. else
  299. {
  300. pHead = pSortedClasses;
  301. }
  302. SchemaObject * pObject = pHead;
  303. BOOL fFound = FALSE;
  304. ASSERT( pObject );
  305. do {
  306. if( objectType == pObject->schemaObjectType &&
  307. !pObject->commonName.CompareNoCase(pszCN) )
  308. {
  309. fFound = TRUE;
  310. break;
  311. }
  312. pObject = pObject->pSortedListFlink;
  313. } while ( pObject != pHead );
  314. //
  315. // LOCKING NOTE: The simple ref counting and locking is not
  316. // currently implemented. See note at the head of the file.
  317. //
  318. return fFound ? pObject : NULL;
  319. }
  320. VOID
  321. SchemaObjectCache::ReleaseRef(
  322. SchemaObject*
  323. ) {
  324. //
  325. // E_NOTIMPL
  326. // See the note at the head of the file.
  327. //
  328. }
  329. HRESULT
  330. SchemaObjectCache::LoadCache(
  331. VOID
  332. )
  333. /***
  334. This routine executes a couple of DS searches to read the
  335. relevant items out of the schema along with some attributes
  336. of those items.
  337. This information is cached.
  338. ***/
  339. {
  340. if ( fInitialized ) {
  341. return S_OK;
  342. }
  343. LPWSTR Attributes[] = {
  344. g_DisplayName,
  345. g_CN,
  346. g_Description,
  347. g_MayContain,
  348. g_MustContain,
  349. g_SystemMayContain,
  350. g_SystemMustContain,
  351. g_AuxiliaryClass,
  352. g_SystemAuxiliaryClass,
  353. g_SubclassOf,
  354. g_ObjectClassCategory,
  355. g_AttributeSyntax,
  356. g_omSyntax,
  357. g_omObjectClass,
  358. g_isDefunct,
  359. g_GlobalClassID, // must be last
  360. };
  361. const DWORD AttributeCount = sizeof(Attributes) / sizeof(Attributes[0]);
  362. ADS_SEARCH_HANDLE hSearchHandle = NULL;
  363. HRESULT hr = S_OK;
  364. CComPtr<IADs> ipADs;
  365. AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
  366. //
  367. // Put up a wait cursor since
  368. // this could take a little while. The cursor will
  369. // revert when the the CWaitCursor goes out of scope.
  370. //
  371. CWaitCursor wait;
  372. //
  373. // Get the schema container path.
  374. //
  375. if ( NULL == pScopeControl )
  376. {
  377. ASSERT(FALSE);
  378. return E_FAIL;
  379. }
  380. hr = pScopeControl->CheckSchemaPermissions( &ipADs );
  381. if ( !ipADs )
  382. {
  383. ASSERT( FAILED(hr) );
  384. if( E_FAIL == hr )
  385. DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_NO_SCHEMA_PATH );
  386. else
  387. DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
  388. return hr;
  389. }
  390. else if( FAILED(hr) )
  391. hr = S_OK; // ignore the error. In case of an error, minimal permissions are assumed
  392. //
  393. // Open the schema container.
  394. //
  395. IDirectorySearch *pDSSearch = 0;
  396. hr = ipADs->QueryInterface( IID_IDirectorySearch,
  397. (void **)&pDSSearch );
  398. if ( FAILED(hr) )
  399. {
  400. ASSERT(FALSE);
  401. DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_NO_SCHEMA_PATH );
  402. return hr;
  403. }
  404. //
  405. // Set up the search preferences
  406. //
  407. static const int SEARCH_PREF_COUNT = 3;
  408. ADS_SEARCHPREF_INFO prefs[SEARCH_PREF_COUNT];
  409. // server side sort preferences
  410. ADS_SORTKEY SortKey;
  411. SortKey.pszAttrType = g_DisplayName;
  412. SortKey.pszReserved = NULL;
  413. SortKey.fReverseorder = 0;
  414. prefs[0].dwSearchPref = ADS_SEARCHPREF_SORT_ON;
  415. prefs[0].vValue.dwType = ADSTYPE_PROV_SPECIFIC;
  416. prefs[0].vValue.ProviderSpecific.dwLength = sizeof(ADS_SORTKEY);
  417. prefs[0].vValue.ProviderSpecific.lpValue = (LPBYTE) &SortKey;
  418. // result page size
  419. prefs[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
  420. prefs[1].vValue.dwType = ADSTYPE_INTEGER;
  421. prefs[1].vValue.Integer = 300; // get a bunch in one hit
  422. // scope
  423. prefs[2].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
  424. prefs[2].vValue.dwType = ADSTYPE_INTEGER;
  425. prefs[2].vValue.Integer = ADS_SCOPE_ONELEVEL; // one level
  426. //
  427. // Build the class search request.
  428. //
  429. hr = pDSSearch->SetSearchPreference(prefs, SEARCH_PREF_COUNT);
  430. ASSERT( SUCCEEDED( hr ) );
  431. hr =
  432. pDSSearch->ExecuteSearch(
  433. L"(objectCategory=classSchema)",
  434. Attributes,
  435. AttributeCount,
  436. &hSearchHandle );
  437. if ( FAILED(hr) )
  438. {
  439. ASSERT(FALSE);
  440. pDSSearch->Release();
  441. return hr;
  442. }
  443. //
  444. // Cache these entries. We ignore error codes and try to
  445. // process the attributes regardless.
  446. //
  447. hr = ProcessSearchResults( pDSSearch, hSearchHandle, SCHMMGMT_CLASS);
  448. pDSSearch->CloseSearchHandle( hSearchHandle );
  449. hr = pDSSearch->SetSearchPreference(prefs, SEARCH_PREF_COUNT);
  450. ASSERT( SUCCEEDED( hr ) );
  451. //
  452. // This array index must match the array declared above!
  453. //
  454. Attributes[AttributeCount - 1] = g_GlobalAttributeID;
  455. hr =
  456. pDSSearch->ExecuteSearch(
  457. L"(objectCategory=attributeSchema)",
  458. Attributes,
  459. AttributeCount,
  460. &hSearchHandle );
  461. if ( FAILED(hr) )
  462. {
  463. ASSERT(FALSE);
  464. pDSSearch->Release();
  465. return hr;
  466. }
  467. //
  468. // Cache these entries. Again, ignore the error code.
  469. //
  470. hr = ProcessSearchResults( pDSSearch, hSearchHandle, SCHMMGMT_ATTRIBUTE );
  471. pDSSearch->CloseSearchHandle( hSearchHandle );
  472. //
  473. // Release the schema container.
  474. //
  475. pDSSearch->Release();
  476. //
  477. // Mark the cache as open for business.
  478. //
  479. fInitialized = TRUE;
  480. return S_OK;
  481. }
  482. HRESULT
  483. SchemaObjectCache::ProcessSearchResults(
  484. IDirectorySearch *pDSSearch,
  485. ADS_SEARCH_HANDLE hSearchHandle,
  486. SchmMgmtObjectType ObjectType
  487. ) {
  488. HRESULT hr = S_OK;
  489. SchemaObject *schemaObject;
  490. ADS_SEARCH_COLUMN Column;
  491. while ( TRUE ) {
  492. //
  493. // Get the next row set. If there are no more rows, break.
  494. // If there was some other error, try to skip over the
  495. // troubled row.
  496. //
  497. hr = pDSSearch->GetNextRow( hSearchHandle );
  498. if ( hr == S_ADS_NOMORE_ROWS ) {
  499. break;
  500. }
  501. if ( hr != S_OK ) {
  502. ASSERT( FALSE );
  503. continue;
  504. }
  505. //
  506. // Allocate a new schema object. If one could not be
  507. // allocated, stop loading the schema since we're in a
  508. // low memory condition.
  509. //
  510. schemaObject = new SchemaObject;
  511. if ( !schemaObject ) {
  512. AfxMessageBox(IDS_SCHEMA_NOT_FULLY_LOADED, MB_OK);
  513. ASSERT( FALSE );
  514. return E_OUTOFMEMORY;
  515. }
  516. //
  517. // Set the object type.
  518. //
  519. schemaObject->schemaObjectType = ObjectType;
  520. //
  521. // Get the common name column.
  522. //
  523. hr = pDSSearch->GetColumn( hSearchHandle, g_CN, &Column );
  524. if ( SUCCEEDED(hr) ) {
  525. schemaObject->commonName = (Column.pADsValues)->CaseIgnoreString;
  526. pDSSearch->FreeColumn( &Column );
  527. }
  528. //
  529. // Get the ldap display name.
  530. //
  531. hr = pDSSearch->GetColumn( hSearchHandle, g_DisplayName, &Column );
  532. if ( SUCCEEDED(hr) ) {
  533. schemaObject->ldapDisplayName = (Column.pADsValues)->CaseIgnoreString;
  534. pDSSearch->FreeColumn( &Column );
  535. }
  536. //
  537. // Get the description.
  538. //
  539. hr = pDSSearch->GetColumn( hSearchHandle, g_Description, &Column );
  540. if ( SUCCEEDED(hr) ) {
  541. schemaObject->description = (Column.pADsValues)->CaseIgnoreString;
  542. pDSSearch->FreeColumn( &Column );
  543. }
  544. //
  545. // Is this object current active?
  546. //
  547. schemaObject->isDefunct = FALSE;
  548. hr = pDSSearch->GetColumn( hSearchHandle, g_isDefunct, &Column );
  549. if ( SUCCEEDED(hr) ) {
  550. if ( (Column.pADsValues)->Boolean ) {
  551. schemaObject->isDefunct = TRUE;
  552. }
  553. pDSSearch->FreeColumn( &Column );
  554. }
  555. //
  556. // Get the class specific data.
  557. //
  558. if ( ObjectType == SCHMMGMT_CLASS ) {
  559. //
  560. // Get the attributes and auxiliary classes for this class.
  561. //
  562. hr = pDSSearch->GetColumn( hSearchHandle, g_SystemMustContain, &Column );
  563. if ( SUCCEEDED(hr) ) {
  564. schemaObject->systemMustContain = MakeColumnList( &Column );
  565. pDSSearch->FreeColumn( &Column );
  566. }
  567. hr = pDSSearch->GetColumn( hSearchHandle, g_SystemMayContain, &Column );
  568. if ( SUCCEEDED(hr) ) {
  569. schemaObject->systemMayContain = MakeColumnList( &Column );
  570. pDSSearch->FreeColumn( &Column );
  571. }
  572. hr = pDSSearch->GetColumn( hSearchHandle, g_MustContain, &Column );
  573. if ( SUCCEEDED(hr) ) {
  574. schemaObject->mustContain = MakeColumnList( &Column );
  575. pDSSearch->FreeColumn( &Column );
  576. }
  577. hr = pDSSearch->GetColumn( hSearchHandle, g_MayContain, &Column );
  578. if ( SUCCEEDED(hr) ) {
  579. schemaObject->mayContain = MakeColumnList( &Column );
  580. pDSSearch->FreeColumn( &Column );
  581. }
  582. hr = pDSSearch->GetColumn( hSearchHandle, g_SystemAuxiliaryClass, &Column );
  583. if ( SUCCEEDED(hr) ) {
  584. schemaObject->systemAuxiliaryClass = MakeColumnList( &Column );
  585. pDSSearch->FreeColumn( &Column );
  586. }
  587. hr = pDSSearch->GetColumn( hSearchHandle, g_AuxiliaryClass, &Column );
  588. if ( SUCCEEDED(hr) ) {
  589. schemaObject->auxiliaryClass = MakeColumnList( &Column );
  590. pDSSearch->FreeColumn( &Column );
  591. }
  592. hr = pDSSearch->GetColumn( hSearchHandle, g_ObjectClassCategory, &Column );
  593. if ( SUCCEEDED(hr) ) {
  594. schemaObject->dwClassType = (Column.pADsValues)->Integer;
  595. pDSSearch->FreeColumn( &Column );
  596. }
  597. hr = pDSSearch->GetColumn( hSearchHandle, g_SubclassOf, &Column );
  598. if ( SUCCEEDED(hr) ) {
  599. schemaObject->subClassOf = (Column.pADsValues)->CaseIgnoreString;
  600. pDSSearch->FreeColumn( &Column );
  601. }
  602. //
  603. // Get the oid.
  604. //
  605. hr = pDSSearch->GetColumn( hSearchHandle, g_GlobalClassID, &Column );
  606. if ( SUCCEEDED(hr) ) {
  607. schemaObject->oid = (Column.pADsValues)->CaseIgnoreString;
  608. pDSSearch->FreeColumn( &Column );
  609. }
  610. }
  611. //
  612. // Get the attribute specific data.
  613. //
  614. if ( ObjectType == SCHMMGMT_ATTRIBUTE ) {
  615. //
  616. // Select a syntax string for the attribute.
  617. //
  618. CString strAttributeSyntax;
  619. ADS_OCTET_STRING OmObjectClass;
  620. UINT omSyntax = 0;
  621. schemaObject->SyntaxOrdinal = SCHEMA_SYNTAX_UNKNOWN;
  622. OmObjectClass.dwLength = 0;
  623. OmObjectClass.lpValue = NULL;
  624. hr = pDSSearch->GetColumn( hSearchHandle, g_AttributeSyntax, &Column );
  625. if ( SUCCEEDED(hr) ) {
  626. strAttributeSyntax = (Column.pADsValues)->CaseIgnoreString;
  627. pDSSearch->FreeColumn( &Column );
  628. hr = pDSSearch->GetColumn( hSearchHandle, g_omSyntax, &Column );
  629. if ( SUCCEEDED(hr) ) {
  630. omSyntax = (Column.pADsValues)->Integer;
  631. pDSSearch->FreeColumn( &Column );
  632. }
  633. hr = pDSSearch->GetColumn( hSearchHandle, g_omObjectClass, &Column );
  634. if ( SUCCEEDED(hr) ) {
  635. OmObjectClass = (Column.pADsValues)->OctetString;
  636. }
  637. schemaObject->SyntaxOrdinal = GetSyntaxOrdinal(
  638. strAttributeSyntax, omSyntax, &OmObjectClass );
  639. // OmObjectClass has a pointer which becomes invalid after FreeColumn()
  640. if ( SUCCEEDED(hr) ) {
  641. pDSSearch->FreeColumn( &Column );
  642. OmObjectClass.dwLength = 0;
  643. OmObjectClass.lpValue = NULL;
  644. }
  645. }
  646. else
  647. ASSERT( FALSE );
  648. //
  649. // Get the oid.
  650. //
  651. hr = pDSSearch->GetColumn( hSearchHandle, g_GlobalAttributeID, &Column );
  652. if ( SUCCEEDED(hr) ) {
  653. schemaObject->oid = (Column.pADsValues)->CaseIgnoreString;
  654. pDSSearch->FreeColumn( &Column );
  655. }
  656. }
  657. //
  658. // Insert this into the sorted item list.
  659. //
  660. InsertSortedTail( schemaObject );
  661. //
  662. // Insert this schema object into the cache.
  663. //
  664. InsertSchemaObject( schemaObject );
  665. schemaObject = NULL;
  666. }
  667. return S_OK;
  668. }
  669. VOID
  670. SchemaObjectCache::InsertSortedTail(
  671. SchemaObject* pObject
  672. ) {
  673. SchemaObject **sorted_list;
  674. SchemaObject *pHead;
  675. //
  676. // Find the correct list.
  677. //
  678. if ( pObject->schemaObjectType == SCHMMGMT_CLASS ) {
  679. sorted_list = &pSortedClasses;
  680. } else {
  681. sorted_list = &pSortedAttribs;
  682. }
  683. //
  684. // Actually insert the element.
  685. //
  686. if ( *sorted_list == NULL ) {
  687. //
  688. // This is the first element.
  689. //
  690. *sorted_list = pObject;
  691. pObject->pSortedListFlink = pObject;
  692. pObject->pSortedListBlink = pObject;
  693. } else {
  694. //
  695. // This is not the first element;
  696. //
  697. pHead = *sorted_list;
  698. pObject->pSortedListBlink = pHead->pSortedListBlink;
  699. pHead->pSortedListBlink->pSortedListFlink = pObject;
  700. pHead->pSortedListBlink = pObject;
  701. pObject->pSortedListFlink = pHead;
  702. }
  703. }
  704. ListEntry*
  705. SchemaObjectCache::MakeColumnList(
  706. PADS_SEARCH_COLUMN pColumn
  707. ) {
  708. ListEntry *pHead = NULL, *pLast = NULL, *pCurrent = NULL;
  709. for ( DWORD i = 0 ; i < pColumn->dwNumValues ; i++ ) {
  710. pCurrent = new ListEntry;
  711. //
  712. // If we run out of memory, return what we made so far.
  713. //
  714. if ( !pCurrent ) {
  715. break;
  716. }
  717. //
  718. // If there's no head, remember this as the first.
  719. // Otherwise, stick this on the end of the list
  720. // and update the last pointer.
  721. //
  722. if ( !pHead ) {
  723. pHead = pCurrent;
  724. pLast = pCurrent;
  725. } else {
  726. pLast->pNext = pCurrent;
  727. pLast = pCurrent;
  728. }
  729. //
  730. // Record the value.
  731. //
  732. pCurrent->Attribute = pColumn->pADsValues[i].CaseIgnoreString;
  733. DebugTrace( L"MakeColumnList recorded %ls.\n",
  734. pColumn->pADsValues[i].CaseIgnoreString );
  735. //
  736. // That's it.
  737. //
  738. }
  739. return pHead;
  740. }
  741. VOID
  742. SchemaObjectCache::FreeColumnList(
  743. ListEntry *pListHead
  744. ) {
  745. //
  746. // Delete the linked list.
  747. //
  748. ListEntry *pNext, *pCurrent;
  749. if ( !pListHead ) {
  750. return;
  751. }
  752. pCurrent = pListHead;
  753. do {
  754. pNext = pCurrent->pNext;
  755. delete pCurrent;
  756. pCurrent = pNext;
  757. } while ( pCurrent );
  758. return;
  759. }