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.

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