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.

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