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.

683 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Copyright (C) 1997, Microsoft Corporation.
  4. //
  5. // File: sitesup.c
  6. //
  7. // Contents: Support routines for managing DFS_SITE_INFO entries
  8. //
  9. // Functions: DfsInitSites - Initialize the hash table for DFS_SITE_INFO lookup
  10. // DfsLookupSiteInfo - Lookup a DFS_SITE_INFO
  11. // DfsAllocateSiteInfo - Allocate a DFS_SITE_INFO
  12. // DfsInsertSiteInfo - Put a DFS_SITE_INFO into the table
  13. // DfsDeleteSiteInfo - Remove a DFS_SITE_INFO from the table
  14. // DfsReleaseSiteInfo - Stop using a DFS_SITE_INFO
  15. //
  16. // DfsFsctrlCreateSiteInfo - Load a Site table entry
  17. // DfsFsctrlDeleteSiteInfo - Remove a Site table entry
  18. //
  19. //--------------------------------------------------------------------------
  20. #include "dfsprocs.h"
  21. #include "attach.h"
  22. #include "sitesup.h"
  23. #include "fsctrl.h"
  24. #define Dbg 0x1000
  25. #define DEFAULT_HASH_SIZE 16 // default size of hash table
  26. NTSTATUS
  27. DfsInitSiteInfoHashTable(
  28. IN ULONG cHash,
  29. OUT PSITE_HASH_TABLE *ppHashTable
  30. );
  31. NTSTATUS
  32. DfsAllocateSiteInfo(
  33. IN PUNICODE_STRING pServerName,
  34. IN ULONG SiteCount,
  35. IN PUNICODE_STRING pSiteNames,
  36. OUT PDFS_SITE_INFO *ppSiteInfo
  37. );
  38. PDFS_SITE_INFO
  39. DfsLookupSiteInfo(
  40. IN PUNICODE_STRING ServerName
  41. );
  42. VOID
  43. DfsInsertSiteInfo(
  44. IN PUNICODE_STRING pServerName,
  45. IN PDFS_SITE_INFO pSiteInfo
  46. );
  47. VOID
  48. DfsDeleteSiteInfo(
  49. PDFS_SITE_INFO pSiteInfo
  50. );
  51. VOID
  52. DfsReleaseSiteInfo(
  53. IN PDFS_SITE_INFO pSiteInfo
  54. );
  55. ULONG
  56. DfsHashSiteName(
  57. IN PUNICODE_STRING ServerName,
  58. IN DWORD HashMask
  59. );
  60. #ifdef ALLOC_PRAGMA
  61. #pragma alloc_text(INIT, DfsInitSites)
  62. #pragma alloc_text(PAGE, DfsUninitSites)
  63. #pragma alloc_text(PAGE, DfsInitSiteInfoHashTable)
  64. #pragma alloc_text(PAGE, DfsAllocateSiteInfo)
  65. #pragma alloc_text(PAGE, DfsReleaseSiteInfo)
  66. #pragma alloc_text(PAGE, DfsHashSiteName)
  67. #pragma alloc_text(PAGE, DfsFsctrlCreateSiteInfo)
  68. #pragma alloc_text(PAGE, DfsFsctrlDeleteSiteInfo)
  69. #pragma alloc_text(PAGE, DfsLookupSiteInfo)
  70. #pragma alloc_text(PAGE, DfsInsertSiteInfo)
  71. #pragma alloc_text(PAGE, DfsDeleteSiteInfo)
  72. #endif
  73. #ifdef DBG
  74. VOID
  75. DfsDumpSiteTable(void);
  76. #endif
  77. //+-------------------------------------------------------------------------
  78. //
  79. // Function: DfsInitSiteHashTable - Initialize the DFS_SITE_INFO lookup hash table
  80. //
  81. // Synopsis: This function initializes data structures which are
  82. // used for looking up a DFS_SITE_INFO associated with some site.
  83. //
  84. // Arguments: [cHash] -- Size of the hash table to be allocated. Must be
  85. // a power of two. If zero, a default size is used.
  86. //
  87. // Returns: NTSTATUS -- STATUS_SUCCESS, unless memory allocation
  88. // fails.
  89. //
  90. // Note: The hash buckets are initialized to zero, then later
  91. // initialized to a list head when used. This is a debugging
  92. // aid to determine if some hash buckets are never used.
  93. //
  94. //--------------------------------------------------------------------------
  95. NTSTATUS
  96. DfsInitSiteHashTable(
  97. ULONG cHash,
  98. PSITE_HASH_TABLE *ppHashTable)
  99. {
  100. PSITE_HASH_TABLE pHashTable;
  101. ULONG cbHashTable;
  102. if (cHash == 0) {
  103. cHash = DEFAULT_HASH_SIZE;
  104. }
  105. ASSERT ((cHash & (cHash-1)) == 0); // Assure cHash is a power of two
  106. cbHashTable = sizeof(SITE_HASH_TABLE) + (cHash-1) * sizeof(LIST_ENTRY);
  107. pHashTable = ExAllocatePoolWithTag(NonPagedPool, cbHashTable, ' sfD');
  108. if (pHashTable == NULL) {
  109. return STATUS_NO_MEMORY;
  110. }
  111. pHashTable->NodeTypeCode = DFS_NTC_SITE_HASH;
  112. pHashTable->NodeByteSize = (NODE_BYTE_SIZE) cbHashTable;
  113. pHashTable->HashMask = (cHash-1);
  114. ExInitializeFastMutex( &pHashTable->HashListMutex );
  115. RtlZeroMemory(&pHashTable->HashBuckets[0], cHash * sizeof(LIST_ENTRY));
  116. *ppHashTable = pHashTable;
  117. return(STATUS_SUCCESS);
  118. }
  119. NTSTATUS
  120. DfsInitSites(
  121. ULONG cHash)
  122. {
  123. NTSTATUS status;
  124. status = DfsInitSiteHashTable( cHash, &DfsData.SiteHashTable );
  125. return status;
  126. }
  127. VOID
  128. DfsUninitSites(
  129. VOID)
  130. {
  131. ExFreePool (DfsData.SiteHashTable);
  132. }
  133. //+-------------------------------------------------------------------------
  134. //
  135. // Function: DfsLookupSiteInfo - Lookup a DFS_SITE_INFO in the hash table
  136. //
  137. // Synopsis: This function will lookup a DFS_SITE_INFO.
  138. // It will increment the UseCount on the DFS_SITE_INFO.
  139. //
  140. // Arguments: [ServerName] -- Servername for which the DFS_SITE_INFO is
  141. // being looked up.
  142. //
  143. // Returns: PVOID -- pointer to the DFS_SITE_INFO found, or NULL if none
  144. //
  145. // Algorithm: Knuth would call it hashing with conflict resoulution
  146. // by chaining.
  147. //
  148. // History: 20 Feb 1993 Alanw Created
  149. // 11 Nov 1997 JHarper Modified for sites (used fcbsup.c as template)
  150. //
  151. //--------------------------------------------------------------------------
  152. PDFS_SITE_INFO
  153. DfsLookupSiteInfo(
  154. PUNICODE_STRING ServerName)
  155. {
  156. PLIST_ENTRY pListHead, pLink;
  157. PDFS_SITE_INFO pSiteInfo;
  158. PSITE_HASH_TABLE pHashTable = DfsData.SiteHashTable;
  159. ExAcquireFastMutex( &pHashTable->HashListMutex );
  160. pListHead = &pHashTable->HashBuckets[ DfsHashSiteName(ServerName, pHashTable->HashMask) ];
  161. if ((pListHead->Flink == NULL) || // list not initialized
  162. (pListHead->Flink == pListHead)) { // list empty
  163. ExReleaseFastMutex( &pHashTable->HashListMutex );
  164. return NULL;
  165. }
  166. for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) {
  167. pSiteInfo = CONTAINING_RECORD(pLink, DFS_SITE_INFO, HashChain);
  168. if (RtlCompareUnicodeString(&pSiteInfo->ServerName,ServerName,TRUE) == 0) {
  169. pSiteInfo->UseCount++;
  170. ExReleaseFastMutex( &pHashTable->HashListMutex );
  171. return pSiteInfo;
  172. }
  173. }
  174. ExReleaseFastMutex( &pHashTable->HashListMutex );
  175. return NULL;
  176. }
  177. //+-------------------------------------------------------------------------
  178. //
  179. // Function: DfsInsertSiteInfo - Inserts a DFS_SITE_INFO into the hash table
  180. //
  181. // Synopsis: This function associates a DFS_SITE_INFO to a Server. This
  182. // involves removing any existing entry, and adding the new.
  183. //
  184. // Arguments: [pSiteInfo] -- Pointer to the DFS_SITE_INFO to be inserted.
  185. // [pServerName] -- Pointer to the corresponding server name, used
  186. // as the hash key.
  187. //
  188. // Returns: -nothing-
  189. //
  190. //--------------------------------------------------------------------------
  191. VOID
  192. DfsInsertSiteInfo(
  193. PUNICODE_STRING pServerName,
  194. PDFS_SITE_INFO pSiteInfo)
  195. {
  196. PSITE_HASH_TABLE pHashTable = (PSITE_HASH_TABLE) DfsData.SiteHashTable;
  197. PLIST_ENTRY pListHead;
  198. PDFS_SITE_INFO pExistingSiteInfo;
  199. pExistingSiteInfo = DfsLookupSiteInfo( &pSiteInfo->ServerName);
  200. //
  201. // Put the new one in
  202. //
  203. ExAcquireFastMutex( &pHashTable->HashListMutex );
  204. pListHead = &pHashTable->HashBuckets[ DfsHashSiteName(pServerName, pHashTable->HashMask) ];
  205. if (pListHead->Flink == NULL) {
  206. InitializeListHead(pListHead);
  207. }
  208. InsertHeadList(pListHead, &pSiteInfo->HashChain);
  209. ExReleaseFastMutex( &pHashTable->HashListMutex );
  210. if (pExistingSiteInfo != NULL) {
  211. DfsDeleteSiteInfo(
  212. pExistingSiteInfo);
  213. DfsReleaseSiteInfo(
  214. pExistingSiteInfo);
  215. }
  216. DebugTrace(0, Dbg, "Added SiteInfo %08lx ", pSiteInfo);
  217. DebugTrace(0, Dbg, "For Server %wZ ", pServerName);
  218. }
  219. //+-------------------------------------------------------------------------
  220. //
  221. // Function: DfsDeleteSiteInfo - Delete a DFS_SITE_INFO from the lookup hash table
  222. //
  223. // Synopsis: This function Deletes a DFS_SITE_INFO from the hash table.
  224. //
  225. // Arguments: [pSiteInfo] -- Pointer to the DFS_SITE_INFO to delete
  226. //
  227. // Returns: -nothing-
  228. //
  229. //--------------------------------------------------------------------------
  230. VOID
  231. DfsDeleteSiteInfo(
  232. PDFS_SITE_INFO pSiteInfo)
  233. {
  234. PSITE_HASH_TABLE pHashTable = (PSITE_HASH_TABLE) DfsData.SiteHashTable;
  235. ExAcquireFastMutex( &pHashTable->HashListMutex);
  236. pSiteInfo->Flags |= SITE_INFO_DELETE_PENDING;
  237. RemoveEntryList(&pSiteInfo->HashChain);
  238. InitializeListHead(&pSiteInfo->HashChain);
  239. ExReleaseFastMutex( &pHashTable->HashListMutex );
  240. DebugTrace(0, Dbg, "deleted SiteInfo %08lx ", pSiteInfo);
  241. DebugTrace(0, Dbg, "For server %wZ ", &pSiteInfo->ServerName);
  242. }
  243. //+----------------------------------------------------------------------------
  244. //
  245. // Function: DfsAllocateSiteInfo - Allocate a DFS_SITE_INFO
  246. //
  247. // Synopsis: This function allocates a contiguous DFS_SITE_INFO struct. The
  248. // strings are stored in the allocated buffer after the DFS_SITE_INFO
  249. // structure.
  250. //
  251. // Arguments: [pServerName] -- The server name for the site list
  252. // [ppSiteInfo] -- On successful return, has pointer to newly allocated
  253. // DFS_SITE_INFO.
  254. //
  255. // Returns: [STATUS_SUCCESS] -- Successfully allocated DFS_SITE_INFO
  256. //
  257. // [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory condition
  258. //
  259. //-----------------------------------------------------------------------------
  260. NTSTATUS
  261. DfsAllocateSiteInfo(
  262. PUNICODE_STRING pServerName,
  263. ULONG SiteCount,
  264. PUNICODE_STRING pSiteNames,
  265. PDFS_SITE_INFO *ppSiteInfo)
  266. {
  267. NTSTATUS status;
  268. PDFS_SITE_INFO pSiteInfo;
  269. ULONG Size;
  270. ULONG i;
  271. LPWSTR pwCh;
  272. PUNICODE_STRING pustr;
  273. if (SiteCount < 1) {
  274. DebugTrace(0, Dbg, "DfsAllocateSiteInfo SiteCount = %d\n", ULongToPtr( SiteCount ));
  275. return STATUS_INSUFFICIENT_RESOURCES;
  276. }
  277. DebugTrace(0, Dbg, "DfsAllocateSiteInfo(%wZ)\n", pServerName);
  278. //
  279. // Size the buffer - include storage for the unicode strings after the
  280. // DFS_SITE_INFO structure.
  281. //
  282. Size = sizeof(DFS_SITE_INFO) +
  283. pServerName->Length +
  284. sizeof(UNICODE_STRING) * (SiteCount - 1);
  285. for (i = 0; i < SiteCount; i++) {
  286. Size += pSiteNames[i].Length;
  287. }
  288. pSiteInfo = (PDFS_SITE_INFO) ExAllocatePoolWithTag( NonPagedPool, Size, ' sfD' );
  289. if (pSiteInfo != NULL) {
  290. RtlZeroMemory( pSiteInfo, Size );
  291. pSiteInfo->NodeTypeCode = DFS_NTC_SITE_INFO;
  292. pSiteInfo->NodeByteSize = (USHORT)Size;
  293. pwCh = (LPWSTR) &pSiteInfo->SiteName[SiteCount];
  294. pustr = &pSiteInfo->ServerName;
  295. pustr->Length = pustr->MaximumLength = pServerName->Length;
  296. pustr->Buffer = pwCh;
  297. RtlCopyMemory(pwCh, pServerName->Buffer, pServerName->Length);
  298. pwCh += pustr->Length / sizeof(WCHAR);
  299. pSiteInfo->SiteCount = SiteCount;
  300. for (i = 0; i < SiteCount; i++) {
  301. pustr = &pSiteInfo->SiteName[i];
  302. pustr->Length = pustr->MaximumLength = pSiteNames[i].Length;
  303. pustr->Buffer = pwCh;
  304. RtlCopyMemory(pwCh, pSiteNames[i].Buffer, pSiteNames[i].Length);
  305. pwCh += pustr->Length / sizeof(WCHAR);
  306. }
  307. *ppSiteInfo = pSiteInfo;
  308. status = STATUS_SUCCESS;
  309. DebugTrace(0, Dbg, "DfsAllocateSiteInfo pSiteInfo = %d\n", pSiteInfo);
  310. } else {
  311. status = STATUS_INSUFFICIENT_RESOURCES;
  312. }
  313. return( status );
  314. }
  315. //+----------------------------------------------------------------------------
  316. //
  317. // Function: DfsReleaseSiteInfo
  318. //
  319. // Synopsis: Decrements UseCount of and possibly frees a DFS_SITE_INFO
  320. //
  321. // Arguments: [pSiteInfo] -- The DFS_SITE_INFO to release
  322. //
  323. // Returns: Nothing
  324. //
  325. //-----------------------------------------------------------------------------
  326. VOID
  327. DfsReleaseSiteInfo(
  328. PDFS_SITE_INFO pSiteInfo)
  329. {
  330. PSITE_HASH_TABLE pHashTable = (PSITE_HASH_TABLE) DfsData.SiteHashTable;
  331. //
  332. // There's a potential race with DfsDeleteSiteInfo's setting of the
  333. // DELETE_PENDING and the test below of DELETE_PENDING, so we still have
  334. // to acquire the lock to safely test the DELETE_PENDING bit.
  335. //
  336. ExAcquireFastMutex( &pHashTable->HashListMutex );
  337. pSiteInfo->UseCount--;
  338. if ((pSiteInfo->Flags & SITE_INFO_DELETE_PENDING) != 0 && pSiteInfo->UseCount == 0) {
  339. ExFreePool(pSiteInfo);
  340. }
  341. ExReleaseFastMutex( &pHashTable->HashListMutex );
  342. }
  343. //+----------------------------------------------------------------------------
  344. //
  345. // Function: DfsHashSiteNames
  346. //
  347. // Synopsis: Generates a hash 0-N - ignores case
  348. //
  349. // Arguments: [pServerName] -- The Server name to hash
  350. //
  351. // Returns: Nothing
  352. //
  353. // Notes: Might need to convert DNS-style names to short names (??)
  354. //
  355. //-----------------------------------------------------------------------------
  356. ULONG
  357. DfsHashSiteName(
  358. PUNICODE_STRING ServerName,
  359. DWORD HashMask)
  360. {
  361. ULONG BucketNo = 0;
  362. WCHAR *pBuffer = ServerName->Buffer;
  363. WCHAR *pBufferEnd = &pBuffer[ServerName->Length / sizeof(WCHAR)];
  364. ULONG wCh;
  365. BucketNo = 0;
  366. while (pBuffer != pBufferEnd) {
  367. wCh = (*pBuffer < L'a')
  368. ? *pBuffer
  369. : ((*pBuffer < L'z')
  370. ? (*pBuffer - L'a' + L'A')
  371. : RtlUpcaseUnicodeChar(*pBuffer));
  372. BucketNo *= 131;
  373. BucketNo += wCh;
  374. pBuffer++;
  375. }
  376. BucketNo = BucketNo & HashMask;
  377. return BucketNo;
  378. }
  379. //+-------------------------------------------------------------------------
  380. //
  381. // Function: DfsFsctrlCreateSiteInfo, public
  382. //
  383. // Synopsis:
  384. //
  385. // Arguments:
  386. //
  387. // Returns:
  388. //
  389. // Notes: We only process this FSCTRL from the file system process,
  390. // never from the driver.
  391. //
  392. //--------------------------------------------------------------------------
  393. NTSTATUS
  394. DfsFsctrlCreateSiteInfo(
  395. PIRP Irp,
  396. PVOID InputBuffer,
  397. ULONG InputBufferLength)
  398. {
  399. NTSTATUS status = STATUS_SUCCESS;
  400. PDFS_CREATE_SITE_INFO_ARG arg;
  401. PDFS_SITE_INFO pSiteInfo;
  402. ULONG i;
  403. ULONG Size;
  404. STD_FSCTRL_PROLOGUE(DfsFsctrlCreateSiteInfo, TRUE, FALSE);
  405. if (InputBufferLength < sizeof(DFS_CREATE_SITE_INFO_ARG)) {
  406. status = STATUS_INVALID_PARAMETER;
  407. goto exit_with_status;
  408. }
  409. //
  410. // unmarshal the arguments...
  411. //
  412. arg = (PDFS_CREATE_SITE_INFO_ARG) InputBuffer;
  413. Size = InputBufferLength - FIELD_OFFSET(DFS_CREATE_SITE_INFO_ARG, SiteName);
  414. if ( (Size / sizeof(arg->SiteName[0])) < arg->SiteCount ) {
  415. status = STATUS_INVALID_PARAMETER;
  416. goto exit_with_status;
  417. }
  418. OFFSET_TO_POINTER(arg->ServerName.Buffer, arg);
  419. if (!UNICODESTRING_IS_VALID(arg->ServerName, InputBuffer, InputBufferLength)) {
  420. status = STATUS_INVALID_PARAMETER;
  421. goto exit_with_status;
  422. }
  423. for (i = 0; i < arg->SiteCount; i++) {
  424. OFFSET_TO_POINTER(arg->SiteName[i].Buffer, arg);
  425. if (!UNICODESTRING_IS_VALID(arg->SiteName[i], InputBuffer, InputBufferLength)) {
  426. status = STATUS_INVALID_PARAMETER;
  427. goto exit_with_status;
  428. }
  429. }
  430. if (NT_SUCCESS(status)) {
  431. status = DfsAllocateSiteInfo(
  432. &arg->ServerName,
  433. arg->SiteCount,
  434. &arg->SiteName[0],
  435. &pSiteInfo);
  436. if (NT_SUCCESS(status)) {
  437. DfsInsertSiteInfo(
  438. &arg->ServerName,
  439. pSiteInfo);
  440. }
  441. }
  442. exit_with_status:
  443. DfsCompleteRequest( Irp, status );
  444. DebugTrace(-1, Dbg,
  445. "DfsFsctrlCreateSiteInfo: Exit -> %08lx\n", ULongToPtr( status ) );
  446. return status;
  447. }
  448. //+-------------------------------------------------------------------------
  449. //
  450. // Function: DfsFsctrlDeleteSiteInfo, public
  451. //
  452. // Synopsis:
  453. //
  454. // Arguments:
  455. //
  456. // Returns:
  457. //
  458. // Notes: We only process this FSCTRL from the file system process,
  459. // never from the driver.
  460. //
  461. //--------------------------------------------------------------------------
  462. NTSTATUS
  463. DfsFsctrlDeleteSiteInfo(
  464. PIRP Irp,
  465. PVOID InputBuffer,
  466. ULONG InputBufferLength)
  467. {
  468. NTSTATUS status = STATUS_SUCCESS;
  469. PDFS_DELETE_SITE_INFO_ARG arg;
  470. PDFS_SITE_INFO pSiteInfo;
  471. STD_FSCTRL_PROLOGUE(DfsFsctrlDeleteSiteInfo, TRUE, FALSE);
  472. if (InputBufferLength < sizeof(DFS_DELETE_SITE_INFO_ARG)) {
  473. status = STATUS_INVALID_PARAMETER;
  474. goto exit_with_status;
  475. }
  476. //
  477. // unmarshal the arguments...
  478. //
  479. arg = (PDFS_DELETE_SITE_INFO_ARG) InputBuffer;
  480. OFFSET_TO_POINTER(arg->ServerName.Buffer, arg);
  481. if (!UNICODESTRING_IS_VALID(arg->ServerName, InputBuffer, InputBufferLength)) {
  482. status = STATUS_INVALID_PARAMETER;
  483. goto exit_with_status;
  484. }
  485. pSiteInfo = DfsLookupSiteInfo(
  486. &arg->ServerName);
  487. //
  488. // The DfsLookupSiteInfo() call bumped the usecount, so we're sure pSiteInfo
  489. // won't become invalid as we're using it.
  490. //
  491. if (pSiteInfo != NULL) {
  492. //
  493. // Removes from the table, but doesn't free the memory
  494. //
  495. DfsDeleteSiteInfo(
  496. pSiteInfo);
  497. //
  498. // This will decrement the usecount, and if it goes to zero, frees the memory
  499. //
  500. DfsReleaseSiteInfo(
  501. pSiteInfo);
  502. }
  503. exit_with_status:
  504. DfsCompleteRequest( Irp, status );
  505. DebugTrace(-1, Dbg,
  506. "DfsFsctrlDeleteSiteInfo: Exit -> %08lx\n", ULongToPtr( status ) );
  507. return status;
  508. }
  509. #ifdef DBG
  510. VOID
  511. DfsDumpSiteTable(void)
  512. {
  513. PLIST_ENTRY pListHead, pLink;
  514. PDFS_SITE_INFO pSiteInfo;
  515. PSITE_HASH_TABLE pHashTable = DfsData.SiteHashTable;
  516. ULONG i, j;
  517. DbgPrint("---------Site Table----------\n");
  518. for (i = 0; i <= pHashTable->HashMask; i++) {
  519. pListHead = &pHashTable->HashBuckets[i];
  520. if ((pListHead->Flink == NULL) || // list not initialized
  521. (pListHead->Flink == pListHead)) { // list empty
  522. continue;
  523. }
  524. for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) {
  525. pSiteInfo = CONTAINING_RECORD(pLink, DFS_SITE_INFO, HashChain);
  526. DbgPrint("\t[%02d][%wZ][%d]", i, &pSiteInfo->ServerName, pSiteInfo->SiteCount);
  527. for (j = 0; j < pSiteInfo->SiteCount; j++) {
  528. DbgPrint("(%wZ)", &pSiteInfo->SiteName[j]);
  529. }
  530. DbgPrint("\n");
  531. }
  532. }
  533. DbgPrint("-----------------------------\n");
  534. }
  535. #endif