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.

905 lines
21 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name :
  4. dirmon.cpp
  5. Abstract:
  6. This module includes definitions of functions and variables
  7. for CDirMonitor and CDirMonitorEntry object
  8. Author:
  9. Charles Grant ( cgrant ) April-1997
  10. Revision History:
  11. Changed to abstract classes to share code between core IIS and ASP
  12. --*/
  13. /************************************************************
  14. * Include Headers
  15. ************************************************************/
  16. #include "isatq.hxx"
  17. #include "malloc.h"
  18. #include "except.h"
  19. #define IATQ_DLL_IMPLEMENTATION
  20. #define IATQ_IMPLEMENTATION_EXPORT
  21. #include "dirmon.h"
  22. //
  23. // CDirMonitorEntry
  24. //
  25. #define DEFAULT_BUFFER_SIZE 512
  26. CDirMonitorEntry::CDirMonitorEntry() :
  27. m_cDirRefCount(0),
  28. m_cIORefCount(0),
  29. m_hDir(INVALID_HANDLE_VALUE),
  30. m_pAtqCtxt(NULL),
  31. m_dwNotificationFlags(0),
  32. m_pszPath(NULL),
  33. m_cPathLength(0),
  34. m_pDirMonitor(NULL),
  35. m_cBufferSize(0),
  36. m_pbBuffer(NULL),
  37. m_fInCleanup(FALSE),
  38. m_fWatchSubdirectories(FALSE)
  39. /*++
  40. Routine Description:
  41. CDirMonitorEntry constructor
  42. Arguments:
  43. None
  44. Return Value:
  45. Nothing
  46. --*/
  47. {
  48. }
  49. CDirMonitorEntry::~CDirMonitorEntry(
  50. VOID
  51. )
  52. /*++
  53. Routine Description:
  54. CDirMonitorEntry destructor
  55. Arguments:
  56. None
  57. Return Value:
  58. Nothing
  59. --*/
  60. {
  61. IF_DEBUG( NOTIFICATION ) {
  62. DBGPRINTF((DBG_CONTEXT, "[CDirMonitorEntry] Destructor\n"));
  63. }
  64. // We should only be destroyed when
  65. // our ref counts have gone to 0
  66. DBG_ASSERT(m_cDirRefCount == 0);
  67. DBG_ASSERT(m_cIORefCount == 0);
  68. //
  69. // We really ought to have closed the handle by now
  70. //
  71. if (m_hDir != INVALID_HANDLE_VALUE) {
  72. DBGPRINTF(( DBG_CONTEXT, "~CDirMonitorEntry: open handle %p : %p\n",
  73. m_hDir, m_pAtqCtxt ));
  74. m_hDir = INVALID_HANDLE_VALUE;
  75. AtqCloseFileHandle(m_pAtqCtxt);
  76. }
  77. if (m_pDirMonitor != NULL)
  78. {
  79. m_pDirMonitor->RemoveEntry(this);
  80. m_pDirMonitor = NULL;
  81. }
  82. m_cPathLength = 0;
  83. if ( m_pszPath != NULL )
  84. {
  85. free( m_pszPath );
  86. m_pszPath = NULL;
  87. }
  88. if (m_pAtqCtxt)
  89. {
  90. AtqFreeContext(m_pAtqCtxt, FALSE);
  91. m_pAtqCtxt = NULL;
  92. }
  93. if (m_pbBuffer != NULL)
  94. {
  95. free(m_pbBuffer);
  96. m_cBufferSize = 0;
  97. }
  98. }
  99. BOOL
  100. CDirMonitorEntry::Init(
  101. DWORD cBufferSize = DEFAULT_BUFFER_SIZE
  102. )
  103. /*++
  104. Routine Description:
  105. Initialize the dir montior entry.
  106. Arguments:
  107. cBufferSize - Initial size of buffer used to store change notifications
  108. Return Value:
  109. TRUE if success, otherwise FALSE
  110. --*/
  111. {
  112. // Don't allow a 0 length buffer
  113. if (cBufferSize == 0)
  114. {
  115. return FALSE;
  116. }
  117. DBG_ASSERT( m_pbBuffer == NULL );
  118. m_pbBuffer = (BYTE *) malloc(cBufferSize);
  119. if (m_pbBuffer != NULL)
  120. {
  121. m_cBufferSize = cBufferSize;
  122. return TRUE;
  123. }
  124. else
  125. {
  126. // Unable to allocate buffer
  127. return FALSE;
  128. }
  129. }
  130. BOOL
  131. CDirMonitorEntry::RequestNotification(
  132. VOID
  133. )
  134. /*++
  135. Routine Description:
  136. Request ATQ to monitor directory changes for the directory handle
  137. associated with this entry
  138. Arguments:
  139. None
  140. Return Value:
  141. TRUE if success, otherwise FALSE
  142. --*/
  143. {
  144. IF_DEBUG( NOTIFICATION ) {
  145. DBGPRINTF((DBG_CONTEXT, "[CDirMonitorEntry] Request change notification\n"));
  146. }
  147. BOOL fResult = FALSE;
  148. DBG_ASSERT(m_pDirMonitor);
  149. // Reset the overlapped io structure
  150. memset(&m_ovr, 0, sizeof(m_ovr));
  151. // Increase the ref count in advance
  152. IOAddRef();
  153. // Request notification of directory changes
  154. fResult = AtqReadDirChanges( m_pAtqCtxt, // Atq context handle
  155. m_pbBuffer, // Buffer for change notifications
  156. m_cBufferSize, // Size of buffer
  157. m_fWatchSubdirectories, // Monitor subdirectories?
  158. m_dwNotificationFlags, // Which changes should we be notified of
  159. &m_ovr ); // Overlapped IO structure
  160. if (!fResult)
  161. {
  162. // ReadDirChanges failed so
  163. // release the ref count we did in advance
  164. // Might cause IO ref count to go to 0
  165. IORelease();
  166. }
  167. return fResult;
  168. }
  169. BOOL
  170. CDirMonitorEntry::Cleanup(
  171. VOID
  172. )
  173. /*++
  174. Routine Description:
  175. Cleans up resource and determines if the caller need to delete
  176. the Directory Monitor Entry instance.
  177. Arguments:
  178. None
  179. Return Value:
  180. TRUE if the caller is responsible for deleting the object
  181. This will be the case if there are no pending Asynch IO requests
  182. --*/
  183. {
  184. DBG_ASSERT(m_cDirRefCount == 0);
  185. BOOL fDeleteNeeded = FALSE;
  186. BOOL fHandleClosed = FALSE;
  187. BOOL fInCleanup = (BOOL) InterlockedExchange((long *) &m_fInCleanup, TRUE);
  188. if (!fInCleanup)
  189. {
  190. // Get the IO ref count BEFORE we close the handle
  191. DWORD cIORefCount = m_cIORefCount;
  192. if (m_hDir != INVALID_HANDLE_VALUE)
  193. {
  194. // If we have a pending AtqReadDirectoryChanges,
  195. // closing the directory handle will cause a call back from ATQ.
  196. // The call back should relase the final refcount on the object
  197. // which should result in its deletion
  198. m_hDir = INVALID_HANDLE_VALUE;
  199. fHandleClosed = AtqCloseFileHandle( m_pAtqCtxt );
  200. }
  201. // If there were no pending Asynch IO operations or if we failed
  202. // to close the handle, then the caller will be responsible for
  203. // deleting this object.
  204. if (cIORefCount == 0 || fHandleClosed == FALSE)
  205. {
  206. fDeleteNeeded = TRUE;
  207. }
  208. }
  209. return fDeleteNeeded;
  210. }
  211. BOOL
  212. CDirMonitorEntry::ResetDirectoryHandle(
  213. VOID
  214. )
  215. /*++
  216. Routine Description:
  217. Opens a new directory handle and ATQ context for the path,
  218. and closes the old ones. We want to be able to do this so we
  219. can change the size of the buffer passed in ReadDirectoryChangesW.
  220. If we are unable to get a new handle or a new ATQ context, we leave
  221. the existing ones in place.
  222. Arguments:
  223. None
  224. Return Value:
  225. TRUE if the handles were succesfully reopened
  226. FALSE otherwise
  227. --*/
  228. {
  229. // We'd better have a directory path available to try this
  230. if (m_pszPath == NULL)
  231. {
  232. return FALSE;
  233. }
  234. // Get a new handle to the directory
  235. HANDLE hDir = CreateFile(
  236. m_pszPath,
  237. FILE_LIST_DIRECTORY,
  238. FILE_SHARE_READ |
  239. FILE_SHARE_WRITE |
  240. FILE_SHARE_DELETE,
  241. NULL,
  242. OPEN_EXISTING,
  243. FILE_FLAG_BACKUP_SEMANTICS |
  244. FILE_FLAG_OVERLAPPED,
  245. NULL );
  246. if ( hDir == INVALID_HANDLE_VALUE )
  247. {
  248. // We couldn't open another handle on the directory,
  249. // leave the current handle and ATQ context alone
  250. return FALSE;
  251. }
  252. // Get a new ATQ context for our new handle
  253. PATQ_CONTEXT pAtqCtxt = NULL;
  254. if ( !AtqAddAsyncHandle(&pAtqCtxt,
  255. NULL,
  256. (LPVOID) this,
  257. (ATQ_COMPLETION) CDirMonitor::DirMonitorCompletionFunction,
  258. INFINITE,
  259. hDir ) )
  260. {
  261. // We couldn't get a new ATQ context. Close our new handle.
  262. // We leave the objects current handle and ATQ context alone
  263. CloseHandle(hDir);
  264. return FALSE;
  265. }
  266. // We have the new handle and ATQ context so we close
  267. // and replace the old ones.
  268. AtqCloseFileHandle(m_pAtqCtxt);
  269. AtqFreeContext(m_pAtqCtxt, FALSE);
  270. m_pAtqCtxt = pAtqCtxt;
  271. m_hDir = hDir;
  272. return TRUE;
  273. }
  274. BOOL
  275. CDirMonitorEntry::SetBufferSize(
  276. DWORD cBufferSize
  277. )
  278. /*++
  279. Routine Description:
  280. Sets the size of the buffer used for storing change notification records
  281. Arguments:
  282. cBufferSize new size for the buffer.
  283. Return Value:
  284. TRUE if the size of the buffer was succesfully set
  285. FALSE otherwise
  286. Note
  287. When a call to ReadDirectoryChangesW is made, the size of the buffer is set in
  288. the data associated with the directory handle and is not changed on subsequent
  289. calls to ReadDirectoryChangesW. To make use of the new buffer size the directory
  290. handle must be closed and a new handle opened (see ResetDirectoryHandle())
  291. --*/
  292. {
  293. // We should never be called if the buffer doesn't already exist
  294. ASSERT(m_pbBuffer);
  295. // Don't allow the buffer to be set to 0
  296. if (cBufferSize == 0)
  297. {
  298. return FALSE;
  299. }
  300. VOID *pbBuffer = realloc(m_pbBuffer, cBufferSize);
  301. if (pbBuffer == NULL)
  302. {
  303. // Re-allocation failed, stuck with the same size buffer
  304. return FALSE;
  305. }
  306. else
  307. {
  308. // Re-allocation succeded, update the member variables
  309. m_pbBuffer = (BYTE *) pbBuffer;
  310. m_cBufferSize = cBufferSize;
  311. return TRUE;
  312. }
  313. }
  314. //
  315. // CDirMonitor
  316. //
  317. CDirMonitor::CDirMonitor()
  318. : CTypedHashTable<CDirMonitor, CDirMonitorEntry, const char*>("DirMon")
  319. /*++
  320. Routine Description:
  321. CDirMonitor constructor
  322. Arguments:
  323. None
  324. Return Value:
  325. Nothing
  326. --*/
  327. {
  328. INITIALIZE_CRITICAL_SECTION( &m_csLock );
  329. INITIALIZE_CRITICAL_SECTION( &m_csSerialComplLock );
  330. m_cRefs = 1;
  331. }
  332. CDirMonitor::~CDirMonitor()
  333. /*++
  334. Routine Description:
  335. CDirMonitor destructor
  336. Arguments:
  337. None
  338. Return Value:
  339. Nothing
  340. --*/
  341. {
  342. DeleteCriticalSection(&m_csLock);
  343. DeleteCriticalSection(&m_csSerialComplLock);
  344. }
  345. BOOL
  346. CDirMonitor::Monitor(
  347. CDirMonitorEntry *pDME,
  348. LPCSTR pszDirectory,
  349. BOOL fWatchSubDirectories,
  350. DWORD dwNotificationFlags
  351. )
  352. /*++
  353. Routine Description:
  354. Create a monitor entry for the specified path
  355. Arguments:
  356. pszDirectory - directory to monitor
  357. pCtxt - Context of path is being monitored
  358. pszDirectory - name of directory to monitor
  359. fWatchSubDirectories - whether to get notifications for subdirectories
  360. dwNotificationFlags - which activities to be notified of
  361. Return Value:
  362. TRUE if success, otherwise FALSE
  363. Remarks:
  364. Caller should have a lock on the CDirMonitor
  365. Not compatible with WIN95
  366. --*/
  367. {
  368. LIST_ENTRY *pEntry;
  369. HANDLE hDirectoryFile = INVALID_HANDLE_VALUE;
  370. BOOL fRet = TRUE;
  371. DWORD dwDirLength = 0;
  372. IF_DEBUG( NOTIFICATION ) {
  373. DBGPRINTF((DBG_CONTEXT, "[CDirMonitor] Monitoring new CDirMonitorEntry\n"));
  374. }
  375. // Must have a directory monitor entry and a string
  376. // containing the directory path
  377. if (!pDME || !pszDirectory)\
  378. {
  379. SetLastError(ERROR_INVALID_PARAMETER);
  380. return FALSE;
  381. }
  382. // Make copy of pszDirectory for the entry to hang on to
  383. pDME->m_cPathLength = strlen(pszDirectory);
  384. if ( !(pDME->m_pszPath = (LPSTR)malloc( pDME->m_cPathLength + 1 )) )
  385. {
  386. pDME->m_cPathLength = 0;
  387. return FALSE;
  388. }
  389. memcpy( pDME->m_pszPath, pszDirectory, pDME->m_cPathLength + 1 );
  390. pDME->Init();
  391. // Open the directory handle
  392. hDirectoryFile = CreateFile(
  393. pszDirectory,
  394. FILE_LIST_DIRECTORY,
  395. FILE_SHARE_READ |
  396. FILE_SHARE_WRITE |
  397. FILE_SHARE_DELETE,
  398. NULL,
  399. OPEN_EXISTING,
  400. FILE_FLAG_BACKUP_SEMANTICS |
  401. FILE_FLAG_OVERLAPPED,
  402. NULL );
  403. if ( hDirectoryFile == INVALID_HANDLE_VALUE )
  404. {
  405. // Cleanup
  406. free(pDME->m_pszPath);
  407. pDME->m_pszPath = NULL;
  408. pDME->m_cPathLength = 0;
  409. return FALSE;
  410. }
  411. else
  412. {
  413. // Store the handle so we can close it on cleanup
  414. pDME->m_hDir = hDirectoryFile;
  415. // Set the flags for the type of notifications we want
  416. // and if we should watch subdirectories or just the root
  417. pDME->m_dwNotificationFlags = dwNotificationFlags;
  418. pDME->m_fWatchSubdirectories = fWatchSubDirectories;
  419. // Get an ATQ context for this handle
  420. // and register our completion call back function
  421. if ( AtqAddAsyncHandle( &pDME->m_pAtqCtxt,
  422. NULL,
  423. (LPVOID) pDME,
  424. (ATQ_COMPLETION) DirMonitorCompletionFunction,
  425. INFINITE,
  426. hDirectoryFile ) )
  427. {
  428. // Insert this entry into the list of active entries
  429. if (InsertEntry(pDME) == LK_SUCCESS)
  430. {
  431. // Ask for notification if this directory has changes
  432. if (!pDME->RequestNotification())
  433. {
  434. // Couldn't register for change notification
  435. // Clean up resources
  436. RemoveEntry(pDME);
  437. pDME->m_hDir = INVALID_HANDLE_VALUE;
  438. AtqCloseFileHandle(pDME->m_pAtqCtxt);
  439. free(pDME->m_pszPath);
  440. pDME->m_pszPath = NULL;
  441. pDME->m_cPathLength = 0;
  442. return FALSE;
  443. }
  444. }
  445. }
  446. else
  447. {
  448. // Failed to add handle to ATQ, clean up
  449. CloseHandle(hDirectoryFile);
  450. pDME->m_hDir = INVALID_HANDLE_VALUE;
  451. free(pDME->m_pszPath);
  452. pDME->m_pszPath = NULL;
  453. pDME->m_cPathLength = 0;
  454. return FALSE;
  455. }
  456. }
  457. return TRUE;
  458. }
  459. VOID
  460. CDirMonitor::DirMonitorCompletionFunction(
  461. PVOID pCtxt,
  462. DWORD dwBytesWritten,
  463. DWORD dwCompletionStatus,
  464. OVERLAPPED *pOvr
  465. )
  466. /*++
  467. Routine Description:
  468. Static member function called by ATQ to signal directory changes
  469. Arguments:
  470. pCtxt - CDirMonitorEntry*
  471. dwBytesWritten - # bytes returned by ReadDirectoryChanges
  472. dwCompletionStatus - status of request to ReadDirectoryChanges
  473. pOvr - OVERLAPPED as specified in call to ReadDirectoryChanges
  474. Return Value:
  475. Nothing
  476. --*/
  477. {
  478. IF_DEBUG( NOTIFICATION ) {
  479. DBGPRINTF((DBG_CONTEXT, "[CDirMonitor] Notification call-back begining. Status %d\n", dwCompletionStatus));
  480. }
  481. CDirMonitorEntry* pDirMonitorEntry = reinterpret_cast<CDirMonitorEntry*>(pCtxt);
  482. DBG_ASSERT(pDirMonitorEntry);
  483. // Safety add ref, this should guarentee that the DME is not deleted
  484. // while we are still processing the callback
  485. pDirMonitorEntry->IOAddRef();
  486. // Release for the current Asynch operation
  487. // Should not send IO ref count to 0
  488. DBG_REQUIRE(pDirMonitorEntry->IORelease());
  489. BOOL fRequestNotification = FALSE;
  490. // There has been a change in the directory we were monitoring
  491. // carry out whatever work we need to do.
  492. if (!pDirMonitorEntry->m_fInCleanup)
  493. {
  494. pDirMonitorEntry->m_pDirMonitor->SerialComplLock();
  495. // BUG Under stress ActOnNotification has been initiating a chain of events
  496. // leading to an AV. For Beta 3 we think we can ignore these AV. For the final
  497. // product we need to rework the critical sections for the template manager and
  498. // the include file table.
  499. TRY
  500. fRequestNotification = pDirMonitorEntry->ActOnNotification(dwCompletionStatus, dwBytesWritten);
  501. CATCH(nExcept)
  502. // We should never get here
  503. DBG_ASSERT(FALSE);
  504. END_TRY
  505. pDirMonitorEntry->m_pDirMonitor->SerialComplUnlock();
  506. }
  507. // If we aren't cleaning up and ActOnNotification returned TRUE
  508. // then make another Asynch notification request. We check m_fInCleanup
  509. // again because ActOnNotification may have caused it to change
  510. if (!pDirMonitorEntry->m_fInCleanup && fRequestNotification)
  511. {
  512. fRequestNotification = pDirMonitorEntry->RequestNotification();
  513. }
  514. // Remove safety ref count, may cause IO ref count to go to 0
  515. pDirMonitorEntry->IORelease();
  516. IF_DEBUG( NOTIFICATION ) {
  517. DBGPRINTF((DBG_CONTEXT, "[CDirMonitor] Notification call-back ending\n"));
  518. }
  519. }
  520. CDirMonitorEntry *
  521. CDirMonitor::FindEntry(
  522. LPCSTR pszPath
  523. )
  524. /*++
  525. Routine Description:
  526. Searches the list of entries for the specified path
  527. Arguments:
  528. pszPath - file path, including file name
  529. Return Value:
  530. pointer to the entry, allready addref'd
  531. --*/
  532. {
  533. DBG_ASSERT(pszPath);
  534. CDirMonitorEntry *pDME = NULL;
  535. FindKey(pszPath, &pDME);
  536. if (pDME)
  537. {
  538. if (pDME->m_fInCleanup)
  539. {
  540. // Don't hand back a DME that is being shutdown
  541. pDME = NULL;
  542. }
  543. else
  544. {
  545. // We found a valid DME which we are going to hand to the caller
  546. pDME->AddRef();
  547. }
  548. }
  549. return pDME;
  550. }
  551. LK_RETCODE
  552. CDirMonitor::InsertEntry(
  553. CDirMonitorEntry *pDME
  554. )
  555. /*++
  556. Routine Description:
  557. Insert an entry into the list of entries for the monitor
  558. Arguments:
  559. pDME - entry to insert
  560. Return Value:
  561. nothing
  562. --*/
  563. {
  564. DBG_ASSERT(pDME);
  565. LK_RETCODE lkResult;
  566. IF_DEBUG( NOTIFICATION ) {
  567. DBGPRINTF((DBG_CONTEXT, "[CDirMonitor] Inserting directory (DME %08x) %s\n", pDME, pDME->m_pszPath));
  568. }
  569. pDME->m_pDirMonitor = this;
  570. // pass a true value for the fOverwrite flag. This allows the new entry
  571. // to replace the previous entry. The previous entry should only be there
  572. // if the app it is associated with is being shutdown and the cleanup of
  573. // the DME records has yet to happen.
  574. lkResult = InsertRecord(pDME, true);
  575. if (lkResult == LK_SUCCESS) {
  576. // AddRef on the DirMonitor object to allow Cleanup to wait for all
  577. // DirMonitorEntries to be removed. The problem arises when duplicates
  578. // are added to the hash table. In this case, only the last entry is
  579. // kept so checking the size of the hash table during shutdown is not
  580. // good enough since the DMEs that were bounced may not have been freed
  581. // yet.
  582. AddRef();
  583. }
  584. return lkResult;
  585. }
  586. LK_RETCODE
  587. CDirMonitor::RemoveEntry(
  588. CDirMonitorEntry *pDME
  589. )
  590. /*++
  591. Routine Description:
  592. Deletes an entry from the list of entries for the monitor
  593. Arguments:
  594. pDME - entry to delete
  595. Return Value:
  596. None
  597. --*/
  598. {
  599. DBG_ASSERT(pDME);
  600. // Release the DME's reference on the DirMonitor object.
  601. Release();
  602. LK_RETCODE lkResult = DeleteKey(pDME->m_pszPath);
  603. pDME->m_pDirMonitor = NULL;
  604. IF_DEBUG( NOTIFICATION ) {
  605. DBGPRINTF((DBG_CONTEXT, "[CDirMonitor] Removed DME(%08x), directory %s\n", pDME, pDME->m_pszPath));
  606. }
  607. return lkResult;
  608. }
  609. BOOL
  610. CDirMonitor::Cleanup(
  611. VOID
  612. )
  613. /*++
  614. Routine Description:
  615. Pauses while all entries are cleaned up
  616. Arguments:
  617. None
  618. Return Value:
  619. None
  620. --*/
  621. {
  622. //BOOL fProperShutdown = FALSE;
  623. // Check that all DME have been released before shutting down
  624. // Sleep a maximum of 30 seconds before shutting down anyway
  625. while (Size() > 0 || m_cRefs != 1)
  626. {
  627. // At least one DME is still active, sleep and try again
  628. Sleep(200);
  629. }
  630. DBGPRINTF((DBG_CONTEXT, "CDirMonitor(%08x): Cleanup, entries remaining %d (Refs = %d)\n", this, Size(),m_cRefs));
  631. #ifdef _DEBUG
  632. // TODO: Use LKHASH iterator
  633. /*if (CHashTable::m_Count)
  634. {
  635. Lock();
  636. CLinkElem *pLink = CHashTable::Head();
  637. DBGPRINTF((DBG_CONTEXT, "Remaining CDirMonitorEntry objects:\n"));
  638. while (pLink)
  639. {
  640. CDirMonitorEntry *pDME = reinterpret_cast<CDirMonitorEntry *>(pLink);
  641. DBGPRINTF((DBG_CONTEXT, "CDirMonitorEntry(%08x), ref count = %d, io refcount = %d", pDME, pDME->m_cDirRefCount, pDME->m_cIORefCount));
  642. pLink = pLink->m_pNext;
  643. }
  644. Unlock();
  645. }
  646. */
  647. #endif //_DEBUG
  648. //DBG_ASSERT(fProperShutdown );
  649. return TRUE;
  650. }
  651. /************************ End of File ***********************/