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.

407 lines
14 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+============================================================================
  3. //
  4. // oidindex.cxx
  5. //
  6. // Implementation of CObjIdIndexChangeNotifier, which moniters the
  7. // object ID index for changes. When a change is received, the
  8. // CVolume is notified.
  9. //
  10. //+============================================================================
  11. #include <pch.cxx>
  12. #pragma hdrstop
  13. #include "trkwks.hxx"
  14. //+----------------------------------------------------------------------------
  15. //
  16. // CObjIdIndexChangeNotifier::DoWork
  17. //
  18. // Called by the work manager - on the primary thread - when we've received
  19. // a notification from the object ID index.
  20. //
  21. //+----------------------------------------------------------------------------
  22. void
  23. CObjIdIndexChangeNotifier::DoWork()
  24. {
  25. LPBYTE pbScan = _Buffer;
  26. FILE_NOTIFY_INFORMATION * pNotifyInfo;
  27. // Get the size and ntstatus of the notification.
  28. DWORD dwNumberOfBytesTransfered = static_cast<DWORD>(_Overlapped.InternalHigh);
  29. NTSTATUS status = static_cast<NTSTATUS>(_Overlapped.Internal);
  30. _cs.Enter();
  31. __try // __except
  32. {
  33. // Is this a good notification?
  34. if( NT_SUCCESS(status) )
  35. {
  36. // Did we get data in this notification?
  37. if( dwNumberOfBytesTransfered >= sizeof(FILE_NOTIFY_INFORMATION) )
  38. {
  39. // Yes. Loop through the entries, calling to special handlers
  40. // for delete notifications and tunnelling-failure notifications.
  41. do
  42. {
  43. pNotifyInfo = (FILE_NOTIFY_INFORMATION*)pbScan;
  44. FILE_OBJECTID_INFORMATION *poi = (FILE_OBJECTID_INFORMATION*) pNotifyInfo->FileName;
  45. TrkLog((TRKDBG_OBJID_DELETIONS,
  46. TEXT("NTFS ObjId Index: %s"),
  47. (const TCHAR*)CDebugString( _pVolume->GetVolIndex(),
  48. pNotifyInfo) ));
  49. // Check for adds/deletes
  50. if (pNotifyInfo->Action == FILE_ACTION_REMOVED_BY_DELETE
  51. ||
  52. pNotifyInfo->Action == FILE_ACTION_ADDED)
  53. {
  54. // Notify the general add/deletions handler
  55. CDomainRelativeObjId droidBirth( *poi );
  56. _pObjIdIndexChangedCallback->NotifyAddOrDelete( pNotifyInfo->Action, droidBirth );
  57. }
  58. // Check for tunnelling notifications
  59. else
  60. if (pNotifyInfo->Action == FILE_ACTION_ID_NOT_TUNNELLED)
  61. {
  62. // An attempt to tunnel an object ID failed because another file on the
  63. // same volume was already using it.
  64. _pVolume->FileActionIdNotTunnelled( (FILE_OBJECTID_INFORMATION*) pNotifyInfo->FileName );
  65. }
  66. } while ( pNotifyInfo->NextEntryOffset != 0 &&
  67. ( pbScan += pNotifyInfo->NextEntryOffset) );
  68. } // if( dwNumberOfBytesTransfered >= sizeof(FILE_NOTIFY_INFORMATION) )
  69. // We didn't get any data. Is this notification telling us that the IRP was
  70. // cancelled?
  71. else if( STATUS_NOTIFY_CLEANUP == status )
  72. {
  73. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("OverlappedCompletionRoutine on %c: cleaning up"),
  74. VolChar( _pVolume->GetIndex() ) ));
  75. }
  76. } // if( NT_SUCCESS(status) )
  77. else if( STATUS_CANCELLED == status )
  78. {
  79. // The thread on which we made the ReadDirectoryChanges call terminated,
  80. // thus terminating our IRP. We should now be running on an IO thread, since
  81. // we register with WT_EXECUTEINIOTHREAD, so we just fall through and
  82. // re-issue the IRP.
  83. TrkLog(( TRKDBG_OBJID_DELETIONS,
  84. TEXT("OverlappedCompletionRoutine on %c: ignoring status_cancelled"),
  85. VolChar( _pVolume->GetIndex() ) ));
  86. }
  87. else
  88. {
  89. // If we failed for any other reason, there's something wrong. We don't
  90. // want to call ReadDirectoryChanges again, because it might give us the
  91. // same failure right away, and we'd thus be in an infinite loop.
  92. TrkLog(( TRKDBG_ERROR, TEXT("OverlappedCompletionRoutine on %c: aborting due to %08x"),
  93. status ));
  94. CloseHandle( _hDir );
  95. _hDir = INVALID_HANDLE_VALUE;
  96. }
  97. // When StopListeningAndClose is called, CancelIo is called, which triggers
  98. // this DoWork routine. But we don't run until we get the critical section,
  99. // after which time _hDir will be invalid.
  100. if( INVALID_HANDLE_VALUE != _hDir )
  101. {
  102. StartListening();
  103. }
  104. }
  105. __except( EXCEPTION_EXECUTE_HANDLER )
  106. {
  107. // We should never get any kind of an error here. If we do, the simplest
  108. // recourse is just to reinit the volume.
  109. _pVolume->OnHandlesMustClose();
  110. }
  111. _cs.Leave();
  112. return;
  113. }
  114. //+----------------------------------------------------------------------------
  115. //
  116. // CObjIdIndexChangeNotifier::Initialize
  117. //
  118. // Initialize the critical section and register with the work manager.
  119. //
  120. //+----------------------------------------------------------------------------
  121. void
  122. CObjIdIndexChangeNotifier::Initialize(
  123. TCHAR *ptszVolumeDeviceName,
  124. PObjIdIndexChangedCallback * pObjIdIndexChangedCallback,
  125. CVolume * pVolumeForTunnelNotification
  126. )
  127. {
  128. TrkAssert( !_fInitialized );
  129. _cs.Initialize();
  130. _fInitialized = TRUE;
  131. _ptszVolumeDeviceName = ptszVolumeDeviceName;
  132. _pObjIdIndexChangedCallback = pObjIdIndexChangedCallback;
  133. _hDir = INVALID_HANDLE_VALUE;
  134. _pVolume = pVolumeForTunnelNotification;
  135. TrkAssert( NULL == _hCompletionEvent );
  136. _hCompletionEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); // Auto-reset, not signaled
  137. if( NULL == _hCompletionEvent )
  138. {
  139. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create completion event for objid index change notify (%lu)"),
  140. GetLastError() ));
  141. TrkRaiseLastError();
  142. }
  143. // Register the completion event with the thread pool.
  144. _hRegisterWaitForSingleObjectEx
  145. = TrkRegisterWaitForSingleObjectEx( _hCompletionEvent, ThreadPoolCallbackFunction,
  146. static_cast<PWorkItem*>(this), INFINITE,
  147. WT_EXECUTEINWAITTHREAD );
  148. if( NULL == _hRegisterWaitForSingleObjectEx )
  149. {
  150. TrkLog(( TRKDBG_ERROR, TEXT("Failed RegisterWaitForSingleObjectEx in CObjIdIndexChangeNotifier (%lu) for %s"),
  151. GetLastError(), ptszVolumeDeviceName ));
  152. TrkRaiseLastError();
  153. }
  154. else
  155. TrkLog(( TRKDBG_VOLUME, TEXT("Registered objid index change notification (%p)"),
  156. _hRegisterWaitForSingleObjectEx ));
  157. }
  158. //+----------------------------------------------------------------------------
  159. //
  160. // CObjIdIndexChangeNotifier::StartListening
  161. //
  162. // Call ReadDirectoryChangesW on the handle to the object ID index.
  163. // This is an event-based async call, so it returns immediately, and
  164. // NTFS signals the event when there's a notification ready.
  165. //
  166. //+----------------------------------------------------------------------------
  167. void
  168. CObjIdIndexChangeNotifier::StartListening()
  169. {
  170. // NTFS will write the notification into the _Overlapped structure.
  171. memset(&_Overlapped, 0, sizeof(_Overlapped));
  172. _Overlapped.hEvent = _hCompletionEvent;
  173. _Overlapped.Internal = STATUS_INTERNAL_ERROR;
  174. if (!ReadDirectoryChangesW( _hDir,
  175. _Buffer, // pointer to the buffer to receive the read results
  176. sizeof(_Buffer), // length of lpBuffer
  177. FALSE, // flag for monitoring directory or directory tree
  178. FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions to watch for
  179. &_dwDummyBytesReturned, // number of bytes returned
  180. &_Overlapped, // pointer to structure needed for overlapped I/O
  181. NULL )) // pointer to completion routine
  182. {
  183. CloseHandle(_hDir);
  184. _hDir = INVALID_HANDLE_VALUE;
  185. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("AsyncListen failed to ReadDirectoryChanges %d"),
  186. GetLastError() ));
  187. TrkRaiseLastError();
  188. }
  189. // Ordinarily, the previous call will leave an IO pending. If, however,
  190. // it actually returns right away with data, set the event as if the data came
  191. // back async.
  192. if( GetOverlappedResult( _hDir, &_Overlapped, &_dwDummyBytesReturned, FALSE /*Don't Wait*/ ))
  193. {
  194. // There was data immediately available. Handle it in the normal way.
  195. TrkVerify( SetEvent( _Overlapped.hEvent ));
  196. }
  197. else if( ERROR_IO_INCOMPLETE != GetLastError() ) // STATUS_PENDING
  198. {
  199. // This should never occur
  200. TrkLog(( TRKDBG_ERROR, TEXT("GetOverlappedResult failed in CObjIdIndexChangeNotifier::AsyncListen (%lu)"),
  201. GetLastError() ));
  202. TrkRaiseLastError();
  203. }
  204. }
  205. //+----------------------------------------------------------------------------
  206. //
  207. // CObjIdIndexChangeNotifier::AsyncListen
  208. //
  209. // This method begins listening for changes to the NTFS object ID index
  210. // directory. It does not block; when notifications are available an
  211. // event is signaled and handled in DoWork.
  212. //
  213. //+----------------------------------------------------------------------------
  214. BOOL
  215. CObjIdIndexChangeNotifier::AsyncListen( )
  216. {
  217. TCHAR tszDirPath[MAX_PATH];
  218. BOOL fStartedListening = FALSE;
  219. _cs.Enter();
  220. __try // __finally
  221. {
  222. if( INVALID_HANDLE_VALUE != _hDir )
  223. {
  224. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("CObjIdIndexChangeNotifier already listening to %s:"),
  225. _ptszVolumeDeviceName ));
  226. __leave;
  227. }
  228. _tcscpy( tszDirPath, _ptszVolumeDeviceName );
  229. _tcscat( tszDirPath, TEXT("\\$Extend\\$ObjId:$O:$INDEX_ALLOCATION") );
  230. //
  231. // Should use TrkCreateFile and NtNotifyChangeDirectoryFile
  232. // but NtNotifyChangeDirectoryFile means writing an APC routine
  233. // so I'm punting for now.
  234. // None of these Win32 error codess need to be raised to the user.
  235. //
  236. _hDir = CreateFile (
  237. tszDirPath,
  238. FILE_LIST_DIRECTORY,
  239. FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
  240. NULL,
  241. OPEN_EXISTING,
  242. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
  243. NULL );
  244. if (_hDir == INVALID_HANDLE_VALUE)
  245. {
  246. TrkLog((TRKDBG_OBJID_DELETIONS,
  247. TEXT("AsyncListen failed to open objid index %s %d"),
  248. tszDirPath,
  249. GetLastError()));
  250. TrkRaiseLastError();
  251. }
  252. StartListening(); // Call ReadDirectoryChangesW
  253. fStartedListening = TRUE;
  254. TrkLog((TRKDBG_OBJID_DELETIONS,
  255. TEXT("AsyncListen succeeded ReadDirectoryChanges on %c:"), VolChar(_pVolume->GetIndex()) ));
  256. }
  257. __finally
  258. {
  259. _cs.Leave();
  260. }
  261. return fStartedListening;
  262. }
  263. //+----------------------------------------------------------------------------
  264. //
  265. // CObjIdIndexChangeNotifier::StopListeningAndClose
  266. //
  267. // Cancel the previous call to ReadDirectoryChangesW, and close the
  268. // handle to the object ID index directory.
  269. //
  270. //+----------------------------------------------------------------------------
  271. void
  272. CObjIdIndexChangeNotifier::StopListeningAndClose()
  273. {
  274. if( !_fInitialized )
  275. return;
  276. _cs.Enter();
  277. TrkLog((TRKDBG_OBJID_DELETIONS, TEXT("StopListeningAndClose() on %c:"),
  278. VolChar(_pVolume->GetIndex())));
  279. // Cancel the IO, which will trigger once last completion with
  280. // STATUS_NOTIFY_CLEANUP (why isn't it STATUS_CANCELLED?)
  281. // Note that this one last completion will see that _hDir
  282. // has been closed, and won't attempt to re-use it.
  283. InterlockedCloseHandle( &_hDir, INVALID_HANDLE_VALUE, TRUE );
  284. _cs.Leave();
  285. }
  286. //+----------------------------------------------------------------------------
  287. //
  288. // CObjIdIndexChangeNotifier::UnInitialize
  289. //
  290. // Cancel the notification IRP and close the handle to the
  291. // object ID index directory.
  292. //
  293. //+----------------------------------------------------------------------------
  294. void
  295. CObjIdIndexChangeNotifier::UnInitialize()
  296. {
  297. if( _fInitialized )
  298. {
  299. StopListeningAndClose();
  300. // Unregister from the thread pool. This must be done before closing
  301. // _hCompletionEvent, because that's the event on which the thread
  302. // pool is waiting.
  303. if( NULL != _hRegisterWaitForSingleObjectEx )
  304. {
  305. if( !TrkUnregisterWait( _hRegisterWaitForSingleObjectEx ))
  306. {
  307. TrkLog(( TRKDBG_ERROR, TEXT("Failed UnregisterWait for CObjIdIndexChangeNotifier (%lu)"),
  308. GetLastError() ));
  309. }
  310. else
  311. TrkLog(( TRKDBG_VOLUME, TEXT("Unregistered wait for CObjIdIndexChangeNotifier (%p)"),
  312. _hRegisterWaitForSingleObjectEx ));
  313. _hRegisterWaitForSingleObjectEx = NULL;
  314. }
  315. if( NULL != _hCompletionEvent )
  316. {
  317. CloseHandle( _hCompletionEvent );
  318. _hCompletionEvent = NULL;
  319. }
  320. // Delete the critical section. This must be done after unregistering from
  321. // the thread pool, because until that time we have to worry about a thread
  322. // coming in to DoWork.
  323. _cs.UnInitialize();
  324. _fInitialized = FALSE;
  325. }
  326. }