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.

540 lines
18 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 2000
  5. //
  6. // File: usntree.cxx
  7. //
  8. // Contents: Tree traversal for usn scopes
  9. //
  10. // History: 07-May-97 SitaramR Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <pch.cxx>
  14. #pragma hdrstop
  15. #include <ntopen.hxx>
  16. #include <pathpars.hxx>
  17. #include <cifrmcom.hxx>
  18. #include <funypath.hxx>
  19. #include "cicat.hxx"
  20. #include "usntree.hxx"
  21. #include "usnmgr.hxx"
  22. //+---------------------------------------------------------------------------
  23. //
  24. // Member: CUsnTreeTraversal::CUsnTreeTraversal
  25. //
  26. // Synopsis: Constructor
  27. //
  28. // Arguments: [cicat] -- Catalog
  29. // [ciManager] -- CI manager
  30. // [lcaseFunnyRootPath] -- Root of scope
  31. // [fDoDeletions] -- Should deletions be done ?
  32. // [fAbort] -- Abort flag
  33. // [fProcessRoot] -- Process root ?
  34. // [volumeId] -- Volume id
  35. // [usnLow] -- Ignore files with USN < [usnLow]
  36. // [usnHigh] -- Ignore files with USN > [usnHigh]
  37. // [fUserInitiated] -- TRUE if the user asked for this
  38. //
  39. // History: 07-May-97 SitaramR Created
  40. //
  41. //----------------------------------------------------------------------------
  42. CUsnTreeTraversal::CUsnTreeTraversal( CiCat& cicat,
  43. CUsnMgr & usnMgr,
  44. ICiManager & ciManager,
  45. const CLowerFunnyPath & lcaseFunnyRootPath,
  46. BOOL fDoDeletions,
  47. BOOL & fAbort,
  48. BOOL fProcessRoot,
  49. VOLUMEID volumeId,
  50. USN const & usnLow,
  51. USN const & usnHigh,
  52. BOOL fUserInitiated )
  53. : CTraverse( cicat, fAbort, fProcessRoot ),
  54. _usnLow( usnLow ),
  55. _usnHigh( usnHigh ),
  56. _cicat(cicat),
  57. _usnMgr( usnMgr ),
  58. _ciManager(ciManager),
  59. _cDoc(0),
  60. _fDoDeletions(fDoDeletions),
  61. _volumeId(volumeId),
  62. _cProcessed(0),
  63. _fUserInitiated( fUserInitiated )
  64. {
  65. BOOL fRootDeleted = FALSE;
  66. DWORD dw = GetFileAttributes( lcaseFunnyRootPath.GetPath() );
  67. if ( 0xffffffff == dw )
  68. {
  69. DWORD dwErr = GetLastError();
  70. ciDebugOut(( DEB_WARN,
  71. "CUpdate::CUpdate(b) can't get attrinfo: %d, for %ws\n",
  72. dwErr, lcaseFunnyRootPath.GetPath() ));
  73. if ( ERROR_FILE_NOT_FOUND == dwErr || ERROR_PATH_NOT_FOUND == dwErr )
  74. fRootDeleted = TRUE;
  75. }
  76. _docList.SetPartId ( 1 );
  77. FILETIME fTime;
  78. _cicat.StartUpdate( &fTime, lcaseFunnyRootPath.GetActualPath(), fDoDeletions, eUsnsArray );
  79. // Make sure EndProcessing deletes all the files in the scope
  80. if ( fRootDeleted )
  81. {
  82. _status = STATUS_NO_MORE_FILES;
  83. return;
  84. }
  85. ciDebugOut(( DEB_ITRACE, "usntree: usnlow %#I64x, usnHigh %I64x\n",
  86. usnLow, usnHigh ));
  87. if ( _fDoDeletions )
  88. {
  89. WORKID wid = _cat.PathToWorkId( lcaseFunnyRootPath, eUsnsArray, fileIdInvalid );
  90. //
  91. // Workid is invalid for root, such as f:\
  92. //
  93. if ( wid != widInvalid )
  94. _cicat.Touch( wid, eUsnsArray );
  95. }
  96. DoIt( lcaseFunnyRootPath );
  97. } //CUsnTreeTraversal
  98. //+---------------------------------------------------------------------------
  99. //
  100. // Member: CUsnTreeTraversal::ProcessFile
  101. //
  102. // Synopsis: Processes the given file and adds it to the list of changed
  103. // documents if necessary.
  104. //
  105. // Arguments: [lcaseFunnyPath] - Path of the file to be processed.
  106. //
  107. // Returns: TRUE if successful
  108. //
  109. // History: 07-May-97 SitaramR Created
  110. //
  111. //----------------------------------------------------------------------------
  112. BOOL CUsnTreeTraversal::ProcessFile( const CLowerFunnyPath & lcaseFunnyPath )
  113. {
  114. ciDebugOut(( DEB_USN, "Usn process file %ws\n", lcaseFunnyPath.GetActualPath() ));
  115. USN usn;
  116. FILEID fileId;
  117. WORKID widParent;
  118. FILETIME ftLastChange;
  119. BOOL fOk = GetUsnInfo( lcaseFunnyPath,
  120. _cicat,
  121. _volumeId,
  122. usn,
  123. fileId,
  124. widParent,
  125. ftLastChange );
  126. if ( fOk )
  127. {
  128. WORKID wid;
  129. BOOL fAdded = _cicat.ProcessFile( lcaseFunnyPath,
  130. fileId,
  131. _volumeId,
  132. widParent,
  133. _pCurEntry->Attributes(),
  134. wid );
  135. ciDebugOut(( DEB_ITRACE,
  136. "wid %d(%x), fAdded %d, _fUserInitiated %d, usn %#I64x,"
  137. " _usnLow %#I64x, _usnHigh %#I64x\n",
  138. wid, wid, fAdded, _fUserInitiated, usn,
  139. _usnLow, _usnHigh ));
  140. if ( widInvalid != wid )
  141. {
  142. #if 0 // NOTE: we can't use this optimization because we don't know why the
  143. // USN is higher than _usnHigh. We might later choose to not
  144. // filter based on this USN change.
  145. if ( usn > _usnHigh )
  146. {
  147. // Modified since scan began. Don't file twice.
  148. _cicat.Touch( wid, eUsnsArray );
  149. }
  150. else
  151. #endif // 0
  152. if ( _fUserInitiated || ( 0 == usn ) )
  153. {
  154. // User asked to filter everything or NT4 create
  155. Add ( wid );
  156. }
  157. else if ( 0 == _usnLow )
  158. {
  159. // Initial scan -- filter if we added the file OR
  160. // if the file has changed since we filtered it last.
  161. if ( fAdded || _cicat.HasChanged( wid, ftLastChange ) )
  162. {
  163. Add ( wid );
  164. }
  165. else
  166. {
  167. // Make sure we don't delete it!
  168. _cicat.Touch(wid, eUsnsArray);
  169. }
  170. }
  171. else if ( usn >= _usnLow )
  172. {
  173. Add ( wid );
  174. }
  175. else if ( _cicat.HasChanged( wid, ftLastChange ) )
  176. {
  177. // NT4 Modify
  178. Add( wid );
  179. }
  180. else
  181. {
  182. // Make sure we don't delete it!
  183. _cicat.Touch(wid, eUsnsArray);
  184. }
  185. }
  186. }
  187. return TRUE;
  188. } //ProcessFile
  189. //+-------------------------------------------------------------------------
  190. //
  191. // Member: CUsnTreeTraversal::TraversalIdle, public
  192. //
  193. // Synopsis: Called when a directory is about to be traversed, when
  194. // the traversal code isn't buffering any paths. This makes
  195. // it a good time to check the USN log, since the Win32 file
  196. // enumeration code buffers files within a given directory.
  197. //
  198. // Arguments: [fStalled] -- TRUE implies scanning halted due to resource
  199. // constraints. Only critical work should be done.
  200. //
  201. // History: 16-Jun-98 dlee Created
  202. //
  203. //--------------------------------------------------------------------------
  204. void CUsnTreeTraversal::TraversalIdle( BOOL fStalled )
  205. {
  206. //
  207. // Every now and then, go empty the USN logs so they won't overflow
  208. // during the scan.
  209. //
  210. if ( _cProcessed > 1000 || fStalled )
  211. {
  212. ciDebugOut(( DEB_USN, "CUsnTreeTraversal: ProcessUsnLog\n" ));
  213. _usnMgr.ProcessUsnLog( _fAbort, _volumeId, _usnHigh );
  214. if ( _cProcessed > 1000 )
  215. _cProcessed = 0;
  216. }
  217. } //ProcessDirectoryTraversal
  218. //+-------------------------------------------------------------------------
  219. //
  220. // Member: CUsnTreeTraversal::Add
  221. //
  222. // Synopsis: Add wid for update to doclist
  223. //
  224. // Arguments: [wid] -- Workid
  225. //
  226. // History: 07-May-97 SitaramR Created
  227. //
  228. //--------------------------------------------------------------------------
  229. void CUsnTreeTraversal::Add( WORKID wid )
  230. {
  231. _cicat.Touch(wid, eUsnsArray);
  232. ciDebugOut(( DEB_USN, " Add %d(%x) as doc %d\n", wid, wid, _cDoc ));
  233. //
  234. // Use an usn of 0, because we are adding wids in tree order, not usn order
  235. //
  236. _docList.Set ( _cDoc, wid, 0, _volumeId );
  237. _cDoc++;
  238. if (_cDoc == CI_MAX_DOCS_IN_WORDLIST)
  239. {
  240. _cProcessed += _cDoc;
  241. _docList.LokSetCount ( _cDoc );
  242. _cicat.AddDocuments( _docList );
  243. _docList.LokClear();
  244. _docList.SetPartId ( 1 );
  245. _cDoc = 0;
  246. // NTRAID#DB-NTBUG9-83800-2000/07/31-dlee notifications can be missed if > 10k files in a directory
  247. //
  248. // This will hit if a directory has > 10k files. In this case, we
  249. // need to read the USN log so it won't overflow, but we may
  250. // mis-interpret the scan since the Win32 enumeration API buffers
  251. // a small number of files during the enumeration. This is a lesser
  252. // of 2 evils fix. A file will be falsely added to the catalog if it
  253. // is in the enumeration buffer buffer, then deleted before it
  254. // is processed. This window has existed on FAT for a long time
  255. // and hasn't been hit.
  256. //
  257. if ( _cProcessed > 10000 )
  258. {
  259. ciDebugOut(( DEB_USN, "Add: ProcessUsnLog\n" ));
  260. _usnMgr.ProcessUsnLog( _fAbort, _volumeId, _usnHigh );
  261. _cProcessed = 0;
  262. }
  263. }
  264. } //Add
  265. //+---------------------------------------------------------------------------
  266. //
  267. // Member: CUsnTreeTraversal::IsEligibleForTraversal
  268. //
  269. // Synopsis: Checks to see if the current directory is eligible for
  270. // traversal.
  271. //
  272. // Arguments: [lcaseFunnyDir] - Directory path
  273. //
  274. // History: 07-May-97 SitaramR Created
  275. //
  276. //----------------------------------------------------------------------------
  277. BOOL CUsnTreeTraversal::IsEligibleForTraversal( const CLowerFunnyPath & lcaseFunnyDir ) const
  278. {
  279. return _cicat.IsEligibleForFiltering( lcaseFunnyDir );
  280. }
  281. //+-------------------------------------------------------------------------
  282. //
  283. // Member: CUsnTreeTraversal::EndProcessing
  284. //
  285. // Synopsis: Flush final updates to catalog
  286. //
  287. // History: 07-May-97 SitaramR Created
  288. //
  289. //--------------------------------------------------------------------------
  290. void CUsnTreeTraversal::EndProcessing()
  291. {
  292. ciDebugOut(( DEB_ITRACE,
  293. "CUsnTreeTraversal__EndProcessing, _fAbort %d, _cDoc %d, _status %#x\n",
  294. _fAbort, _cDoc, _status ));
  295. if ( !_fAbort )
  296. {
  297. if ( _cDoc != 0 )
  298. {
  299. _docList.LokSetCount ( _cDoc );
  300. _cicat.AddDocuments( _docList );
  301. _docList.LokClear();
  302. _docList.SetPartId ( 1 );
  303. _cDoc = 0;
  304. }
  305. if ( STATUS_NO_MORE_FILES == _status )
  306. {
  307. ciDebugOut(( DEB_ITRACE, "_fDoDeletions %d\n", _fDoDeletions ));
  308. _cat.EndUpdate( _fDoDeletions, eUsnsArray );
  309. }
  310. else if ( STATUS_SUCCESS != _status )
  311. {
  312. ciDebugOut(( DEB_ERROR, "Error %#x while traversing\n",
  313. _status ));
  314. THROW( CException( _status ) );
  315. }
  316. }
  317. } //EndProcessing
  318. //+-------------------------------------------------------------------------
  319. //
  320. // Member: CUsnTreeTraversal::GetUsnInfo
  321. //
  322. // Synopsis: Returns fileid etc info for given file
  323. //
  324. // Arguments: [funnyPath] -- Path to file
  325. // [cicat] -- Catalog
  326. // [volumeId] -- Volume id
  327. // [usn] -- Usn returned here
  328. // [fileId] -- FileId returned here
  329. // [widParent] -- Workid of parent returned here
  330. // [ftLastChange] -- Time of last change (data, EA, security, ...)
  331. //
  332. // History: 07-May-97 SitaramR Created
  333. //
  334. // Notes: The routine doesn't throw, it returns false to indicate that
  335. // the tree traversal must continue after skipping over the
  336. // current file.
  337. //
  338. //--------------------------------------------------------------------------
  339. BOOL CUsnTreeTraversal::GetUsnInfo( const CFunnyPath & funnyPath,
  340. CiCat &cicat,
  341. VOLUMEID volumeId,
  342. USN &usn,
  343. FILEID &fileId,
  344. WORKID &widParent,
  345. FILETIME &ftLastChange )
  346. {
  347. HANDLE hFile;
  348. NTSTATUS status = CiNtOpenNoThrow( hFile,
  349. funnyPath.GetPath(),
  350. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  351. FILE_SHARE_READ | FILE_SHARE_WRITE |
  352. FILE_SHARE_DELETE,
  353. 0 );
  354. if ( !NT_SUCCESS( status ) )
  355. {
  356. // File may have been deleted, in use etc, so skip processing the file.
  357. ciDebugOut(( DEB_ITRACE,
  358. "File open for %ws get usn info failed (0x%x)\n",
  359. funnyPath.GetPath(), status ));
  360. #if CIDBG == 1
  361. if ( IsSharingViolation( status ) ||
  362. STATUS_ACCESS_DENIED == status ||
  363. STATUS_INVALID_PARAMETER == status ||
  364. STATUS_DELETE_PENDING == status ||
  365. STATUS_UNRECOGNIZED_VOLUME == status ||
  366. STATUS_INSUFFICIENT_RESOURCES == status ||
  367. STATUS_OBJECT_PATH_NOT_FOUND == status ||
  368. STATUS_OBJECT_NAME_NOT_FOUND == status ||
  369. STATUS_IO_REPARSE_TAG_NOT_HANDLED == status ||
  370. STATUS_NO_MEDIA_IN_DEVICE == status || // junction points can hit this
  371. STATUS_OBJECT_NAME_INVALID == status )
  372. return FALSE;
  373. else
  374. {
  375. #if CIDBG == 1
  376. char szTemp[200];
  377. sprintf( szTemp, "New error 0x%x from NtCreateFile in ::GetUsnInfo. Call dlee or hit 'g'", status );
  378. Win4AssertEx(__FILE__, __LINE__, szTemp);
  379. #endif
  380. // THROW( CException( status ) );
  381. }
  382. #endif
  383. return FALSE;
  384. }
  385. SHandle xFile( hFile );
  386. // NTRAID#DB-NTBUG9-83802-2000/07/31-dlee File names are limited to 400 characters when reading USN records
  387. IO_STATUS_BLOCK iosb;
  388. ULONGLONG readBuffer[100];
  389. USN_RECORD *pUsnRecord;
  390. status = NtFsControlFile( hFile,
  391. NULL,
  392. NULL,
  393. NULL,
  394. &iosb,
  395. FSCTL_READ_FILE_USN_DATA,
  396. NULL,
  397. NULL,
  398. &readBuffer,
  399. sizeof(readBuffer) );
  400. if ( NT_SUCCESS( status ) )
  401. status = iosb.Status;
  402. if ( NT_SUCCESS( status ) )
  403. {
  404. FILE_BASIC_INFORMATION BasicInfo;
  405. status = NtQueryInformationFile( hFile,
  406. &iosb,
  407. &BasicInfo,
  408. sizeof(BasicInfo),
  409. FileBasicInformation );
  410. if ( NT_SUCCESS( status ) )
  411. status = iosb.Status;
  412. if ( NT_SUCCESS( status ) )
  413. {
  414. pUsnRecord = (USN_RECORD *) &readBuffer;
  415. fileId = pUsnRecord->FileReferenceNumber;
  416. //
  417. // If not indexed, return FALSE unless it's a directory and it's
  418. // already in the index. We need to leave these in case there
  419. // are files below the directory.
  420. //
  421. if ( ( 0 != (pUsnRecord->FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) ) )
  422. {
  423. if ( 0 == (pUsnRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
  424. return FALSE;
  425. WORKID wid = cicat.FileIdToWorkId( fileId, volumeId );
  426. if ( widInvalid == wid )
  427. return FALSE;
  428. ciDebugOut(( DEB_ITRACE, "leaving special-case directory\n" ));
  429. }
  430. usn = pUsnRecord->Usn;
  431. //
  432. // Workid of parent will be widInvalid for files in root directory
  433. //
  434. widParent = cicat.FileIdToWorkId( pUsnRecord->ParentFileReferenceNumber,
  435. volumeId );
  436. ftLastChange = *(FILETIME *)&BasicInfo.ChangeTime;
  437. }
  438. else
  439. {
  440. // NTRAID#DB-NTBUG9-83804-2000/07/31-dlee When a lookup of USN info fails during a USN scan due to low resources we don't abort the scan
  441. //
  442. // Incorrect behavior if out of resources -- we need to
  443. // restart the scan!
  444. //
  445. // Need to handle reparse point errors like
  446. // STATUS_IO_REPARSE_TAG_NOT_HANDLED. For now, ignore
  447. //
  448. ciDebugOut(( DEB_ITRACE, "NtQueryInformationFile failed, 0x%x\n", status ));
  449. return FALSE;
  450. }
  451. }
  452. else
  453. {
  454. // NTRAID#DB-NTBUG9-83804-2000/07/31-dlee When a lookup of USN info fails during a USN scan due to low resources we don't abort the scan
  455. // incorrect behavior if out of resources -- we need to
  456. // restart the scan!
  457. //
  458. // Need to handle reparse point errors like
  459. // STATUS_IO_REPARSE_TAG_NOT_HANDLED. For now, ignore
  460. //
  461. ciDebugOut(( DEB_ITRACE, "File usn read fsctl failed, 0x%x\n", status ));
  462. return FALSE;
  463. }
  464. return TRUE;
  465. } //GetUsnInfo