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.

416 lines
11 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. blkdir.c
  5. Abstract:
  6. This module implements routines for managing cached directory names
  7. Author:
  8. Isaac Heizer
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "blkdir.tmh"
  13. #pragma hdrstop
  14. #define BugCheckFileId SRV_FILE_BLKDIR
  15. BOOLEAN
  16. SrvIsDirectoryCached (
  17. IN PWORK_CONTEXT WorkContext,
  18. IN PUNICODE_STRING DirectoryName
  19. )
  20. {
  21. PLIST_ENTRY listEntry;
  22. PCACHED_DIRECTORY cd;
  23. ULONG directoryNameHashValue;
  24. PCONNECTION connection = WorkContext->Connection;
  25. KIRQL oldIrql;
  26. LARGE_INTEGER timeNow;
  27. //
  28. // DirectoryName must point to memory in nonpaged pool, else we can't touch
  29. // it under spinlock control. If the incomming SMB is UNICODE, we know that
  30. // the name is in the smb buffer, and is therefore in nonpaged pool. Otherwise
  31. // we can't trust it and we're better off just not trying to cache it.
  32. //
  33. if( connection->CachedDirectoryCount == 0 || !SMB_IS_UNICODE( WorkContext ) ) {
  34. return FALSE;
  35. }
  36. KeQueryTickCount( &timeNow );
  37. timeNow.LowPart -= (SrvFiveSecondTickCount >> 1 );
  38. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  39. top:
  40. for ( listEntry = connection->CachedDirectoryList.Flink;
  41. listEntry != &connection->CachedDirectoryList;
  42. listEntry = listEntry->Flink ) {
  43. cd = CONTAINING_RECORD( listEntry, CACHED_DIRECTORY, ListEntry );
  44. //
  45. // Is this element too old?
  46. //
  47. if( cd->TimeStamp < timeNow.LowPart ) {
  48. //
  49. // This element is more than 2.5 seconds old. Toss it out
  50. //
  51. RemoveEntryList( listEntry );
  52. connection->CachedDirectoryCount--;
  53. DEALLOCATE_NONPAGED_POOL( cd );
  54. goto top;
  55. }
  56. if( cd->Tid != WorkContext->TreeConnect->Tid ) {
  57. continue;
  58. }
  59. //
  60. // Is the requested entry a subdir of this cache entry?
  61. //
  62. if( DirectoryName->Length < cd->DirectoryName.Length &&
  63. RtlCompareMemory( DirectoryName->Buffer, cd->DirectoryName.Buffer,
  64. DirectoryName->Length ) == DirectoryName->Length &&
  65. cd->DirectoryName.Buffer[ DirectoryName->Length / sizeof( WCHAR ) ] == L'\\' ) {
  66. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  67. return TRUE;
  68. //
  69. // Not a subdir -- is it an exact match?
  70. //
  71. } else if( DirectoryName->Length == cd->DirectoryName.Length &&
  72. RtlCompareMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer,
  73. DirectoryName->Length ) == DirectoryName->Length ) {
  74. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  75. return TRUE;
  76. }
  77. }
  78. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  79. return FALSE;
  80. }
  81. VOID
  82. SrvCacheDirectoryName (
  83. IN PWORK_CONTEXT WorkContext,
  84. IN PUNICODE_STRING DirectoryName
  85. )
  86. /*++
  87. Routine Description:
  88. This routine remembers 'DirectoryName' for further fast processing of the CheckPath SMB
  89. Arguments:
  90. WorkContext - Pointer to the work context block
  91. DirectoryName - Fully canonicalized name of the directory we're caching
  92. ++*/
  93. {
  94. CLONG blockLength;
  95. PCACHED_DIRECTORY cd;
  96. KIRQL oldIrql;
  97. PCONNECTION connection = WorkContext->Connection;
  98. PLIST_ENTRY listEntry;
  99. LARGE_INTEGER timeNow;
  100. USHORT tid;
  101. if( SrvMaxCachedDirectory == 0 ) {
  102. return;
  103. }
  104. //
  105. // DirectoryName must point to memory in nonpaged pool, else we can't touch
  106. // it under spinlock control. If the incomming SMB is UNICODE, we know that
  107. // the name is in the smb buffer, and is therefore in nonpaged pool. Otherwise
  108. // we can't trust it and we're better off just not trying to cache it.
  109. //
  110. if( !SMB_IS_UNICODE( WorkContext ) ) {
  111. return;
  112. }
  113. KeQueryTickCount( &timeNow );
  114. timeNow.LowPart -= ( SrvFiveSecondTickCount >> 1 );
  115. tid = WorkContext->TreeConnect->Tid;
  116. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  117. //
  118. // Search the directory cache and see if this directory is already cached. If so,
  119. // don't cache it again.
  120. //
  121. top:
  122. for ( listEntry = connection->CachedDirectoryList.Flink;
  123. listEntry != &connection->CachedDirectoryList;
  124. listEntry = listEntry->Flink ) {
  125. cd = CONTAINING_RECORD( listEntry, CACHED_DIRECTORY, ListEntry );
  126. //
  127. // Is this element too old?
  128. //
  129. if( cd->TimeStamp < timeNow.LowPart ) {
  130. //
  131. // This element is more than 2.5 seconds old. Toss it out
  132. //
  133. RemoveEntryList( listEntry );
  134. connection->CachedDirectoryCount--;
  135. DEALLOCATE_NONPAGED_POOL( cd );
  136. goto top;
  137. }
  138. if( cd->Tid != tid ) {
  139. continue;
  140. }
  141. //
  142. // Is the new entry a subdir of this cache entry?
  143. //
  144. if( DirectoryName->Length < cd->DirectoryName.Length &&
  145. RtlCompareMemory( DirectoryName->Buffer, cd->DirectoryName.Buffer,
  146. DirectoryName->Length ) == DirectoryName->Length &&
  147. cd->DirectoryName.Buffer[ DirectoryName->Length / sizeof( WCHAR ) ] == L'\\' ) {
  148. //
  149. // It is a subdir -- no need to cache it again
  150. //
  151. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  152. return;
  153. }
  154. //
  155. // Is the cache entry a subdir of the new entry?
  156. //
  157. if( cd->DirectoryName.Length < DirectoryName->Length &&
  158. RtlCompareMemory( DirectoryName->Buffer, cd->DirectoryName.Buffer,
  159. cd->DirectoryName.Length ) == cd->DirectoryName.Length &&
  160. DirectoryName->Buffer[ cd->DirectoryName.Length / sizeof( WCHAR ) ] == L'\\' ) {
  161. //
  162. // We can remove this entry
  163. //
  164. RemoveEntryList( listEntry );
  165. connection->CachedDirectoryCount--;
  166. DEALLOCATE_NONPAGED_POOL( cd );
  167. //
  168. // We want to cache this new longer entry
  169. //
  170. break;
  171. }
  172. //
  173. // Not a subdir -- is it an exact match?
  174. //
  175. if( cd->DirectoryName.Length == DirectoryName->Length &&
  176. RtlCompareMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer,
  177. DirectoryName->Length ) == DirectoryName->Length ) {
  178. //
  179. // This entry is already in the cache -- no need to recache
  180. //
  181. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  182. return;
  183. }
  184. }
  185. //
  186. // This directory name is not already in the cache. So add it.
  187. //
  188. blockLength = sizeof( CACHED_DIRECTORY ) + DirectoryName->Length + sizeof(WCHAR);
  189. cd = ALLOCATE_NONPAGED_POOL( blockLength, BlockTypeCachedDirectory );
  190. if( cd == NULL ) {
  191. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  192. INTERNAL_ERROR(
  193. ERROR_LEVEL_EXPECTED,
  194. "SrvCacheDirectoryName: Unable to allocate %d bytes from pool",
  195. blockLength,
  196. NULL
  197. );
  198. return;
  199. }
  200. cd->Type = BlockTypeCachedDirectory;
  201. cd->State = BlockStateActive;
  202. cd->Size = (USHORT)blockLength;
  203. // cd->ReferenceCount = 1; // not used
  204. //
  205. // Set the timestamp of this entry. Remember, we subtracted
  206. // ticks up above from timeNow -- put them back in now.
  207. //
  208. cd->TimeStamp = timeNow.LowPart + ( SrvFiveSecondTickCount >> 1 );
  209. //
  210. // Store the directory name as it was passed into us
  211. //
  212. cd->DirectoryName.Length = DirectoryName->Length;
  213. cd->DirectoryName.MaximumLength = (USHORT)DirectoryName->MaximumLength;
  214. cd->DirectoryName.Buffer = (PWCH)(cd + 1);
  215. RtlCopyMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer, DirectoryName->Length );
  216. cd->Tid = tid;
  217. InsertHeadList(
  218. &connection->CachedDirectoryList,
  219. &cd->ListEntry
  220. );
  221. //
  222. // Check the number of elements in the cache. If getting too large, close oldest one.
  223. //
  224. if( connection->CachedDirectoryCount++ < SrvMaxCachedDirectory ) {
  225. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  226. return;
  227. }
  228. //
  229. // Remove the last entry from the cache
  230. //
  231. cd = CONTAINING_RECORD(
  232. connection->CachedDirectoryList.Blink,
  233. CACHED_DIRECTORY,
  234. ListEntry
  235. );
  236. RemoveEntryList( &cd->ListEntry );
  237. connection->CachedDirectoryCount--;
  238. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  239. DEALLOCATE_NONPAGED_POOL( cd );
  240. return;
  241. }
  242. VOID
  243. SrvRemoveCachedDirectoryName(
  244. IN PWORK_CONTEXT WorkContext,
  245. IN PUNICODE_STRING DirectoryName
  246. )
  247. {
  248. PLIST_ENTRY listEntry;
  249. PCACHED_DIRECTORY cd;
  250. ULONG directoryNameHashValue;
  251. PCONNECTION connection = WorkContext->Connection;
  252. KIRQL oldIrql;
  253. USHORT tid;
  254. if( connection->CachedDirectoryCount == 0 ) {
  255. return;
  256. }
  257. //
  258. // DirectoryName must point to memory in nonpaged pool, else we can't touch
  259. // it under spinlock control. If the incomming SMB is UNICODE, we know that
  260. // the name is in the smb buffer, and is therefore in nonpaged pool. Otherwise
  261. // we can't trust it and we're better off just not trying to cache it.
  262. //
  263. if( !SMB_IS_UNICODE( WorkContext ) ) {
  264. return;
  265. }
  266. COMPUTE_STRING_HASH( DirectoryName, &directoryNameHashValue );
  267. tid = WorkContext->TreeConnect->Tid;
  268. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  269. for ( listEntry = connection->CachedDirectoryList.Flink;
  270. listEntry != &connection->CachedDirectoryList;
  271. listEntry = listEntry->Flink ) {
  272. cd = CONTAINING_RECORD( listEntry, CACHED_DIRECTORY, ListEntry );
  273. //
  274. // See if this entry is an exact match for what was requested
  275. //
  276. if( cd->DirectoryName.Length == DirectoryName->Length &&
  277. cd->Tid == tid &&
  278. RtlCompareMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer,
  279. DirectoryName->Length ) == DirectoryName->Length ) {
  280. //
  281. // Remove this entry from the list and adjust the count
  282. //
  283. RemoveEntryList( &cd->ListEntry );
  284. connection->CachedDirectoryCount--;
  285. ASSERT( (LONG)connection->CachedDirectoryCount >= 0 );
  286. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  287. DEALLOCATE_NONPAGED_POOL( cd );
  288. return;
  289. }
  290. }
  291. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  292. return;
  293. }
  294. VOID
  295. SrvCloseCachedDirectoryEntries(
  296. IN PCONNECTION Connection
  297. )
  298. /*++
  299. Routine Description:
  300. This routine closes all the cached directory entries on the connection
  301. Arguments:
  302. Connection - Pointer to the connection structure having the cache
  303. ++*/
  304. {
  305. KIRQL oldIrql;
  306. PCACHED_DIRECTORY cd;
  307. ACQUIRE_SPIN_LOCK( &Connection->SpinLock, &oldIrql );
  308. while( Connection->CachedDirectoryCount > 0 ) {
  309. cd = CONTAINING_RECORD( Connection->CachedDirectoryList.Flink, CACHED_DIRECTORY, ListEntry );
  310. RemoveEntryList( &cd->ListEntry );
  311. Connection->CachedDirectoryCount--;
  312. DEALLOCATE_NONPAGED_POOL( cd );
  313. }
  314. RELEASE_SPIN_LOCK( &Connection->SpinLock, oldIrql );
  315. }