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.

453 lines
12 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. cache.c
  5. Abstract:
  6. This module contains the code to cache BINL client information across
  7. requests for the BINL server.
  8. Author:
  9. Andy Herron (andyhe) 5-Mar-1998
  10. Environment:
  11. User Mode - Win32
  12. Revision History:
  13. --*/
  14. #include "binl.h"
  15. #pragma hdrstop
  16. ULONG BinlCacheCount = 0;
  17. ULONG BinlCacheEntriesInUse = 0;
  18. VOID
  19. BinlFreeCacheEntry (
  20. PMACHINE_INFO CacheEntry
  21. );
  22. DWORD
  23. BinlCreateOrFindCacheEntry (
  24. PCHAR Guid,
  25. BOOLEAN CreateIfNotExist,
  26. PMACHINE_INFO *CacheEntry
  27. )
  28. //
  29. // This searches the list of cached entries for the matching GUID. If it is
  30. // found and is in use, we return an error as another thread is working on
  31. // the same request. If it is found and not in use, we mark it in use and
  32. // return it. If it is not found, we add it and return it.
  33. //
  34. {
  35. PLIST_ENTRY listEntry;
  36. PMACHINE_INFO currentEntry = NULL;
  37. DWORD expireTickCount;
  38. EnterCriticalSection( &BinlCacheListLock );
  39. //
  40. // For now, we don't bother with a scavenger thread. Just free them up
  41. // as we come across them.
  42. //
  43. if (BinlCacheExpireMilliseconds > 0) {
  44. expireTickCount = GetTickCount() - BinlCacheExpireMilliseconds;
  45. } else {
  46. expireTickCount = 0;
  47. }
  48. if (BinlCurrentState == BINL_STOPPED) {
  49. //
  50. // if we're in the midst of shutting down, ignore the request. We
  51. // do the check while holding the lock to synchronize with any thread
  52. // calling BinlCloseCache.
  53. //
  54. LeaveCriticalSection( &BinlCacheListLock );
  55. *CacheEntry = NULL;
  56. //
  57. // We return the EVENT_SERVER_SHUTDOWN to tell the calling thread
  58. // not to bother with this request.
  59. //
  60. return EVENT_SERVER_SHUTDOWN;
  61. }
  62. listEntry = BinlCacheList.Flink;
  63. while ( listEntry != &BinlCacheList ) {
  64. LONG isEqual;
  65. currentEntry = (PMACHINE_INFO) CONTAINING_RECORD(
  66. listEntry,
  67. MACHINE_INFO,
  68. CacheListEntry );
  69. //
  70. // lazy free.. check to see if we should free this entry because
  71. // it's time to live has expired.
  72. //
  73. if ((currentEntry->InProgress == FALSE) &&
  74. (expireTickCount > 0) &&
  75. (currentEntry->TimeCreated < expireTickCount)) {
  76. listEntry = listEntry->Flink;
  77. BinlFreeCacheEntry( currentEntry );
  78. BinlPrintDbg((DEBUG_BINL_CACHE, "removed cache entry %x", currentEntry ));
  79. continue;
  80. }
  81. //
  82. // search for the given guid. The list is sorted by GUID so when
  83. // we hit a guid that is greater than current, we stop searching.
  84. //
  85. isEqual = memcmp( Guid, currentEntry->Guid, BINL_GUID_LENGTH );
  86. if (isEqual < 0) {
  87. listEntry = listEntry->Flink;
  88. continue;
  89. }
  90. if (isEqual == 0) {
  91. DWORD rc = ERROR_SUCCESS;
  92. //
  93. // If another thread is using this entry, then we should ignore
  94. // the request we're currently working on as the other thread will
  95. // respond.
  96. if (currentEntry->InProgress == TRUE) {
  97. LeaveCriticalSection( &BinlCacheListLock );
  98. *CacheEntry = NULL;
  99. return ERROR_BINL_CLIENT_EXISTS;
  100. }
  101. //
  102. // Also, if the entry is not ours to handle, then we return the
  103. // error here. We don't return ERROR_BINL_INVALID_BINL_CLIENT
  104. // because that will tell GetBootParameters to process it as a
  105. // new client. Instead, we return ERROR_BINL_CLIENT_EXISTS so
  106. // that the caller will simply return the error. A bit ugly,
  107. // but necessary.
  108. //
  109. if (currentEntry->MyClient == FALSE) {
  110. if (currentEntry->EntryExists) {
  111. LeaveCriticalSection( &BinlCacheListLock );
  112. *CacheEntry = NULL;
  113. return ERROR_BINL_CLIENT_EXISTS;
  114. }
  115. //
  116. // we return the empty entry since we might now be
  117. // creating the account.
  118. //
  119. rc = ERROR_BINL_INVALID_BINL_CLIENT;
  120. }
  121. //
  122. // since we're now using an entry, reset the event saying all
  123. // threads are done.
  124. //
  125. BinlCacheEntriesInUse++;
  126. currentEntry->InProgress = TRUE;
  127. *CacheEntry = currentEntry;
  128. LeaveCriticalSection( &BinlCacheListLock );
  129. return rc;
  130. }
  131. //
  132. // we're at the first entry that is greater than the guid in question.
  133. //
  134. break;
  135. }
  136. if (! CreateIfNotExist) {
  137. LeaveCriticalSection( &BinlCacheListLock );
  138. *CacheEntry = NULL;
  139. return ERROR_BINL_INVALID_BINL_CLIENT;
  140. }
  141. // currentEntry is not valid, but listEntry is.
  142. //
  143. // Add a new entry at the end of listEntry. Either listEntry points to
  144. // the first entry that is larger than our guid or it points to the root
  145. // of the list (in which case we add it at the end).
  146. //
  147. currentEntry = BinlAllocateMemory( sizeof( MACHINE_INFO ) );
  148. if (currentEntry == NULL) {
  149. *CacheEntry = NULL;
  150. LeaveCriticalSection( &BinlCacheListLock );
  151. return ERROR_NOT_ENOUGH_MEMORY;
  152. }
  153. currentEntry->TimeCreated = GetTickCount();
  154. currentEntry->InProgress = TRUE;
  155. // leave MyClient and EntryExists as FALSE
  156. memcpy( currentEntry->Guid, Guid, BINL_GUID_LENGTH );
  157. InsertTailList( listEntry, &currentEntry->CacheListEntry );
  158. InitializeListHead( &currentEntry->DNsWithSameGuid );
  159. BinlCacheEntriesInUse++;
  160. BinlCacheCount++;
  161. *CacheEntry = currentEntry;
  162. //
  163. // If we're at the max, then go through the entire list to free the
  164. // oldest. We do this here because the loop below doesn't go through
  165. // the whole list.
  166. //
  167. if (BinlCacheCount > BinlGlobalCacheCountLimit) {
  168. PMACHINE_INFO entryToDelete = NULL;
  169. DWORD earliestTime;
  170. listEntry = BinlCacheList.Flink;
  171. while ( listEntry != &BinlCacheList ) {
  172. currentEntry = (PMACHINE_INFO) CONTAINING_RECORD(
  173. listEntry,
  174. MACHINE_INFO,
  175. CacheListEntry );
  176. listEntry = listEntry->Flink;
  177. if (currentEntry->InProgress == FALSE) {
  178. //
  179. // if this one is expired, stop here as we have one to free
  180. //
  181. if ((expireTickCount > 0) &&
  182. (currentEntry->TimeCreated < expireTickCount)) {
  183. entryToDelete = currentEntry;
  184. break;
  185. }
  186. //
  187. // if this one is earlier than the one we previously found,
  188. // remember it.
  189. //
  190. if ((( entryToDelete == NULL) ||
  191. ( currentEntry->TimeCreated < earliestTime)) ) {
  192. entryToDelete = currentEntry;
  193. earliestTime = currentEntry->TimeCreated;
  194. }
  195. }
  196. }
  197. if (entryToDelete) {
  198. BinlFreeCacheEntry( entryToDelete );
  199. BinlPrintDbg((DEBUG_BINL_CACHE, "removed cache entry %x", entryToDelete ));
  200. }
  201. }
  202. LeaveCriticalSection( &BinlCacheListLock );
  203. return ERROR_SUCCESS;
  204. }
  205. VOID
  206. BinlDoneWithCacheEntry (
  207. PMACHINE_INFO CacheEntry,
  208. BOOLEAN FreeIt
  209. )
  210. {
  211. EnterCriticalSection( &BinlCacheListLock );
  212. //
  213. // This one is no longer actively used. See if it's time to set the
  214. // event to tell the terminating thread that everyone is done.
  215. //
  216. CacheEntry->InProgress = FALSE;
  217. BinlCacheEntriesInUse--;
  218. if ((BinlCacheEntriesInUse == 0) && BinlCloseCacheEvent) {
  219. SetEvent( BinlCloseCacheEvent );
  220. }
  221. if (FreeIt) {
  222. BinlFreeCacheEntry( CacheEntry );
  223. }
  224. LeaveCriticalSection( &BinlCacheListLock );
  225. BinlPrintDbg((DEBUG_BINL_CACHE, "binl done with cache entry 0x%x, FreeIt == %s\n",
  226. CacheEntry,
  227. (FreeIt == TRUE) ? "TRUE" : "FALSE" ));
  228. return;
  229. }
  230. VOID
  231. BinlFreeCacheEntry (
  232. PMACHINE_INFO CacheEntry
  233. )
  234. //
  235. // The lock must be held while coming in here. It will be not be freed.
  236. //
  237. {
  238. PLIST_ENTRY p;
  239. PDUP_GUID_DN dupDN;
  240. //
  241. // We're done with this entry. Simply remove it from the list, free it,
  242. // and update the global count. The lock is held, so party on.
  243. //
  244. BinlPrintDbg((DEBUG_BINL_CACHE, "binl freeing cache entry at 0x%x\n", CacheEntry ));
  245. RemoveEntryList( &CacheEntry->CacheListEntry );
  246. if ( CacheEntry->dwFlags & MI_NAME_ALLOC ) {
  247. BinlFreeMemory( CacheEntry->Name );
  248. }
  249. if ( CacheEntry->dwFlags & MI_SETUPPATH_ALLOC ) {
  250. BinlFreeMemory( CacheEntry->SetupPath );
  251. }
  252. if ( CacheEntry->dwFlags & MI_HOSTNAME_ALLOC ) {
  253. BinlFreeMemory( CacheEntry->HostName );
  254. }
  255. if ( CacheEntry->dwFlags & MI_BOOTFILENAME_ALLOC ) {
  256. BinlFreeMemory( CacheEntry->BootFileName );
  257. }
  258. if ( CacheEntry->dwFlags & MI_SAMNAME_ALLOC ) {
  259. BinlFreeMemory( CacheEntry->SamName );
  260. }
  261. if ( CacheEntry->dwFlags & MI_DOMAIN_ALLOC ) {
  262. BinlFreeMemory( CacheEntry->Domain );
  263. }
  264. if ( CacheEntry->dwFlags & MI_SIFFILENAME_ALLOC ) {
  265. BinlFreeMemory( CacheEntry->ForcedSifFileName );
  266. }
  267. if ( CacheEntry->dwFlags & MI_MACHINEDN_ALLOC ) {
  268. BinlFreeMemory( CacheEntry->MachineDN );
  269. }
  270. while (!IsListEmpty(&CacheEntry->DNsWithSameGuid)) {
  271. p = RemoveHeadList(&CacheEntry->DNsWithSameGuid);
  272. dupDN = CONTAINING_RECORD(p, DUP_GUID_DN, ListEntry);
  273. BinlFreeMemory( dupDN );
  274. }
  275. BinlFreeMemory( CacheEntry );
  276. BinlCacheCount--;
  277. return;
  278. }
  279. VOID
  280. BinlCloseCache (
  281. VOID
  282. )
  283. //
  284. // This routine closes down all entries in the DS cache. It waits until all
  285. // threads are done with entries before it returns. It waits for the
  286. // BinlCloseCacheEvent to be set if threads are waiting.
  287. //
  288. {
  289. PLIST_ENTRY listEntry;
  290. EnterCriticalSection( &BinlCacheListLock );
  291. listEntry = BinlCacheList.Flink;
  292. while ( listEntry != &BinlCacheList ) {
  293. DWORD Error;
  294. PMACHINE_INFO cacheEntry;
  295. //
  296. // For each entry in the list, if it's not in use we free it. If it
  297. // is in use, we wait for the thread to finish with it.
  298. //
  299. cacheEntry = (PMACHINE_INFO) CONTAINING_RECORD(
  300. listEntry,
  301. MACHINE_INFO,
  302. CacheListEntry );
  303. if (cacheEntry->InProgress != TRUE) {
  304. listEntry = listEntry->Flink;
  305. BinlFreeCacheEntry( cacheEntry );
  306. continue;
  307. }
  308. if (BinlCloseCacheEvent) {
  309. ResetEvent( BinlCloseCacheEvent );
  310. }
  311. LeaveCriticalSection( &BinlCacheListLock );
  312. //
  313. // Wait for the event signalling that all threads are done with
  314. // the cache
  315. //
  316. if (BinlCloseCacheEvent) {
  317. Error = WaitForSingleObject( BinlCloseCacheEvent, THREAD_TERMINATION_TIMEOUT );
  318. } else {
  319. //
  320. // well, the event that we would wait on isn't there and there's
  321. // still a worker thread using a cache entry, so we just wait
  322. // and then recheck. Yup, this is ugly.
  323. //
  324. Sleep( 10*1000 );
  325. }
  326. EnterCriticalSection( &BinlCacheListLock );
  327. listEntry = BinlCacheList.Flink;
  328. }
  329. LeaveCriticalSection( &BinlCacheListLock );
  330. }
  331. // cache.c eof