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.

3250 lines
109 KiB

  1. /*++
  2. (c) 1998 Seagate Software, Inc. All rights reserved.
  3. Module Name:
  4. fsaitemr.cpp
  5. Abstract:
  6. This class contains represents a scan item (i.e. file or directory) for NTFS 5.0.
  7. Author:
  8. Chuck Bardeen [cbardeen] 1-Dec-1996
  9. Revision History:
  10. Michael Lotz [lotz ] 13-Jan-1997
  11. --*/
  12. #include "stdafx.h"
  13. #define WSB_TRACE_IS WSB_TRACE_BIT_FSA
  14. #include "wsb.h"
  15. #include "fsa.h"
  16. #include "fsaitem.h"
  17. #include "rpdata.h"
  18. #include "rpio.h"
  19. #include "rpguid.h"
  20. #include "fsaitemr.h"
  21. #define SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
  22. #define EXCLUSIVE_FLAG ( 0 ) // exclusive open without sharing of the file
  23. //
  24. // Notice these two bits are NOT the same location????
  25. //
  26. #define BIT_FOR_TRUNCATED FILE_ATTRIBUTE_OFFLINE
  27. #define BIT_FOR_RP FILE_ATTRIBUTE_REPARSE_POINT
  28. //
  29. // File extensions that are treated as special cases for truncate
  30. //
  31. #define EXT_FOR_EXE L".exe"
  32. #define EXT_FOR_DLL L".dll"
  33. //
  34. // Macros and defines for exe and dll headers
  35. //
  36. #define SIZE_OF_NT_SIGNATURE sizeof(DWORD)
  37. //
  38. // Macros
  39. //
  40. /* Offset to PE file signature */
  41. #define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + \
  42. ((PIMAGE_DOS_HEADER)a)->e_lfanew))
  43. /* MS-OS header identifies the NT PEFile signature dword;
  44. the PEFILE header exists just after that dword. */
  45. #define PEFHDROFFSET(a) ((LPVOID)((BYTE *)a + \
  46. ((PIMAGE_DOS_HEADER)a)->e_lfanew + \
  47. SIZE_OF_NT_SIGNATURE))
  48. /* PE optional header is immediately after PEFile header. */
  49. #define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \
  50. ((PIMAGE_DOS_HEADER)a)->e_lfanew + \
  51. SIZE_OF_NT_SIGNATURE + \
  52. sizeof (IMAGE_FILE_HEADER)))
  53. /* Section headers are immediately after PE optional header. */
  54. #define SECHDROFFSET(a) ((LPVOID)((BYTE *)a + \
  55. ((PIMAGE_DOS_HEADER)a)->e_lfanew + \
  56. SIZE_OF_NT_SIGNATURE + \
  57. sizeof (IMAGE_FILE_HEADER) + \
  58. sizeof (IMAGE_OPTIONAL_HEADER)))
  59. HRESULT
  60. OpenObject (
  61. IN WCHAR const *pwszFile,
  62. IN ULONG CreateOptions,
  63. IN ULONG DesiredAccess,
  64. IN ULONG ShareAccess,
  65. IN ULONG CreateDisposition,
  66. OUT IO_STATUS_BLOCK *IoStatusBlock,
  67. OUT HANDLE *ObjectHandle
  68. )
  69. /*++
  70. Implements: A wrapper function for NtCreateFile
  71. OpenObject
  72. --*/
  73. //
  74. // Simple wrapper for NtCreateFile
  75. //
  76. {
  77. HRESULT hr = S_OK;
  78. NTSTATUS ntStatus;
  79. BOOL bStatus;
  80. OBJECT_ATTRIBUTES ObjectAttributes;
  81. UNICODE_STRING str;
  82. RTL_RELATIVE_NAME_U RelativeName;
  83. PVOID StrBuffer = NULL;
  84. BOOL bReleaseRelativeName = FALSE;
  85. WsbTraceIn(OLESTR("OpenObject"), OLESTR(""));
  86. //
  87. // Null out the pointer so we know when it was allocated
  88. //
  89. str.Buffer = NULL;
  90. RelativeName.RelativeName.Buffer = NULL;
  91. try {
  92. //
  93. // Convert input name into special format with \??\
  94. //
  95. //bStatus = RtlDosPathNameToNtPathName_U( pwszFile,
  96. // &str,
  97. // NULL,
  98. // NULL );
  99. //WsbAffirm( bStatus, E_FAIL);
  100. bStatus = RtlDosPathNameToRelativeNtPathName_U(
  101. pwszFile,
  102. &str,
  103. NULL,
  104. &RelativeName
  105. );
  106. WsbAffirm( bStatus, E_FAIL);
  107. bReleaseRelativeName = TRUE;
  108. StrBuffer = str.Buffer;
  109. if ( RelativeName.RelativeName.Length ) {
  110. str = RelativeName.RelativeName;
  111. } else {
  112. RelativeName.ContainingDirectory = NULL;
  113. }
  114. InitializeObjectAttributes(
  115. &ObjectAttributes,
  116. &str,
  117. 0,
  118. RelativeName.ContainingDirectory,
  119. NULL
  120. );
  121. ntStatus = NtCreateFile(
  122. ObjectHandle,
  123. DesiredAccess | SYNCHRONIZE,
  124. &ObjectAttributes,
  125. IoStatusBlock,
  126. NULL, // pallocationsize (none!)
  127. FILE_ATTRIBUTE_NORMAL,
  128. ShareAccess,
  129. CreateDisposition,
  130. CreateOptions | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
  131. NULL, // EA buffer (none!)
  132. 0);
  133. //
  134. // Right now if the file is not a reparse point the above open
  135. // fails -- so now try it without the FILE_OPEN_REPARSE_POINT
  136. //
  137. if ( STATUS_NOT_A_REPARSE_POINT == ntStatus) {
  138. WsbAffirmNtStatus( NtCreateFile(
  139. ObjectHandle,
  140. DesiredAccess | SYNCHRONIZE,
  141. &ObjectAttributes,
  142. IoStatusBlock,
  143. NULL, // pallocationsize (none!)
  144. FILE_ATTRIBUTE_NORMAL,
  145. ShareAccess,
  146. CreateDisposition,
  147. CreateOptions | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_POSIX_SEMANTICS,
  148. NULL, // EA buffer (none!)
  149. 0 ) );
  150. } else {
  151. WsbAffirmNtStatus( ntStatus );
  152. }
  153. } WsbCatch( hr );
  154. //
  155. // Clean up the memory if we allocated it
  156. //
  157. if (bReleaseRelativeName) {
  158. RtlReleaseRelativeName(&RelativeName);
  159. }
  160. if (NULL != StrBuffer) {
  161. RtlFreeHeap(RtlProcessHeap(), 0, StrBuffer);
  162. }
  163. //if( NULL == str.Buffer ) {
  164. // bStatus = RtlFreeHeap( RtlProcessHeap(), 0, str.Buffer );
  165. //}
  166. WsbTraceOut(OLESTR("OpenObject"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  167. return( hr );
  168. } // OpenObject
  169. HRESULT
  170. CopyPlaceholderToRP (
  171. IN CONST FSA_PLACEHOLDER *pPlaceholder,
  172. OUT PREPARSE_DATA_BUFFER pReparseBuffer,
  173. IN BOOL bTruncated
  174. )
  175. /*++
  176. Implements: A wrapper function for copying the placeholder data into the reparse point
  177. CopyPlaceholderToRP
  178. --*/
  179. //
  180. // Simple wrapper moving the data from the scan item in-memory
  181. // placeholder information into a reparse point buffer
  182. //
  183. {
  184. HRESULT hr = S_OK;
  185. PRP_DATA pHsmData;
  186. WsbTraceIn(OLESTR("CopyPlaceholderToRP"), OLESTR(""));
  187. WsbTrace(OLESTR(" fileStart = %I64d, dataStart = %I64d, dataStreamStart = %I64d\n"),
  188. pPlaceholder->fileStart, pPlaceholder->dataStart,
  189. pPlaceholder->dataStreamStart);
  190. WsbTrace(OLESTR(" fileSize = %I64d, dataSize = %I64d, dataStreamSize = %I64d\n"),
  191. pPlaceholder->fileSize, pPlaceholder->dataSize,
  192. pPlaceholder->dataStreamSize);
  193. try {
  194. //
  195. // Validate the pointers passed in
  196. //
  197. WsbAssert( NULL != pPlaceholder, E_POINTER );
  198. WsbAssert( NULL != pReparseBuffer, E_POINTER );
  199. //
  200. // Setup the pointer to our hsm data
  201. //
  202. pHsmData = (PRP_DATA) &pReparseBuffer->GenericReparseBuffer.DataBuffer[0];
  203. //
  204. // Set the generic reparse point header information for the tag and size
  205. //
  206. pReparseBuffer->ReparseTag = IO_REPARSE_TAG_HSM ;
  207. pReparseBuffer->ReparseDataLength = sizeof(RP_DATA);
  208. pReparseBuffer->Reserved = 0 ;
  209. //
  210. // Set the private data that is the vendor id and version number
  211. //
  212. pHsmData->vendorId = RP_MSFT_VENDOR_ID;
  213. pHsmData->version = RP_VERSION;
  214. //
  215. // Assume for now that there is only one placeholder
  216. // This needs to be updated
  217. //
  218. pHsmData->numPrivateData = 1;
  219. pHsmData->fileIdentifier = GUID_NULL;
  220. ZeroMemory(pHsmData->data.reserved, RP_RESV_SIZE);
  221. //
  222. // If the file is to indicate the file is truncated then set the bit
  223. // otherwise make sure it is off
  224. //
  225. RP_INIT_BITFLAG( pHsmData->data.bitFlags );
  226. if( bTruncated ) {
  227. RP_SET_TRUNCATED_BIT( pHsmData->data.bitFlags );
  228. } else {
  229. RP_CLEAR_TRUNCATED_BIT( pHsmData->data.bitFlags );
  230. }
  231. //
  232. // Set the truncate on close bit as needed
  233. //
  234. if( pPlaceholder->truncateOnClose ) {
  235. RP_SET_TRUNCATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
  236. } else {
  237. RP_CLEAR_TRUNCATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
  238. }
  239. //
  240. // Set the Premigrate on close bit as needed
  241. //
  242. if( pPlaceholder->premigrateOnClose ) {
  243. RP_SET_PREMIGRATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
  244. } else {
  245. RP_CLEAR_PREMIGRATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
  246. }
  247. //
  248. // Set the global bit flags based on the placeholder data
  249. // For now since we are assuming one placeholder then set
  250. // them the same.
  251. pHsmData->globalBitFlags = pHsmData->data.bitFlags;
  252. //
  253. // Move over the data parts of the information
  254. //
  255. pHsmData->data.migrationTime.QuadPart = WsbFTtoLL( pPlaceholder->migrationTime );
  256. pHsmData->data.hsmId = pPlaceholder->hsmId;
  257. pHsmData->data.bagId = pPlaceholder->bagId;
  258. pHsmData->data.fileStart.QuadPart = pPlaceholder->fileStart;
  259. pHsmData->data.fileSize.QuadPart = pPlaceholder->fileSize;
  260. pHsmData->data.dataStart.QuadPart = pPlaceholder->dataStart;
  261. pHsmData->data.dataSize.QuadPart = pPlaceholder->dataSize;
  262. pHsmData->data.fileVersionId.QuadPart = pPlaceholder->fileVersionId;
  263. pHsmData->data.verificationData.QuadPart = pPlaceholder->verificationData;
  264. pHsmData->data.verificationType = pPlaceholder->verificationType;
  265. pHsmData->data.recallCount = pPlaceholder->recallCount;
  266. pHsmData->data.recallTime.QuadPart = WsbFTtoLL( pPlaceholder->recallTime );
  267. pHsmData->data.dataStreamStart.QuadPart = pPlaceholder->dataStreamStart;
  268. pHsmData->data.dataStreamSize.QuadPart = pPlaceholder->dataStreamSize;
  269. pHsmData->data.dataStream = pPlaceholder->dataStream;
  270. pHsmData->data.dataStreamCRCType = pPlaceholder->dataStreamCRCType;
  271. pHsmData->data.dataStreamCRC.QuadPart = pPlaceholder->dataStreamCRC;
  272. //
  273. // Lastly generate the check sum
  274. //
  275. RP_GEN_QUALIFIER(pHsmData, pHsmData->qualifier);
  276. //
  277. // Now set the bit that tells the filter that it is us setting the reparse point.
  278. // This is not included in the qualifier checksum generation.
  279. //
  280. RP_SET_ORIGINATOR_BIT( pHsmData->data.bitFlags );
  281. } WsbCatch(hr);
  282. WsbTraceOut(OLESTR("CopyPlaceholderToRP"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  283. return( hr );
  284. }
  285. HRESULT
  286. CopyRPDataToPlaceholder (
  287. IN CONST PRP_DATA pHsmData,
  288. OUT FSA_PLACEHOLDER *pPlaceholder
  289. )
  290. /*++
  291. Implements: A wrapper function for moving the Reparse Point into generic FSA_PLACEHOLDER format
  292. CopyRPDataToPlaceholder
  293. --*/
  294. {
  295. HRESULT hr = S_OK;
  296. ULONG qualifier; // Used to checksum the data
  297. WsbTraceIn(OLESTR("CopyRPDataToPlaceholder"), OLESTR(""));
  298. WsbTrace(OLESTR(" fileStart = %I64d, dataStart = %I64d, dataStreamStart = %I64d\n"),
  299. pHsmData->data.fileStart.QuadPart, pHsmData->data.dataStart.QuadPart,
  300. pHsmData->data.dataStreamStart.QuadPart);
  301. WsbTrace(OLESTR(" fileSize = %I64d, dataSize = %I64d, dataStreamSize = %I64d\n"),
  302. pHsmData->data.fileSize.QuadPart, pHsmData->data.dataSize.QuadPart,
  303. pHsmData->data.dataStreamSize.QuadPart);
  304. //
  305. // Simple wrapper moving the data from the reparse point buffer into the
  306. // generic placeholder information
  307. //
  308. try {
  309. //
  310. // Validate the pointers passed in
  311. //
  312. WsbAssert( NULL != pHsmData, E_POINTER );
  313. WsbAssert( NULL != pPlaceholder, E_POINTER );
  314. //
  315. // Just in case, we clear out the originator bit.
  316. //
  317. RP_CLEAR_ORIGINATOR_BIT( pHsmData->data.bitFlags );
  318. //
  319. // Verify the check sum and the key private fields
  320. //
  321. RP_GEN_QUALIFIER(pHsmData, qualifier);
  322. WsbAffirm( pHsmData->qualifier == qualifier, E_FAIL );
  323. WsbAffirm( RP_MSFT_VENDOR_ID == pHsmData->vendorId, E_FAIL );
  324. WsbAffirm( RP_VERSION == pHsmData->version, E_FAIL );
  325. //
  326. // Now that everything worked, save the values in our private data
  327. //
  328. pPlaceholder->migrationTime = WsbLLtoFT( pHsmData->data.migrationTime.QuadPart );
  329. pPlaceholder->hsmId = pHsmData->data.hsmId;
  330. pPlaceholder->bagId = pHsmData->data.bagId;
  331. pPlaceholder->fileStart = pHsmData->data.fileStart.QuadPart;
  332. pPlaceholder->fileSize = pHsmData->data.fileSize.QuadPart;
  333. pPlaceholder->dataStart = pHsmData->data.dataStart.QuadPart;
  334. pPlaceholder->dataSize = pHsmData->data.dataSize.QuadPart;
  335. pPlaceholder->fileVersionId = pHsmData->data.fileVersionId.QuadPart;
  336. pPlaceholder->verificationData = pHsmData->data.verificationData.QuadPart;
  337. pPlaceholder->verificationType = pHsmData->data.verificationType;
  338. pPlaceholder->recallCount = pHsmData->data.recallCount;
  339. pPlaceholder->recallTime = WsbLLtoFT( pHsmData->data.recallTime.QuadPart );
  340. pPlaceholder->dataStreamStart = pHsmData->data.dataStreamStart.QuadPart;
  341. pPlaceholder->dataStreamSize = pHsmData->data.dataStreamSize.QuadPart;
  342. pPlaceholder->dataStream = pHsmData->data.dataStream;
  343. pPlaceholder->dataStreamCRCType = pHsmData->data.dataStreamCRCType;
  344. pPlaceholder->dataStreamCRC = pHsmData->data.dataStreamCRC.QuadPart;
  345. //
  346. // Set placeholder bits
  347. //
  348. if( RP_FILE_IS_TRUNCATED( pHsmData->data.bitFlags ) ) {
  349. pPlaceholder->isTruncated = TRUE;
  350. } else {
  351. pPlaceholder->isTruncated = FALSE;
  352. }
  353. if( RP_FILE_DO_TRUNCATE_ON_CLOSE( pHsmData->data.bitFlags ) ) {
  354. pPlaceholder->truncateOnClose = TRUE;
  355. } else {
  356. pPlaceholder->truncateOnClose = FALSE;
  357. }
  358. if( RP_FILE_DO_PREMIGRATE_ON_CLOSE( pHsmData->data.bitFlags ) ) {
  359. pPlaceholder->premigrateOnClose = TRUE;
  360. } else {
  361. pPlaceholder->premigrateOnClose = FALSE;
  362. }
  363. } WsbCatch(hr);
  364. WsbTraceOut(OLESTR("CopyRPDataToPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  365. return( hr );
  366. }
  367. HRESULT
  368. CopyRPToPlaceholder (
  369. IN CONST PREPARSE_DATA_BUFFER pReparseBuffer,
  370. OUT FSA_PLACEHOLDER *pPlaceholder
  371. )
  372. /*++
  373. Implements: A wrapper function for moving the Reparse Point into generic FSA_PLACEHOLDER format
  374. CopyRPToPlaceholder
  375. --*/
  376. {
  377. HRESULT hr = S_OK;
  378. PRP_DATA pHsmData;
  379. WsbTraceIn(OLESTR("CopyRPToPlaceholder"), OLESTR(""));
  380. //
  381. // Simple wrapper moving the data from the reparse point buffer into the
  382. // generic placeholder information
  383. //
  384. try {
  385. //
  386. // Validate the pointers passed in
  387. //
  388. WsbAssert( NULL != pReparseBuffer, E_POINTER );
  389. WsbAssert( NULL != pPlaceholder, E_POINTER );
  390. //
  391. // Get the pointers setup correctly to this buffer because the
  392. // type REPARSE_DATA_BUFFER actually doesn't have any space
  393. // allocated for the data and that is our own type, so get pointers
  394. // pointing into the real allocated space so we can use them
  395. //
  396. pHsmData = (PRP_DATA) &pReparseBuffer->GenericReparseBuffer.DataBuffer[0];
  397. //
  398. // Validate the key public fields to make sure it is data we
  399. // understand
  400. //
  401. WsbAffirm( IO_REPARSE_TAG_HSM == pReparseBuffer->ReparseTag , S_FALSE );
  402. WsbAffirm( sizeof(RP_DATA) == pReparseBuffer->ReparseDataLength , S_FALSE );
  403. //
  404. // Copy over the RP_DATA information
  405. //
  406. WsbAffirmHr(CopyRPDataToPlaceholder(pHsmData, pPlaceholder));
  407. } WsbCatch(hr);
  408. WsbTraceOut(OLESTR("CopyRPToPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  409. return( hr );
  410. }
  411. HRESULT
  412. CFsaScanItem::CalculateCurrentCRCAndUSN(
  413. IN LONGLONG offset,
  414. IN LONGLONG size,
  415. OUT ULONG *pCurrentCRC,
  416. OUT LONGLONG *pUsn
  417. )
  418. {
  419. HRESULT hr = S_OK;
  420. HRESULT hrTest = S_OK;
  421. CWsbStringPtr path;
  422. IO_STATUS_BLOCK IoStatusBlock;
  423. HANDLE handle = INVALID_HANDLE_VALUE;
  424. FILE_BASIC_INFORMATION basicInformation;
  425. try {
  426. WsbTraceIn(OLESTR("CFsaScanItem::CalculateCurrentCRCAndUSN"), OLESTR("offset = <%I64d>, size = <%I64d>"),
  427. offset, size);
  428. //
  429. // Create the real file name we need to open, under the covers this
  430. // allocates the buffer since the path pointer is null
  431. //
  432. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  433. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  434. WsbTrace(OLESTR("CFsaScanItem::CalculateCurrentCRCAndUSN for file <%ls>"), (OLECHAR *)path);
  435. // Open the file.
  436. WsbAffirmHr( OpenObject( path,
  437. FILE_NON_DIRECTORY_FILE,
  438. FILE_READ_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES,
  439. EXCLUSIVE_FLAG,
  440. FILE_OPEN,
  441. &IoStatusBlock,
  442. &handle ) );
  443. //
  444. // The open worked, our handle should be valid but we check to be
  445. // safe and sure
  446. //
  447. WsbAssertHandle( handle );
  448. //
  449. // Get the current attributes of the file and the times
  450. //
  451. WsbAssertNtStatus( NtQueryInformationFile( handle,
  452. &IoStatusBlock,
  453. (PVOID)&basicInformation,
  454. sizeof( basicInformation ),
  455. FileBasicInformation ) );
  456. //
  457. // Set the time flags so that when we close the handle the
  458. // time are not updated on the file and the FileAttributes
  459. //
  460. basicInformation.CreationTime.QuadPart = -1;
  461. basicInformation.LastAccessTime.QuadPart = -1;
  462. basicInformation.LastWriteTime.QuadPart = -1;
  463. basicInformation.ChangeTime.QuadPart = -1;
  464. WsbAssertNtStatus( NtSetInformationFile( handle,
  465. &IoStatusBlock,
  466. (PVOID)&basicInformation,
  467. sizeof( basicInformation ),
  468. FileBasicInformation ) );
  469. //
  470. // Calculate the CRC
  471. //
  472. WsbAffirmHr(CalculateCurrentCRCInternal(handle, offset, size, pCurrentCRC));
  473. //
  474. // Calculate the USN
  475. //
  476. *pUsn = 0;
  477. hr = WsbGetUsnFromFileHandle(handle, FALSE, pUsn);
  478. if (S_OK != hr) {
  479. //
  480. // If we can't get the USN set it to 0 which is an invalid
  481. // USN and keep going.
  482. *pUsn = 0;
  483. hr = S_OK;
  484. }
  485. //
  486. // Close the file
  487. //
  488. NtClose( handle );
  489. handle = INVALID_HANDLE_VALUE;
  490. } WsbCatch( hr );
  491. //
  492. // Close the file for sure
  493. //
  494. if( INVALID_HANDLE_VALUE != handle) {
  495. NtClose( handle );
  496. }
  497. WsbTraceOut(OLESTR("CalculateCurrentCRCAndUSN"), OLESTR("hr = <%ls>, CRC is <%ls>, USN is <%ls>"),
  498. WsbHrAsString(hr), WsbPtrToUlongAsString(pCurrentCRC), WsbPtrToLonglongAsString(pUsn));
  499. return(hr);
  500. }
  501. HRESULT
  502. CFsaScanItem::CalculateCurrentCRCInternal(
  503. IN HANDLE handle,
  504. IN LONGLONG offset,
  505. IN LONGLONG size,
  506. ULONG *pCurrentCRC
  507. )
  508. {
  509. HRESULT hr = S_OK;
  510. HRESULT hrTest = S_OK;
  511. register ULONG crc32 = 0;
  512. LONGLONG bytesRemaining;
  513. LONGLONG bytesToRead;
  514. ULONG bufferSize;
  515. ULONG bytesRead;
  516. CHAR * pBuffer = 0;
  517. CHAR * pCurrent;
  518. IO_STATUS_BLOCK IoStatusBlock;
  519. try {
  520. WsbTraceIn(OLESTR("CFsaScanItem::CalculateCurrentCRCInternal"), OLESTR("offset = <%I64d>, size = <%I64d>"),
  521. offset, size);
  522. // set initial value of CRC to 'pre-conditioning value'
  523. INITIALIZE_CRC(crc32);
  524. //
  525. // Set up to read where we want to start
  526. //
  527. LARGE_INTEGER startPoint;
  528. startPoint.QuadPart = offset;
  529. // Get the size of the file.
  530. bytesToRead = size;
  531. //
  532. // Figure out the size of the buffer to create
  533. //
  534. if (bytesToRead < 1024*1024) {
  535. //
  536. // Allocate one buffer the size of the file
  537. //
  538. bufferSize = (ULONG)bytesToRead;
  539. } else {
  540. bufferSize = (1024 * 1024);
  541. }
  542. pBuffer = (CHAR *)malloc(bufferSize);
  543. if (0 == pBuffer) {
  544. //
  545. // Try again for half the space
  546. //
  547. bufferSize = bufferSize / 2;
  548. pBuffer = (CHAR *)malloc(bufferSize);
  549. if (0 == pBuffer) {
  550. WsbThrow( E_OUTOFMEMORY );
  551. }
  552. }
  553. // Start calculating CRC by processing a 'chunk' of the file at a time.
  554. // While there are still chunks left, read that amount. Otherwise read the amount left.
  555. for (bytesRemaining = bytesToRead; bytesRemaining > 0; bytesRemaining -= bytesRead) {
  556. // Read data from the file.
  557. WsbAssertNtStatus(NtReadFile(handle, NULL, NULL, NULL, &IoStatusBlock, pBuffer, bufferSize, &startPoint, NULL));
  558. bytesRead = (DWORD)IoStatusBlock.Information;
  559. startPoint.QuadPart += bytesRead;
  560. // Each byte needs to be added into the CRC.
  561. for (pCurrent = pBuffer; (pCurrent < (pBuffer + bytesRead)) && (S_OK == hr); pCurrent++) {
  562. hrTest = WsbCRCReadFile((UCHAR *)pCurrent, &crc32);
  563. if (S_OK != hrTest) {
  564. hr = S_FALSE;
  565. }
  566. }
  567. }
  568. // return ones-complement of the calc'd CRC value - this is the actual CRC
  569. FINIALIZE_CRC(crc32);
  570. *pCurrentCRC = crc32;
  571. } WsbCatch( hr );
  572. //
  573. // Make sure allocated memory is freed
  574. //
  575. if (0 != pBuffer) {
  576. free(pBuffer);
  577. }
  578. WsbTraceOut(OLESTR("CalculateCurrentCRCInternal"), OLESTR("hr = <%ls>, CRC is <%ls>"), WsbHrAsString(hr), WsbPtrToUlongAsString(pCurrentCRC));
  579. return(hr);
  580. }
  581. HRESULT
  582. CFsaScanItem::CreatePlaceholder(
  583. IN LONGLONG offset,
  584. IN LONGLONG size,
  585. IN FSA_PLACEHOLDER placeholder,
  586. IN BOOL checkUsn,
  587. IN LONGLONG usn,
  588. OUT LONGLONG *pUsn
  589. )
  590. /*++
  591. Implements:
  592. IFsaScanItem::CreatePlaceholder().
  593. --*/
  594. {
  595. HRESULT hr = S_OK;
  596. CWsbStringPtr path;
  597. HANDLE handle = INVALID_HANDLE_VALUE;
  598. ULONG DesiredAccess;
  599. IO_STATUS_BLOCK IoStatusBlock;
  600. PREPARSE_DATA_BUFFER pReparseBuffer;
  601. UCHAR ReparseBuffer[sizeof(REPARSE_DATA_BUFFER) + sizeof(RP_DATA) + 10];
  602. NTSTATUS ntStatus;
  603. FILE_BASIC_INFORMATION basicInformation;
  604. LONGLONG lastWriteTime;
  605. LONGLONG nowUsn = 0;
  606. CWsbStringPtr volName;
  607. ULONG attributes;
  608. WsbTraceIn(OLESTR("CFsaScanItem::CreatePlaceholder"), OLESTR("offset = <%I64d>, size = <%I64d>, checkUsn = <%ls>, usn = <%I64d>"),
  609. offset, size, WsbBoolAsString(checkUsn), usn);
  610. try {
  611. BOOL wasReadOnly = FALSE;
  612. //
  613. // Set the offset and size information
  614. //
  615. placeholder.dataStreamStart = offset;
  616. placeholder.dataStreamSize = size;
  617. //
  618. // Get the pointers setup correctly to this buffer because the
  619. // type REPARSE_DATA_BUFFER actually doesn't have any space
  620. // allocated for the data and that is our own type, so get pointers
  621. // pointing into the real allocated space so we can use them
  622. //
  623. pReparseBuffer = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  624. WsbAffirmHr( CopyPlaceholderToRP( &placeholder, pReparseBuffer, placeholder.isTruncated ) );
  625. //
  626. // Create the real file name we need to open, under the covers this
  627. // allocates the buffer since the path pointer is null
  628. //
  629. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  630. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  631. // Save whether this was readonly for later
  632. if (S_OK == IsReadOnly()) {
  633. wasReadOnly = TRUE;
  634. }
  635. //
  636. // Make sure the file is read/write
  637. WsbAffirmHr( MakeReadWrite() );
  638. //
  639. // Open the file to put the placeholder information in the reparse point
  640. //
  641. DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
  642. WsbAffirmHr( OpenObject( path,
  643. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  644. DesiredAccess,
  645. EXCLUSIVE_FLAG,
  646. FILE_OPEN,
  647. &IoStatusBlock,
  648. &handle ) );
  649. //
  650. // The open worked, our handle should be valid but we check to be
  651. // safe and sure
  652. //
  653. WsbAssertHandle( handle );
  654. //
  655. // Tell the USN journal that we are the source of the changes.
  656. //
  657. WsbAffirmHr(m_pResource->GetPath(&volName, 0));
  658. WsbAffirmHr(WsbMarkUsnSource(handle, volName));
  659. //
  660. // Get the USN from the file now before any writes occur.
  661. // Note: NtSetInformationFile will not change the USN if you set the FileAttributes to 0
  662. // and the dates to -1. Setting the attributes to 0 leaves them unchanged.
  663. //
  664. // (For now we skip this check for read-only files because the call to MakeReadWrite
  665. // changes the USN. This needs to be fixed in the future.)
  666. //
  667. if (checkUsn && !wasReadOnly) {
  668. //
  669. // Get the current USN for this file
  670. //
  671. hr = WsbGetUsnFromFileHandle(handle, FALSE, &nowUsn);
  672. if (S_OK != hr) {
  673. nowUsn = 0;
  674. hr = S_OK;
  675. }
  676. }
  677. //
  678. // Get the current attributes of the file and the times
  679. //
  680. WsbAssertNtStatus( NtQueryInformationFile( handle,
  681. &IoStatusBlock,
  682. (PVOID)&basicInformation,
  683. sizeof( basicInformation ),
  684. FileBasicInformation ) );
  685. lastWriteTime = basicInformation.LastWriteTime.QuadPart;
  686. //
  687. // Set the time flags so that when we close the handle the
  688. // time are not updated on the file and the FileAttributes
  689. // indicate the file is offline. You must do this AFTER you
  690. // get the USN because the NtSetInformationFile changes the USN
  691. //
  692. basicInformation.CreationTime.QuadPart = -1;
  693. basicInformation.LastAccessTime.QuadPart = -1;
  694. basicInformation.LastWriteTime.QuadPart = -1;
  695. basicInformation.ChangeTime.QuadPart = -1;
  696. //
  697. // Set the attributes to 0 to avoid the usn change (the file attributes will remain unchanged).
  698. //
  699. attributes = basicInformation.FileAttributes;
  700. basicInformation.FileAttributes = 0; // No change to attributes
  701. WsbAssertNtStatus( NtSetInformationFile( handle,
  702. &IoStatusBlock,
  703. (PVOID)&basicInformation,
  704. sizeof( basicInformation ),
  705. FileBasicInformation ) );
  706. basicInformation.FileAttributes = attributes;
  707. //
  708. // Make sure that the modify time of the file matches that
  709. // of the placeholder data.
  710. //
  711. if (lastWriteTime != placeholder.fileVersionId) {
  712. //
  713. // The file has changed - don't put the reparse point on the file.
  714. //
  715. hr = FSA_E_REPARSE_NOT_WRITTEN_FILE_CHANGED;
  716. WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_WRITTEN_FILE_CHANGED, 0, NULL, WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
  717. WsbThrow( hr );
  718. } else if (checkUsn) {
  719. //
  720. // If we are to check the USN do it now
  721. //
  722. //
  723. // Rember if a USN is 0, it is not useful information so we can't
  724. // rely on it
  725. //
  726. WsbTrace(OLESTR("CFsaScanItem::CreatePlaceholder premig usn = <%I64d>, current usn <%I64d>\n"),
  727. usn, nowUsn);
  728. if ((0 != nowUsn) && (0 != usn) && (nowUsn != usn)) {
  729. //
  730. // The file has changed - don't put the reparse point on the file.
  731. //
  732. hr = FSA_E_REPARSE_NOT_WRITTEN_FILE_CHANGED;
  733. WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_WRITTEN_FILE_CHANGED, 0, NULL, WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
  734. WsbThrow( hr );
  735. }
  736. }
  737. //
  738. // Make the file able to be a sparse file
  739. // Note we assert only if the error is not a disk full error because we can get STATUS_NO_DISK_SPACE from this call and we
  740. // do not want to see the log for that error.
  741. // This is because the file must be padded out to a 16 cluster boundry before being made sparse.
  742. //
  743. // Note that this call does not affect the files data. It just enables "sparseness" for the file.
  744. //
  745. ntStatus = NtFsControlFile( handle,
  746. NULL,
  747. NULL,
  748. NULL,
  749. &IoStatusBlock,
  750. FSCTL_SET_SPARSE,
  751. NULL,
  752. 0,
  753. NULL,
  754. 0 );
  755. if (!NT_SUCCESS(ntStatus)) {
  756. if (STATUS_DISK_FULL == ntStatus) {
  757. // Check whether the disk is really full, otherwise, assume that user has passed the quota limit
  758. // Note: we ignore errors here and assume disk-full
  759. hr = CheckIfDiskFull();
  760. if (S_OK == hr) {
  761. hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
  762. } else if (S_FALSE == hr) {
  763. hr = FSA_E_REPARSE_OWNER_PASS_QUOTA;
  764. } else {
  765. // error
  766. WsbTraceAlways(OLESTR("CFsaScanItem::CreatePlaceholder: failed to check disk space after DISK_FULL error. hr=<%ls>\n"),
  767. WsbHrAsString(hr));
  768. hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
  769. }
  770. } else {
  771. hr = HRESULT_FROM_NT(ntStatus);
  772. }
  773. WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_CREATED, 0, NULL,
  774. WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
  775. WsbThrow(hr);
  776. }
  777. //
  778. // Do the work of setting the Reparse Point
  779. //
  780. ntStatus = NtFsControlFile( handle,
  781. NULL,
  782. NULL,
  783. NULL,
  784. &IoStatusBlock,
  785. FSCTL_SET_REPARSE_POINT,
  786. pReparseBuffer,
  787. FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
  788. + pReparseBuffer->ReparseDataLength,
  789. NULL,
  790. 0 );
  791. //
  792. // Check the return code, if everything worked update the in memory flag
  793. //
  794. if (!NT_SUCCESS(ntStatus)) {
  795. if (STATUS_DISK_FULL == ntStatus) {
  796. // Check whether the disk is really full, otherwise, assume that user has passed the quota limit
  797. // Note: we ignore errors here and assume disk-full
  798. hr = CheckIfDiskFull();
  799. if (S_OK == hr) {
  800. hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
  801. } else if (S_FALSE == hr) {
  802. hr = FSA_E_REPARSE_OWNER_PASS_QUOTA;
  803. } else {
  804. // error
  805. WsbTraceAlways(OLESTR("CFsaScanItem::CreatePlaceholder: failed to check disk space after DISK_FULL error. hr=<%ls>\n"),
  806. WsbHrAsString(hr));
  807. hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
  808. }
  809. } else {
  810. hr = HRESULT_FROM_NT(ntStatus);
  811. }
  812. WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_CREATED, 0, NULL,
  813. WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
  814. WsbThrow(hr);
  815. }
  816. //
  817. // Now that we change the bit, change the in memory flags for
  818. // this scan item
  819. //
  820. m_findData.dwFileAttributes |= BIT_FOR_RP;
  821. //
  822. // Set the OFFLINE attribute to indicate the correct status of
  823. // the file
  824. //
  825. if( placeholder.isTruncated ) {
  826. basicInformation.FileAttributes |= BIT_FOR_TRUNCATED;
  827. } else {
  828. basicInformation.FileAttributes &= ~BIT_FOR_TRUNCATED;
  829. }
  830. basicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL; // Just in case result was zero (then no attributes would be set)
  831. WsbAssertNtStatus( NtSetInformationFile( handle,
  832. &IoStatusBlock,
  833. (PVOID)&basicInformation,
  834. sizeof( basicInformation ),
  835. FileBasicInformation ) );
  836. //
  837. // Get the current attributes of the file and the times
  838. //
  839. WsbAssertNtStatus( NtQueryInformationFile( handle,
  840. &IoStatusBlock,
  841. (PVOID)&basicInformation,
  842. sizeof( basicInformation ),
  843. FileBasicInformation ) );
  844. //
  845. // Set the in memory copy of the attributes to the right values
  846. //
  847. m_findData.dwFileAttributes = basicInformation.FileAttributes;
  848. //
  849. // Restore original attributes if required (must be done before retrieving the USN
  850. // since changing attributes changes the USN as well)
  851. //
  852. if (TRUE == m_changedAttributes) {
  853. RestoreAttributes();
  854. }
  855. //
  856. // Before we close the file, get the USN to return to the caller
  857. // Writing the reparse information will change the USN.
  858. //
  859. hr = WsbGetUsnFromFileHandle(handle, TRUE, pUsn);
  860. if (S_OK != hr) {
  861. *pUsn = 0;
  862. hr = S_OK;
  863. }
  864. //
  865. // Close the file since we are done with it and set the handle to invalid
  866. //
  867. WsbAssertNtStatus( NtClose( handle ) );
  868. handle = INVALID_HANDLE_VALUE;
  869. //
  870. // Now that everything worked change the in memory flags for
  871. // this scan item
  872. //
  873. m_placeholder = placeholder;
  874. m_gotPlaceholder = TRUE;
  875. WsbTrace( OLESTR("(CreatePlaceholder) Reparse CRC <%ls>\n"),
  876. WsbLonglongAsString( m_placeholder.dataStreamCRC ) );
  877. } WsbCatch(hr);
  878. //
  879. // if we opened the file we need to close it
  880. //
  881. if( INVALID_HANDLE_VALUE != handle) {
  882. NtClose( handle );
  883. }
  884. WsbTraceOut(OLESTR("CFsaScanItem::CreatePlaceholder"), OLESTR("hr = <%ls>, usn = <%ls>"),
  885. WsbHrAsString(hr), WsbPtrToLonglongAsString(pUsn));
  886. return(hr);
  887. }
  888. HRESULT
  889. CFsaScanItem::DeletePlaceholder(
  890. IN LONGLONG /*offset*/,
  891. IN LONGLONG /*size*/
  892. )
  893. /*++
  894. Implements:
  895. IFsaScanItem::DeletePlaceholder().
  896. --*/
  897. {
  898. HRESULT hr = S_OK;
  899. CWsbStringPtr path;
  900. HANDLE handle = INVALID_HANDLE_VALUE;
  901. ULONG DesiredAccess;
  902. IO_STATUS_BLOCK IoStatusBlock;
  903. PREPARSE_DATA_BUFFER pReparseBuffer;
  904. UCHAR ReparseBuffer[sizeof(REPARSE_DATA_BUFFER) + sizeof(RP_DATA) + 10];
  905. NTSTATUS ntStatus;
  906. FILE_BASIC_INFORMATION basicInformation;
  907. WsbTraceIn(OLESTR("CFsaScanItem::DeletePlaceholder"), OLESTR(""));
  908. //
  909. // Remove the Reparse Point off the file
  910. //
  911. try {
  912. //
  913. // Create the real file name we need to open, under the covers this
  914. // allocates the buffer since the path pointer is null
  915. //
  916. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  917. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  918. // Make sure it is read/write
  919. WsbAffirmHr( MakeReadWrite() );
  920. //
  921. // Open the file to remove the placeholder information in the reparse point
  922. //
  923. DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
  924. WsbAffirmHr( OpenObject( path,
  925. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  926. DesiredAccess,
  927. EXCLUSIVE_FLAG,
  928. FILE_OPEN,
  929. &IoStatusBlock,
  930. &handle ) );
  931. //
  932. // The open worked, our handle should be valid but we check to be
  933. // safe and sure
  934. //
  935. WsbAssertHandle( handle );
  936. //
  937. // Get the current attributes of the file and the times
  938. //
  939. WsbAssertNtStatus( NtQueryInformationFile( handle,
  940. &IoStatusBlock,
  941. (PVOID)&basicInformation,
  942. sizeof( basicInformation ),
  943. FileBasicInformation ) );
  944. //
  945. // Set the time flags so that when we close the handle the
  946. // time are not updated on the file and the FileAttributes
  947. // indicate the file is offline
  948. //
  949. basicInformation.CreationTime.QuadPart = -1;
  950. basicInformation.LastAccessTime.QuadPart = -1;
  951. basicInformation.LastWriteTime.QuadPart = -1;
  952. basicInformation.ChangeTime.QuadPart = -1;
  953. basicInformation.FileAttributes &= ~BIT_FOR_TRUNCATED;
  954. basicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL; // Just in case result was zero (then no attributes would be set)
  955. WsbAssertNtStatus( NtSetInformationFile( handle,
  956. &IoStatusBlock,
  957. (PVOID)&basicInformation,
  958. sizeof( basicInformation ),
  959. FileBasicInformation ) );
  960. m_findData.dwFileAttributes &= ~BIT_FOR_TRUNCATED;
  961. m_originalAttributes &= ~BIT_FOR_TRUNCATED;
  962. //
  963. // Get the pointers setup correctly to this buffer because the
  964. // type REPARSE_DATA_BUFFER actually doesn't have any space
  965. // allocated for the data and that is our own type, so get pointers
  966. // pointing into the real allocated space so we can use them
  967. //
  968. pReparseBuffer = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  969. pReparseBuffer->ReparseTag = IO_REPARSE_TAG_HSM ;
  970. pReparseBuffer->ReparseDataLength = 0 ;
  971. pReparseBuffer->Reserved = 0 ;
  972. //
  973. // Do the work of deleting the Reparse Point
  974. //
  975. ntStatus = NtFsControlFile( handle,
  976. NULL,
  977. NULL,
  978. NULL,
  979. &IoStatusBlock,
  980. FSCTL_DELETE_REPARSE_POINT,
  981. pReparseBuffer,
  982. FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer),
  983. NULL,
  984. 0 );
  985. //
  986. // Check the return code - verify this is the correct way to check
  987. //
  988. WsbAssertNtStatus( ntStatus );
  989. //
  990. // Close the file since we are done with it and set the handle to invalid
  991. //
  992. WsbAssertNtStatus( NtClose( handle ) );
  993. handle = INVALID_HANDLE_VALUE;
  994. //
  995. // Now that everything worked change the in memory flags for
  996. // this scan item
  997. //
  998. m_findData.dwFileAttributes &= ~BIT_FOR_RP;
  999. m_gotPlaceholder = FALSE;
  1000. } WsbCatch(hr);
  1001. //
  1002. // if we opened the file we need to close it
  1003. //
  1004. if( INVALID_HANDLE_VALUE != handle) {
  1005. NtClose( handle );
  1006. }
  1007. WsbTraceOut(OLESTR("CFsaScanItem::DeletePlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1008. return(hr);
  1009. }
  1010. HRESULT
  1011. CFsaScanItem::GetFromRPIndex(
  1012. BOOL first
  1013. )
  1014. /*
  1015. Get file information from the Reparse Point Index
  1016. --*/
  1017. {
  1018. HRESULT hr = S_OK;
  1019. BOOLEAN bFirst;
  1020. bFirst = (BOOLEAN)( first ? TRUE : FALSE );
  1021. WsbTraceIn(OLESTR("CFsaScanItem::GetFromRPIndex"), OLESTR(""));
  1022. try {
  1023. HRESULT hrFindFileId;
  1024. IO_STATUS_BLOCK IoStatusBlock;
  1025. IFsaScanItem * pScanItem;
  1026. FILE_REPARSE_POINT_INFORMATION ReparsePointInfo;
  1027. NTSTATUS Status;
  1028. WsbAssert(0 != m_handleRPI, E_FAIL);
  1029. try_again:
  1030. Status = NtQueryDirectoryFile(m_handleRPI,
  1031. NULL, // Event
  1032. NULL, // ApcRoutine
  1033. NULL, // ApcContext
  1034. &IoStatusBlock,
  1035. &ReparsePointInfo,
  1036. sizeof(ReparsePointInfo),
  1037. FileReparsePointInformation,
  1038. TRUE, // ReturnSingleEntry
  1039. NULL, // FileName
  1040. bFirst ); // RestartScan
  1041. if (Status != STATUS_SUCCESS) {
  1042. WsbTrace(OLESTR("CFsaScanItem::GetFromRPIndex: CreateFileW failed, GetLastError = %ld\n"),
  1043. GetLastError());
  1044. WsbThrow(WSB_E_NOTFOUND);
  1045. }
  1046. // Reset some items in case this isn't the first call to
  1047. // FindFileId
  1048. if (INVALID_HANDLE_VALUE != m_handle) {
  1049. FindClose(m_handle);
  1050. m_handle = INVALID_HANDLE_VALUE;
  1051. }
  1052. if (TRUE == m_changedAttributes) {
  1053. RestoreAttributes();
  1054. }
  1055. // Find the file from the ID (not efficient or elegant, perhaps, but
  1056. // the code is already there).
  1057. pScanItem = this;
  1058. hrFindFileId = m_pResource->FindFileId(ReparsePointInfo.FileReference,
  1059. m_pSession, &pScanItem);
  1060. // If the FindFileId failed, we just skip that item and get the
  1061. // next one. This is to keep the scan from just stopping on this
  1062. // item. FindFileId could fail because the file has been deleted
  1063. // already or the NT code could have a bug that prevents finding
  1064. // the file name from the ID when the ID ends with 0x5C.
  1065. if (!SUCCEEDED(hrFindFileId)) {
  1066. bFirst = FALSE;
  1067. goto try_again;
  1068. }
  1069. WsbAffirmHr(pScanItem->Release()); // Get rid of extra ref. count
  1070. } WsbCatch(hr);
  1071. WsbTraceOut(OLESTR("CFsaScanItem::GetFromRPIndex"),
  1072. OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1073. return(hr);
  1074. }
  1075. HRESULT
  1076. CFsaScanItem::CheckUsnJournalForChanges(
  1077. LONGLONG StartUsn,
  1078. LONGLONG StopUsn,
  1079. BOOL* pChanged
  1080. )
  1081. /*
  1082. Check the USN Journal for changes to the unnamed data stream for this
  1083. file between the given USNs.
  1084. --*/
  1085. {
  1086. HRESULT hr = S_OK;
  1087. WsbTraceIn(OLESTR("CFsaScanItem::CheckUsnJournalForChanges"), OLESTR(""));
  1088. *pChanged = FALSE;
  1089. try {
  1090. LONGLONG fileId;
  1091. CWsbStringPtr volName;
  1092. ULONGLONG volUsnJournalId;
  1093. WsbAffirm(StartUsn <= StopUsn, E_UNEXPECTED);
  1094. WsbAffirmHr(m_pResource->GetPath(&volName, 0));
  1095. WsbAffirmHr(m_pResource->GetUsnId(&volUsnJournalId));
  1096. WsbAffirmHr(GetFileId(&fileId));
  1097. WsbAffirmHr(WsbCheckUsnJournalForChanges(volName, volUsnJournalId, fileId,
  1098. StartUsn, StopUsn, pChanged));
  1099. } WsbCatch(hr);
  1100. WsbTraceOut(OLESTR("CFsaScanItem::CheckUsnJournalForChanges"),
  1101. OLESTR("changed = %ls, hr = <%ls>"), WsbBoolAsString(*pChanged),
  1102. WsbHrAsString(hr));
  1103. return(hr);
  1104. }
  1105. HRESULT
  1106. CFsaScanItem::FindFirstInRPIndex(
  1107. IN IFsaResource* pResource,
  1108. IN IHsmSession* pSession
  1109. )
  1110. /*++
  1111. Implements:
  1112. IFsaResource::FindFirstInRPIndex
  1113. --*/
  1114. {
  1115. HRESULT hr = S_OK;
  1116. WsbTraceIn(OLESTR("CFsaScanItem::FindFirstInRPIndex"), OLESTR(""));
  1117. try {
  1118. CWsbStringPtr path;
  1119. WsbAssert(0 != pResource, E_POINTER);
  1120. // Store off some of the scan information.
  1121. m_pResource = pResource;
  1122. m_pSession = pSession;
  1123. // Generate the Reparse Point Index directory name for this volume
  1124. WsbAffirmHr(pResource->GetPath(&path, 0));
  1125. WsbAffirmHr(path.Prepend("\\\\?\\"));
  1126. WsbAffirmHr(path.Append("$Extend\\$Reparse:$R:$INDEX_ALLOCATION"));
  1127. WsbTrace(OLESTR("CFsaScanItem::FindFirstInRPIndex: path = <%ls>\n"),
  1128. static_cast<WCHAR*>(path));
  1129. // Open the Reparse Point Index
  1130. m_handleRPI = CreateFileW(static_cast<WCHAR*>(path),
  1131. GENERIC_READ,
  1132. FILE_SHARE_READ,
  1133. NULL,
  1134. OPEN_EXISTING,
  1135. FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION,
  1136. NULL );
  1137. if (INVALID_HANDLE_VALUE == m_handleRPI) {
  1138. WsbTrace(OLESTR("CFsaScanItem::FindFirstInRPIndex: CreateFileW failed, GetLastError = %ld\n"),
  1139. GetLastError());
  1140. WsbThrow(WSB_E_NOTFOUND);
  1141. }
  1142. // Get file information
  1143. WsbAffirmHr(GetFromRPIndex(TRUE));
  1144. } WsbCatch(hr);
  1145. WsbTraceOut(OLESTR("CFsaScanItem::FindFirstInRPIndex"),
  1146. OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1147. return(hr);
  1148. }
  1149. HRESULT
  1150. CFsaScanItem::FindFirstPlaceholder(
  1151. IN OUT LONGLONG* pOffset,
  1152. IN OUT LONGLONG* pSize,
  1153. IN OUT FSA_PLACEHOLDER* pPlaceholder
  1154. )
  1155. /*++
  1156. Implements:
  1157. IFsaScanItem::FindFirstPlaceholder().
  1158. --*/
  1159. {
  1160. HRESULT hr = S_OK;
  1161. WsbTraceIn(OLESTR("CFsaScanItem::FindFirstPlaceholder"), OLESTR(""));
  1162. try {
  1163. WsbAssert(0 != pOffset, E_POINTER);
  1164. WsbAssert(0 != pSize, E_POINTER);
  1165. WsbAssert(0 != pPlaceholder, E_POINTER);
  1166. // Until these routines get rewritten, assume that the first placeholder is the one for the
  1167. // who file that is returned by GetPlaceholder().
  1168. *pOffset = 0;
  1169. WsbAffirmHr(GetLogicalSize(pSize));
  1170. // The code above assumes that a WSB_E_NOTFOUND error will be returned if there is no
  1171. // reparse point.
  1172. try {
  1173. WsbAffirmHr(GetPlaceholder(*pOffset, *pSize, pPlaceholder));
  1174. } WsbCatchAndDo(hr, if (E_UNEXPECTED == hr) {hr = WSB_E_NOTFOUND;});
  1175. } WsbCatch(hr);
  1176. WsbTraceOut(OLESTR("CFsaScanItem::FindFirstPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1177. return(hr);
  1178. }
  1179. HRESULT
  1180. CFsaScanItem::FindNextInRPIndex(
  1181. void
  1182. )
  1183. /*++
  1184. Implements:
  1185. IFsaResource::FindNextInRPIndex
  1186. --*/
  1187. {
  1188. HRESULT hr = S_OK;
  1189. WsbTraceIn(OLESTR("CFsaScanItem::FindNextInRPIndex"), OLESTR(""));
  1190. try {
  1191. WsbAssert(0 != m_handleRPI, E_FAIL);
  1192. // Get file information
  1193. WsbAffirmHr(GetFromRPIndex(FALSE));
  1194. } WsbCatch(hr);
  1195. WsbTraceOut(OLESTR("CFsaScanItem::FindNextInRPIndex"),
  1196. OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1197. return(hr);
  1198. }
  1199. HRESULT
  1200. CFsaScanItem::FindNextPlaceholder(
  1201. IN OUT LONGLONG* pOffset,
  1202. IN OUT LONGLONG* pSize,
  1203. IN OUT FSA_PLACEHOLDER* pPlaceholder
  1204. )
  1205. /*++
  1206. Implements:
  1207. IFsaScanItem::FindNextPlaceholder().
  1208. --*/
  1209. {
  1210. HRESULT hr = S_OK;
  1211. WsbTraceIn(OLESTR("CFsaScanItem::FindNext"), OLESTR(""));
  1212. try {
  1213. WsbAssert(0 != pOffset, E_POINTER);
  1214. WsbAssert(0 != pSize, E_POINTER);
  1215. WsbAssert(0 != pPlaceholder, E_POINTER);
  1216. // Until these routines get rewritten, assume there is only one placeholder.
  1217. hr = WSB_E_NOTFOUND;
  1218. } WsbCatch(hr);
  1219. WsbTraceOut(OLESTR("CFsaScanItem::FindNextPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1220. return(hr);
  1221. }
  1222. HRESULT
  1223. CFsaScanItem::GetFileId(
  1224. OUT LONGLONG* pFileId
  1225. )
  1226. /*++
  1227. Implements:
  1228. IFsaScanItem::GetFileId().
  1229. --*/
  1230. {
  1231. HANDLE handle = INVALID_HANDLE_VALUE;
  1232. HRESULT hr = S_OK;
  1233. WsbTraceIn(OLESTR("CFsaScanItem::GetFileId"), OLESTR(""));
  1234. try {
  1235. ULONG DesiredAccess;
  1236. FILE_INTERNAL_INFORMATION iInfo;
  1237. IO_STATUS_BLOCK IoStatusBlock;
  1238. CWsbStringPtr path;
  1239. WsbAssert(0 != pFileId, E_POINTER);
  1240. //
  1241. // Create the real file name we need to open, under the covers this
  1242. // allocates the buffer since the path pointer is null
  1243. //
  1244. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  1245. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  1246. WsbTrace(OLESTR("CFsaScanItem::GetFileId, full Path = <%ls>\n"),
  1247. static_cast<WCHAR*>(path));
  1248. //
  1249. // Open the file
  1250. //
  1251. DesiredAccess = FILE_READ_ATTRIBUTES ;
  1252. WsbAffirmHr( OpenObject( path,
  1253. FILE_NON_DIRECTORY_FILE,
  1254. DesiredAccess,
  1255. SHARE_FLAGS,
  1256. FILE_OPEN,
  1257. &IoStatusBlock,
  1258. &handle ) );
  1259. //
  1260. // The open worked, our handle should be valid but we check to be
  1261. // safe and sure
  1262. //
  1263. WsbAssertHandle( handle );
  1264. // Get the internal information
  1265. WsbAssertNtStatus( NtQueryInformationFile( handle,
  1266. &IoStatusBlock,
  1267. &iInfo,
  1268. sizeof(FILE_INTERNAL_INFORMATION),
  1269. FileInternalInformation ));
  1270. // Get the file id
  1271. *pFileId = iInfo.IndexNumber.QuadPart;
  1272. //
  1273. // Close the file since we are done with it and set the handle to invalid
  1274. //
  1275. WsbAssertNtStatus( NtClose( handle ) );
  1276. handle = INVALID_HANDLE_VALUE;
  1277. } WsbCatchAndDo(hr,
  1278. WsbTrace(OLESTR("CFsaScanItem::GetFileId, GetLastError = %lx\n"),
  1279. GetLastError());
  1280. );
  1281. //
  1282. // if we opened the file we need to close it
  1283. //
  1284. if( INVALID_HANDLE_VALUE != handle) {
  1285. NtClose( handle );
  1286. }
  1287. WsbTraceOut(OLESTR("CFsaScanItem::GetFileId"), OLESTR("Hr = <%ls>, FileId = %I64x"),
  1288. WsbHrAsString(hr), *pFileId);
  1289. return(hr);
  1290. }
  1291. HRESULT
  1292. CFsaScanItem::GetFileUsn(
  1293. OUT LONGLONG* pFileUsn
  1294. )
  1295. /*++
  1296. Routine Description:
  1297. Get the current USN Journal number for this file.
  1298. Arguments:
  1299. pFileUsn - Pointer to File USN to be returned.
  1300. Return Value:
  1301. S_OK - success
  1302. --*/
  1303. {
  1304. HANDLE handle = INVALID_HANDLE_VALUE;
  1305. HRESULT hr = S_OK;
  1306. WsbTraceIn(OLESTR("CFsaScanItem::GetFileUsn"), OLESTR(""));
  1307. try {
  1308. ULONG DesiredAccess;
  1309. IO_STATUS_BLOCK IoStatusBlock;
  1310. CWsbStringPtr path;
  1311. WsbAssert(pFileUsn, E_POINTER);
  1312. //
  1313. // Create the real file name we need to open, under the covers this
  1314. // allocates the buffer since the path pointer is null
  1315. //
  1316. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  1317. WsbTrace(OLESTR("CFsaScanItem::GetFileUsn, full Path = <%ls>\n"),
  1318. static_cast<WCHAR*>(path));
  1319. //
  1320. // Open the file
  1321. //
  1322. DesiredAccess = FILE_READ_ATTRIBUTES ;
  1323. WsbAffirmHr( OpenObject( path,
  1324. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  1325. DesiredAccess,
  1326. SHARE_FLAGS,
  1327. FILE_OPEN,
  1328. &IoStatusBlock,
  1329. &handle ) );
  1330. //
  1331. // The open worked, our handle should be valid but we check to be
  1332. // safe and sure
  1333. //
  1334. WsbAssertHandle( handle );
  1335. // Get the internal information
  1336. WsbAffirmHr(WsbGetUsnFromFileHandle(handle, FALSE, pFileUsn));
  1337. //
  1338. // Close the file since we are done with it and set the handle to invalid
  1339. //
  1340. WsbAssertNtStatus( NtClose( handle ) );
  1341. handle = INVALID_HANDLE_VALUE;
  1342. } WsbCatchAndDo(hr,
  1343. WsbTrace(OLESTR("CFsaScanItem::GetFileUsn, GetLastError = %lx\n"),
  1344. GetLastError());
  1345. );
  1346. //
  1347. // if we opened the file we need to close it
  1348. //
  1349. if( INVALID_HANDLE_VALUE != handle) {
  1350. NtClose( handle );
  1351. }
  1352. WsbTraceOut(OLESTR("CFsaScanItem::GetFileUsn"), OLESTR("Hr = <%ls>, FileUsn = %I64d"),
  1353. WsbHrAsString(hr), *pFileUsn);
  1354. return(hr);
  1355. }
  1356. HRESULT
  1357. CFsaScanItem::GetPlaceholder(
  1358. IN LONGLONG offset,
  1359. IN LONGLONG size,
  1360. OUT FSA_PLACEHOLDER* pPlaceholder
  1361. )
  1362. /*++
  1363. Implements:
  1364. IFsaScanItem::GetPlaceholder().
  1365. --*/
  1366. {
  1367. WsbTraceIn(OLESTR("CFsaScanItem::GetPlaceholder"), OLESTR(""));
  1368. HRESULT hr = S_OK;
  1369. //
  1370. // If we already have the placeholder information just return it
  1371. //
  1372. try {
  1373. //
  1374. // Validate the file is managed. If it is the affirm will succeed.
  1375. // If the file is not managed then we can only tell the caller the
  1376. // problem.
  1377. //
  1378. WsbAffirmHr(hr = IsManaged(offset, size));
  1379. //
  1380. // Make sure the file is managed - will return S_OK
  1381. //
  1382. WsbAffirm( S_OK == hr, FSA_E_NOTMANAGED );
  1383. //
  1384. // Assert that the internal flag for the data is set, should
  1385. // always be on if the hr was S_OK above
  1386. //
  1387. WsbAssert( m_gotPlaceholder, E_UNEXPECTED );
  1388. //
  1389. // Copy the data to the callers structure
  1390. //
  1391. *pPlaceholder = m_placeholder;
  1392. } WsbCatch(hr);
  1393. WsbTraceOut(OLESTR("CFsaScanItem::GetPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1394. return(hr);
  1395. }
  1396. HRESULT
  1397. CFsaScanItem::HasExtendedAttributes(
  1398. void
  1399. )
  1400. /*++
  1401. Implements:
  1402. IFsaScanItem::HasExtendedAttributes().
  1403. --*/
  1404. {
  1405. HRESULT hr = S_FALSE;
  1406. HANDLE handle = INVALID_HANDLE_VALUE;
  1407. CWsbStringPtr path;
  1408. ULONG desiredAccess;
  1409. IO_STATUS_BLOCK ioStatusBlock;
  1410. FILE_EA_INFORMATION eaInformation;
  1411. try {
  1412. // Create the real file name we need to open, under the covers this
  1413. // allocates the buffer since the path pointer is null
  1414. WsbAffirmHr(GetFullPathAndName(OLESTR("\\\\?\\"), NULL, &path, 0));
  1415. //WsbAffirmHr(GetFullPathAndName(NULL, NULL, &path, 0));
  1416. // Open the file to get the attributes
  1417. desiredAccess = FILE_READ_ATTRIBUTES;
  1418. WsbAffirmHr(OpenObject(path, FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING, desiredAccess, SHARE_FLAGS,
  1419. FILE_OPEN, &ioStatusBlock, &handle));
  1420. // The open worked, our handle should be valid but we check to be
  1421. // safe and sure
  1422. WsbAssertHandle(handle);
  1423. // Get the current attributes of the file.
  1424. WsbAssertNtStatus(NtQueryInformationFile(handle, &ioStatusBlock, (VOID*) &eaInformation, sizeof(eaInformation ), FileEaInformation));
  1425. // Close the file since we are done with it and set the handle to invalid
  1426. WsbAssertNtStatus(NtClose(handle));
  1427. handle = INVALID_HANDLE_VALUE;
  1428. // Are there any EAs present?
  1429. if (eaInformation.EaSize != 0) {
  1430. hr = S_OK;
  1431. }
  1432. } WsbCatch(hr);
  1433. // if we opened the file we need to close it
  1434. if (INVALID_HANDLE_VALUE != handle) {
  1435. NtClose(handle);
  1436. }
  1437. return(hr);
  1438. }
  1439. HRESULT
  1440. CFsaScanItem::IsALink(
  1441. void
  1442. )
  1443. /*++
  1444. Implements:
  1445. IFsaScanItem::IsALink().
  1446. --*/
  1447. {
  1448. HRESULT hr = S_FALSE;
  1449. LONGLONG size;
  1450. //
  1451. // The file is a link if it is a reparse point and it is not our
  1452. // type.
  1453. //
  1454. WsbAffirmHr(GetLogicalSize(&size));
  1455. if (((m_findData.dwFileAttributes & BIT_FOR_RP) != 0) &&
  1456. (!(IsManaged(0, size) == S_OK))) {
  1457. hr = S_OK;
  1458. }
  1459. return(hr);
  1460. }
  1461. HRESULT
  1462. CFsaScanItem::IsManaged(
  1463. IN LONGLONG /*offset*/,
  1464. IN LONGLONG /*size*/
  1465. )
  1466. /*++
  1467. Implements:
  1468. IFsaScanItem::IsManaged().
  1469. --*/
  1470. {
  1471. HRESULT hr = S_FALSE;
  1472. CWsbStringPtr path;
  1473. HANDLE handle = INVALID_HANDLE_VALUE;
  1474. IO_STATUS_BLOCK IoStatusBlock;
  1475. UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  1476. NTSTATUS ntStatus;
  1477. ULONG DesiredAccess;
  1478. BOOL actualOfflineStatus = FALSE;
  1479. BOOL readReparseData = FALSE; // Used to know if we got an error reading the reparse data
  1480. BOOL changeOfflineStatus = FALSE;
  1481. FILE_BASIC_INFORMATION basicInformation;
  1482. CWsbStringPtr volName;
  1483. HRESULT saveHr;
  1484. WsbTraceIn(OLESTR("CFsaScanItem::IsManaged"), OLESTR(""));
  1485. //
  1486. // If the file has a reparse point then we need to get the information
  1487. // so we can tell if it is our type. Whether it is premigrated or
  1488. // truncate is not for this function to care, if it is either then
  1489. // the return is S_OK.
  1490. //
  1491. //
  1492. // If we already know we manage this file and have the placeholder
  1493. // information then tell caller
  1494. //
  1495. if ( m_gotPlaceholder) {
  1496. hr = S_OK;
  1497. actualOfflineStatus = m_placeholder.isTruncated;
  1498. readReparseData = TRUE;
  1499. //
  1500. // We don't know the answer so lets first check the reparse point bit.
  1501. // If it is not set then this is not managed by us
  1502. //
  1503. } else if ( (m_findData.dwFileAttributes & BIT_FOR_RP) == 0) {
  1504. hr = S_FALSE;
  1505. actualOfflineStatus = FALSE;
  1506. readReparseData = TRUE;
  1507. //
  1508. // So we know it has a reparse point but do not know what kind so
  1509. // lets get the data and fill in our global if we need
  1510. //
  1511. } else {
  1512. try {
  1513. //
  1514. // If the reparse point is not our type we get out now. This avoids a problem with SIS keeping
  1515. // the backing file open when one of their link files is open. Once we open the link file the backing file is
  1516. // opened by their filter and held open. If we attempt to migrate it later we get an error because it is open exclusive.
  1517. // This bit of code prevents us from being the one to trigger this condition - there is nothing we can do if some other
  1518. // process caused it to happen.
  1519. //
  1520. if (m_findData.dwReserved0 != IO_REPARSE_TAG_HSM) {
  1521. readReparseData = TRUE;
  1522. WsbThrow(S_FALSE);
  1523. }
  1524. //
  1525. // Create the real file name we need to open, under the
  1526. // covers this allocates the buffer since the path pointer
  1527. // is null
  1528. //
  1529. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  1530. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  1531. //
  1532. // Open the file to read the placeholder information in the reparse point
  1533. //
  1534. //DesiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES ;
  1535. DesiredAccess = FILE_READ_ATTRIBUTES ;
  1536. WsbAffirmHr( OpenObject( path,
  1537. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  1538. DesiredAccess,
  1539. SHARE_FLAGS,
  1540. FILE_OPEN,
  1541. &IoStatusBlock,
  1542. &handle ) );
  1543. //
  1544. // The open worked, our handle should be valid but we check to be
  1545. // safe and sure
  1546. //
  1547. WsbAssertHandle( handle );
  1548. //
  1549. // Read the placeholder information
  1550. //
  1551. ntStatus = NtFsControlFile( handle,
  1552. NULL,
  1553. NULL,
  1554. NULL,
  1555. &IoStatusBlock,
  1556. FSCTL_GET_REPARSE_POINT,
  1557. NULL,
  1558. 0,
  1559. &ReparseBuffer,
  1560. sizeof( ReparseBuffer ) );
  1561. //
  1562. // Verify that the get really worked. NOTE: If the reparse
  1563. // point is not there, it could be that it has been deleted since
  1564. // we last got the bits. We should just indicate that the file
  1565. // is not managed.
  1566. //
  1567. if (STATUS_NOT_A_REPARSE_POINT == ntStatus) {
  1568. readReparseData = TRUE;
  1569. WsbThrow(S_FALSE);
  1570. }
  1571. WsbAssertNtStatus( ntStatus );
  1572. //
  1573. // Close the file since we are done with it
  1574. //
  1575. WsbAssertNtStatus( NtClose( handle ) );
  1576. handle = INVALID_HANDLE_VALUE;
  1577. readReparseData = TRUE;
  1578. //
  1579. // Get the pointers setup correctly to this buffer because the
  1580. // type REPARSE_DATA_BUFFER actually doesn't have any space
  1581. // allocated for the data and that is our own type, so get pointers
  1582. // pointing into the real allocated space so we can use them
  1583. //
  1584. WsbAffirmHrOk( CopyRPToPlaceholder( (PREPARSE_DATA_BUFFER)ReparseBuffer, &m_placeholder ) );
  1585. actualOfflineStatus = m_placeholder.isTruncated;
  1586. //
  1587. // Set flag indicating placeholder found and information in memory
  1588. //
  1589. m_gotPlaceholder = TRUE;
  1590. hr = S_OK;
  1591. } WsbCatch(hr);
  1592. //
  1593. // if we opened the file we need to close it
  1594. //
  1595. if( INVALID_HANDLE_VALUE != handle) {
  1596. NtClose( handle );
  1597. }
  1598. }
  1599. saveHr = hr;
  1600. // Check the actual offline status against the offline bit and fix it if necessary.
  1601. if (readReparseData) { // If there was no error getting the reparse data
  1602. WsbTrace(OLESTR("CFsaScanItem::IsManaged: Checking offline status %x - actual = %x\n"),
  1603. m_findData.dwFileAttributes & BIT_FOR_TRUNCATED, actualOfflineStatus );
  1604. switch (actualOfflineStatus) {
  1605. case TRUE:
  1606. if (!(m_findData.dwFileAttributes & BIT_FOR_TRUNCATED)) {
  1607. // Offline bit is not set and should be - set it.
  1608. m_findData.dwFileAttributes |= BIT_FOR_TRUNCATED;
  1609. m_originalAttributes |= BIT_FOR_TRUNCATED; // Just in case we have changed to read/write;
  1610. changeOfflineStatus = TRUE;
  1611. }
  1612. break;
  1613. case FALSE:
  1614. if (m_findData.dwFileAttributes & BIT_FOR_TRUNCATED) {
  1615. // Offline bit is set and should not be - clear it.
  1616. m_findData.dwFileAttributes &= ~BIT_FOR_TRUNCATED;
  1617. m_originalAttributes &= ~BIT_FOR_TRUNCATED; // Just in case we have changed to read/write;
  1618. changeOfflineStatus = TRUE;
  1619. }
  1620. break;
  1621. }
  1622. if (changeOfflineStatus) {
  1623. // Set the new attribute
  1624. WsbTrace(OLESTR("CFsaScanItem::IsManaged: Changing offline status %x - actual = %x\n"),
  1625. m_findData.dwFileAttributes & BIT_FOR_TRUNCATED, actualOfflineStatus );
  1626. try {
  1627. //
  1628. // Create the real file name we need to open, under the
  1629. // covers this allocates the buffer since the path pointer
  1630. // is null
  1631. //
  1632. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  1633. //
  1634. // Open the file to set attributes
  1635. //
  1636. DesiredAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
  1637. WsbAffirmHr( OpenObject( path,
  1638. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  1639. DesiredAccess,
  1640. SHARE_FLAGS,
  1641. FILE_OPEN,
  1642. &IoStatusBlock,
  1643. &handle ) );
  1644. //
  1645. // The open worked, our handle should be valid but we check to be
  1646. // safe and sure
  1647. //
  1648. WsbAssertHandle( handle );
  1649. WsbAffirmHr(m_pResource->GetPath(&volName, 0));
  1650. WsbAffirmHr(WsbMarkUsnSource(handle, volName));
  1651. // Set the time flags so that when we close the handle the
  1652. // time are not updated on the file and the FileAttributes
  1653. basicInformation.CreationTime.QuadPart = -1;
  1654. basicInformation.LastAccessTime.QuadPart = -1;
  1655. basicInformation.LastWriteTime.QuadPart = -1;
  1656. basicInformation.ChangeTime.QuadPart = -1;
  1657. basicInformation.FileAttributes = m_findData.dwFileAttributes;
  1658. WsbAffirmNtStatus(NtSetInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
  1659. //
  1660. // Close the file since we are done with it
  1661. //
  1662. WsbAssertNtStatus( NtClose( handle ) );
  1663. handle = INVALID_HANDLE_VALUE;
  1664. } WsbCatch(hr);
  1665. //
  1666. // if we opened the file we need to close it
  1667. //
  1668. if( INVALID_HANDLE_VALUE != handle) {
  1669. NtClose( handle );
  1670. }
  1671. }
  1672. }
  1673. hr = saveHr;
  1674. WsbTraceOut(OLESTR("CFsaScanItem::IsManaged"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1675. return(hr);
  1676. }
  1677. HRESULT
  1678. CFsaScanItem::IsPremigrated(
  1679. IN LONGLONG offset,
  1680. IN LONGLONG size
  1681. )
  1682. /*++
  1683. Implements:
  1684. IFsaScanItem::IsPremigrated().
  1685. --*/
  1686. {
  1687. HRESULT hr = S_FALSE;
  1688. HRESULT hrTest = S_FALSE;
  1689. WsbTraceIn(OLESTR("CFsaScanItem::IsPremigrated"), OLESTR(""));
  1690. // We really need to look at the placeholder information to figure
  1691. // this out (is offline, and is out type of HSM.
  1692. //
  1693. // If the file is NOT truncated AND is a reparse point and is a
  1694. // managed one then the file is a premigrated file
  1695. //
  1696. // if ( !(m_findData.dwFileAttributes & BIT_FOR_TRUNCATED) &&
  1697. // m_findData.dwFileAttributes & BIT_FOR_RP &&
  1698. // IsManaged() == S_OK) {
  1699. try {
  1700. if ( m_findData.dwFileAttributes & BIT_FOR_RP ) {
  1701. WsbAffirmHr(hrTest = IsManaged(offset, size));
  1702. if ((S_OK == hrTest) &&
  1703. ( !m_placeholder.isTruncated )) {
  1704. hr = S_OK;
  1705. }
  1706. }
  1707. } WsbCatch (hr);
  1708. WsbTraceOut(OLESTR("CFsaScanItem::IsPremigrated"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1709. return(hr);
  1710. }
  1711. HRESULT
  1712. CFsaScanItem::IsTruncated(
  1713. IN LONGLONG offset,
  1714. IN LONGLONG size
  1715. )
  1716. /*++
  1717. Implements:
  1718. IFsaScanItem::IsTruncated().
  1719. --*/
  1720. {
  1721. HRESULT hr = S_FALSE;
  1722. HRESULT hrTest = S_FALSE;
  1723. WsbTraceIn(OLESTR("CFsaScanItem::IsTruncated"), OLESTR(""));
  1724. //
  1725. // If the bit is on that indicates we have truncated the file AND
  1726. // the file is a reparse point AND the reparse point is one of
  1727. // our types (i.e. it really is our information stuffed away in
  1728. // there the it really is a truncated file
  1729. //
  1730. // if ( // ???? m_findData.dwFileAttributes & BIT_FOR_TRUNCATED &&
  1731. // m_findData.dwFileAttributes & BIT_FOR_RP &&
  1732. // IsManaged() == S_OK && RP_FILE_IS_TRUNCATED( m_placeholder.bitFlags ) ) {
  1733. try {
  1734. if ( m_findData.dwFileAttributes & BIT_FOR_RP ) {
  1735. WsbAffirmHr(hrTest = IsManaged(offset, size));
  1736. if ((S_OK == hrTest) &&
  1737. ( m_placeholder.isTruncated )) {
  1738. hr = S_OK;
  1739. }
  1740. }
  1741. } WsbCatch (hr);
  1742. WsbTraceOut(OLESTR("CFsaScanItem::IsTruncated"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1743. return(hr);
  1744. }
  1745. HRESULT
  1746. CFsaScanItem::GetVersionId(
  1747. LONGLONG *fileVersionId
  1748. )
  1749. /*++
  1750. Implements:
  1751. IFsaScanItem::GetVersionId().
  1752. --*/
  1753. {
  1754. HRESULT hr = E_FAIL;
  1755. HANDLE handle = INVALID_HANDLE_VALUE;
  1756. CWsbStringPtr path;
  1757. ULONG DesiredAccess;
  1758. IO_STATUS_BLOCK IoStatusBlock;
  1759. FILE_BASIC_INFORMATION basicInformation;
  1760. try {
  1761. //
  1762. // Create the real file name we need to open, under the covers this
  1763. // allocates the buffer since the path pointer is null
  1764. //
  1765. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  1766. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  1767. //
  1768. // Open the file to get the attributes
  1769. //
  1770. DesiredAccess = FILE_READ_ATTRIBUTES ;
  1771. WsbAffirmHr( OpenObject( path,
  1772. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  1773. DesiredAccess,
  1774. SHARE_FLAGS,
  1775. FILE_OPEN,
  1776. &IoStatusBlock,
  1777. &handle ) );
  1778. //
  1779. // The open worked, our handle should be valid but we check to be
  1780. // safe and sure
  1781. //
  1782. WsbAssertHandle( handle );
  1783. //
  1784. // Get the current attributes of the file and the times
  1785. //
  1786. WsbAssertNtStatus( NtQueryInformationFile( handle,
  1787. &IoStatusBlock,
  1788. (PVOID)&basicInformation,
  1789. sizeof( basicInformation ),
  1790. FileBasicInformation ) );
  1791. //
  1792. // Close the file since we are done with it and set the handle to invalid
  1793. //
  1794. WsbAssertNtStatus( NtClose( handle ) );
  1795. handle = INVALID_HANDLE_VALUE;
  1796. *fileVersionId = basicInformation.LastWriteTime.QuadPart;
  1797. hr = S_OK;
  1798. } WsbCatch( hr );
  1799. //
  1800. // if we opened the file we need to close it
  1801. //
  1802. if( INVALID_HANDLE_VALUE != handle) {
  1803. NtClose( handle );
  1804. }
  1805. return( hr );
  1806. }
  1807. HRESULT
  1808. CFsaScanItem::MakeReadWrite(
  1809. )
  1810. /*++
  1811. Routine Description:
  1812. Make the file attributes read/write if they aren't already.
  1813. Arguments:
  1814. pUsn - Pointer to File USN to check (if != 0) and to be returned after the change.
  1815. Return Value:
  1816. S_OK - success
  1817. --*/
  1818. {
  1819. HRESULT hr = S_OK;
  1820. CWsbStringPtr path;
  1821. IO_STATUS_BLOCK IoStatusBlock;
  1822. HANDLE handle = INVALID_HANDLE_VALUE;
  1823. FILE_BASIC_INFORMATION basicInformation;
  1824. if (S_OK == IsReadOnly()) {
  1825. try {
  1826. // NOTE: MakeReadOnly(), IsReadOnly(), and RestoreAttributes() seem like dangerous implementations, since
  1827. // the used cached information and reset all the attirbutes. It is also assuming that the
  1828. // application wants the file reset to read only after FindNext() or the destructor. This
  1829. // may not be true for a general purpose application. Unfortunately, it seems to risky to
  1830. // try to change this implementation now.
  1831. // Create the real file name we need to open, under the covers this
  1832. // allocates the buffer since the path pointer is null
  1833. WsbAffirmHr(GetFullPathAndName(OLESTR("\\\\?\\"), NULL, &path, 0));
  1834. // Open the file.
  1835. WsbAffirmHr(OpenObject(path, FILE_NON_DIRECTORY_FILE, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, EXCLUSIVE_FLAG, FILE_OPEN, &IoStatusBlock, &handle));
  1836. // The open worked, our handle should be valid but we check to be
  1837. // safe and sure
  1838. WsbAffirmHandle(handle);
  1839. // Get the current attributes of the file and the times
  1840. WsbAffirmNtStatus(NtQueryInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
  1841. // Make sure it is still read only.
  1842. if ((basicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
  1843. m_originalAttributes = basicInformation.FileAttributes;
  1844. // Set the time flags so that when we close the handle the
  1845. // time are not updated on the file and the FileAttributes
  1846. basicInformation.CreationTime.QuadPart = -1;
  1847. basicInformation.LastAccessTime.QuadPart = -1;
  1848. basicInformation.LastWriteTime.QuadPart = -1;
  1849. basicInformation.ChangeTime.QuadPart = -1;
  1850. basicInformation.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
  1851. basicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL; // Just in case result was zero (then no attributes would be set)
  1852. WsbAffirmNtStatus(NtSetInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
  1853. m_changedAttributes = TRUE;
  1854. }
  1855. // Close the file
  1856. NtClose(handle);
  1857. handle = INVALID_HANDLE_VALUE;
  1858. } WsbCatch(hr);
  1859. // Close the file for sure
  1860. if (INVALID_HANDLE_VALUE != handle) {
  1861. NtClose(handle);
  1862. }
  1863. }
  1864. return(hr);
  1865. }
  1866. HRESULT
  1867. CFsaScanItem::PrepareForManage(
  1868. IN LONGLONG offset,
  1869. IN LONGLONG size
  1870. )
  1871. /*++
  1872. Implements:
  1873. IFsaScanItem::PrepareForManage().
  1874. --*/
  1875. {
  1876. UNREFERENCED_PARAMETER(offset);
  1877. UNREFERENCED_PARAMETER(size);
  1878. return S_OK;
  1879. }
  1880. HRESULT
  1881. CFsaScanItem::RestoreAttributes(
  1882. )
  1883. /*++
  1884. Implements:
  1885. IFsaScanItem::RestoreAttributes
  1886. --*/
  1887. {
  1888. HRESULT hr = E_FAIL;
  1889. CWsbStringPtr path;
  1890. IO_STATUS_BLOCK IoStatusBlock;
  1891. HANDLE handle = INVALID_HANDLE_VALUE;
  1892. FILE_BASIC_INFORMATION basicInformation;
  1893. try {
  1894. // NOTE: MakeReadOnly(), IsReadOnly(), and RestoreAttributes() seem like dangerous implementations, since
  1895. // the used cached information and reset all the attirbutes. It is also assuming that the
  1896. // application wants the file reset to read only after FindNext() or the destructor. This
  1897. // may not be true for a general purpose application. Unfortunately, it seems to risky to
  1898. // try to change this implementation now.
  1899. // Create the real file name we need to open, under the covers this
  1900. // allocates the buffer since the path pointer is null
  1901. WsbTrace(OLESTR("CFsaScanItem::RestoreAttributes - Restoring attributes to %x"), m_originalAttributes);
  1902. WsbAffirmHr(GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  1903. // Open the file.
  1904. WsbAffirmHr(OpenObject(path, FILE_NON_DIRECTORY_FILE, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, EXCLUSIVE_FLAG, FILE_OPEN, &IoStatusBlock, &handle));
  1905. // The open worked, our handle should be valid but we check to be
  1906. // safe and sure
  1907. WsbAffirmHandle(handle);
  1908. // Get the current attributes of the file and the times
  1909. WsbAffirmNtStatus(NtQueryInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
  1910. // Set the time flags so that when we close the handle the
  1911. // time are not updated on the file and the FileAttributes
  1912. basicInformation.CreationTime.QuadPart = -1;
  1913. basicInformation.LastAccessTime.QuadPart = -1;
  1914. basicInformation.LastWriteTime.QuadPart = -1;
  1915. basicInformation.ChangeTime.QuadPart = -1;
  1916. basicInformation.FileAttributes = m_originalAttributes;
  1917. WsbAffirmNtStatus(NtSetInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
  1918. // Close the file
  1919. NtClose(handle);
  1920. handle = INVALID_HANDLE_VALUE;
  1921. m_changedAttributes = FALSE;
  1922. } WsbCatch(hr);
  1923. // Close the file for sure
  1924. if (INVALID_HANDLE_VALUE != handle) {
  1925. NtClose(handle);
  1926. }
  1927. return(hr);
  1928. }
  1929. HRESULT
  1930. CFsaScanItem::Truncate(
  1931. IN LONGLONG offset,
  1932. IN LONGLONG size
  1933. )
  1934. /*++
  1935. Implements:
  1936. IFsaScanItem::Truncate().
  1937. --*/
  1938. {
  1939. HRESULT hr = S_OK;
  1940. BOOL fileIsTruncated = FALSE;
  1941. LONGLONG usn = 0;
  1942. WsbTraceIn(OLESTR("CFsaScanItem::Truncate"), OLESTR(""));
  1943. try {
  1944. // call the engine
  1945. if (IsManaged(offset, size) == S_OK) {
  1946. WsbAffirmHr(m_pResource->ValidateForTruncate((IFsaScanItem*) this, offset, size, usn));
  1947. }
  1948. } WsbCatch(hr);
  1949. WsbTraceOut(OLESTR("CFsaScanItem::Truncate"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1950. return(hr);
  1951. }
  1952. HRESULT
  1953. CFsaScanItem::TruncateValidated(
  1954. IN LONGLONG offset,
  1955. IN LONGLONG size
  1956. )
  1957. /*++
  1958. Implements:
  1959. IFsaScanItem::TruncateValidated().
  1960. --*/
  1961. {
  1962. HRESULT hr = S_OK;
  1963. HRESULT truncateHr = S_OK;
  1964. WsbTraceIn(OLESTR("CFsaScanItem::TruncateValidated"), OLESTR(""));
  1965. try {
  1966. IFsaScanItem* pMe = this;
  1967. truncateHr = TruncateInternal(offset, size);
  1968. //
  1969. // Note: Must check for S_OK since TruncateInternal may return FSA_E_ITEMCHANGED or FSA_E_ITEMINUSE
  1970. // Both are "Success hr", but imply no truncation was done
  1971. //
  1972. if (S_OK == truncateHr) {
  1973. WsbAffirmHr(m_pResource->RemovePremigrated(pMe, offset, size));
  1974. WsbAffirmHr(m_pResource->AddTruncated(pMe, offset, size));
  1975. }
  1976. } WsbCatch(hr);
  1977. // The important hr to return to the caller is the actual result of the truncation
  1978. hr = truncateHr;
  1979. WsbTraceOut(OLESTR("CFsaScanItem::TruncateValidated"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  1980. return(hr);
  1981. }
  1982. HRESULT
  1983. CFsaScanItem::TruncateInternal(
  1984. IN LONGLONG offset,
  1985. IN LONGLONG size
  1986. )
  1987. /*++
  1988. Implements:
  1989. IFsaScanItem::TruncateInternal().
  1990. --*/
  1991. {
  1992. HRESULT hr = E_FAIL;
  1993. CWsbStringPtr path;
  1994. ULONG DesiredAccess;
  1995. IO_STATUS_BLOCK IoStatusBlock;
  1996. NTSTATUS ntStatus;
  1997. FILE_END_OF_FILE_INFORMATION sizeInformation;
  1998. FILE_BASIC_INFORMATION basicInformation;
  1999. PREPARSE_DATA_BUFFER pReparseBuffer;
  2000. UCHAR ReparseBuffer[sizeof(REPARSE_DATA_BUFFER) + sizeof(RP_DATA) + 10];
  2001. CWsbStringPtr fileName;
  2002. CWsbStringPtr jobName;
  2003. LONGLONG fileVersionId;
  2004. ULONG i = 0;
  2005. CWsbStringPtr volName;
  2006. WsbTraceIn(OLESTR("CFsaScanItem::TruncateInternal"), OLESTR(""));
  2007. // Putting these statistics in the registry is probably not the best
  2008. // place for them, but it's the easiest solution for now
  2009. #define TEMPORARY_TRUNCATE_STATISTICS 1
  2010. #if defined(TEMPORARY_TRUNCATE_STATISTICS)
  2011. // Try to increment the truncate-attempt count in the registry
  2012. WsbIncRegistryValueDWORD(NULL, FSA_REGISTRY_PARMS,
  2013. OLESTR("TruncateCalls"));
  2014. #endif
  2015. // Get strings for tracing and error logging (ignore errors?!)
  2016. GetFullPathAndName( 0, 0, &fileName, 0);
  2017. m_pSession->GetName(&jobName, 0);
  2018. m_handleVerify = INVALID_HANDLE_VALUE;
  2019. try {
  2020. LONGLONG fileUsn1 = 0, fileUsn2 = 0;
  2021. // If the file is not migrated, then we can't truncate it
  2022. if (S_OK != IsPremigrated(offset, size)) {
  2023. if (S_OK != IsManaged(offset, size)) {
  2024. hr = FSA_E_NOTMANAGED;
  2025. WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISNOTMANAGED, 0, NULL,
  2026. (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120),
  2027. WsbHrAsString(hr), NULL);
  2028. WsbThrow(hr);
  2029. } else {
  2030. //
  2031. // Do not bother to log an event here as this should only
  2032. // happen if someone uses rstest or some other program
  2033. // to truncate a file that is already truncated.
  2034. WsbThrow(FSA_E_FILE_ALREADY_MANAGED);
  2035. }
  2036. }
  2037. WsbAssert( m_gotPlaceholder, E_UNEXPECTED );
  2038. //
  2039. // Setup the reparse point data with that which was on the file
  2040. // with the bit in the data indicating it is truncated
  2041. //
  2042. pReparseBuffer = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  2043. WsbAffirmHr( CopyPlaceholderToRP( &m_placeholder, pReparseBuffer, TRUE ) );
  2044. //
  2045. // Create the real file name we need to open, under the covers this
  2046. // allocates the buffer since the path pointer is null
  2047. //
  2048. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  2049. //
  2050. // Open the file exclusively for read-only so we can get the usn before and after
  2051. // making the file R/W, without letting anybody to make a "real" change in the middle
  2052. //
  2053. DesiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES;
  2054. WsbAffirmHr( OpenObject( path,
  2055. FILE_NON_DIRECTORY_FILE,
  2056. DesiredAccess,
  2057. EXCLUSIVE_FLAG,
  2058. FILE_OPEN,
  2059. &IoStatusBlock,
  2060. &m_handleVerify ) );
  2061. WsbAssertHandle( m_handleVerify );
  2062. //
  2063. // Get usn before making R/W
  2064. // This usn is used to compare with the usn which we kept in the premigrated list.
  2065. // MakeReadWrite may chnage the usn so we need to get it before
  2066. //
  2067. if (S_OK != WsbGetUsnFromFileHandle(m_handleVerify, FALSE, &fileUsn1)) {
  2068. fileUsn1 = 0;
  2069. }
  2070. // Make sure it is read/write
  2071. WsbAffirmHr( MakeReadWrite() );
  2072. //
  2073. // Get usn after making R/W
  2074. // This usn will be use to compare with the usn of the file after we'll open it for R/W. We need
  2075. // this comparison in order to ensure that nobody changed the file before we opened it again for R/W.
  2076. //
  2077. if (S_OK != WsbGetUsnFromFileHandle(m_handleVerify, TRUE, &fileUsn2)) {
  2078. fileUsn2 = 0;
  2079. }
  2080. // Close the file
  2081. NtClose( m_handleVerify );
  2082. m_handleVerify = INVALID_HANDLE_VALUE;
  2083. //
  2084. // Open the file (for R/W)
  2085. //
  2086. DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
  2087. WsbAffirmHr( OpenObject( path,
  2088. FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH,
  2089. DesiredAccess,
  2090. EXCLUSIVE_FLAG,
  2091. FILE_OPEN,
  2092. &IoStatusBlock,
  2093. &m_handleVerify ) );
  2094. //
  2095. // The open worked, our handle should be valid but we check to be
  2096. // safe and sure
  2097. //
  2098. WsbAssertHandle( m_handleVerify );
  2099. //
  2100. // Tell the USN journal that we are the source of the changes.
  2101. //
  2102. WsbAffirmHr(m_pResource->GetPath(&volName, 0));
  2103. WsbAffirmHr(WsbMarkUsnSource(m_handleVerify, volName));
  2104. //
  2105. // Get the current attributes of the file and the times
  2106. //
  2107. WsbAffirmNtStatus( NtQueryInformationFile( m_handleVerify,
  2108. &IoStatusBlock,
  2109. (PVOID)&basicInformation,
  2110. sizeof( basicInformation ),
  2111. FileBasicInformation ) );
  2112. fileVersionId = basicInformation.LastWriteTime.QuadPart;
  2113. //
  2114. // Set the time flags so that when we close the handle the
  2115. // times are not updated on the file and the FileAttributes
  2116. // indicate the file is offline
  2117. //
  2118. basicInformation.CreationTime.QuadPart = -1;
  2119. basicInformation.LastAccessTime.QuadPart = -1;
  2120. basicInformation.LastWriteTime.QuadPart = -1;
  2121. basicInformation.ChangeTime.QuadPart = -1;
  2122. basicInformation.FileAttributes = 0; // Do not change attributes yet
  2123. WsbAffirmNtStatus( NtSetInformationFile( m_handleVerify,
  2124. &IoStatusBlock,
  2125. (PVOID)&basicInformation,
  2126. sizeof( basicInformation ),
  2127. FileBasicInformation ) );
  2128. //
  2129. // Do the check to see if the file changed
  2130. //
  2131. hr = VerifyInternal(offset, size, fileUsn1, fileUsn2);
  2132. //
  2133. // Note: Must check for S_OK since VerifyInternal may return FSA_E_ITEMCHANGED or FSA_E_ITEMINUSE
  2134. // Both are "Success hr", but should cause no truncation !!
  2135. //
  2136. if (S_OK != hr) {
  2137. WsbThrow(hr);
  2138. }
  2139. //
  2140. // Change the in memory flags for this scan item
  2141. //
  2142. m_findData.dwFileAttributes |= BIT_FOR_TRUNCATED;
  2143. //
  2144. // Rewrite the reparse point with the new flag
  2145. //
  2146. ntStatus = NtFsControlFile( m_handleVerify,
  2147. NULL,
  2148. NULL,
  2149. NULL,
  2150. &IoStatusBlock,
  2151. FSCTL_SET_REPARSE_POINT,
  2152. pReparseBuffer,
  2153. FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
  2154. + pReparseBuffer->ReparseDataLength,
  2155. NULL,
  2156. 0 );
  2157. //
  2158. // Check the return code
  2159. //
  2160. WsbAffirmNtStatus( ntStatus );
  2161. //
  2162. // It really happened so we need to flip the in memory copy of the
  2163. // isTruncated flag so it reflects reality
  2164. //
  2165. m_placeholder.isTruncated = TRUE;
  2166. //
  2167. // Set the file size to zero to truncate the file
  2168. sizeInformation.EndOfFile.QuadPart = 0 ;
  2169. WsbAffirmNtStatus( NtSetInformationFile( m_handleVerify,
  2170. &IoStatusBlock,
  2171. &sizeInformation,
  2172. sizeof( sizeInformation ),
  2173. FileEndOfFileInformation ) );
  2174. //
  2175. // Set the logical file size to the original size
  2176. sizeInformation.EndOfFile.QuadPart = m_placeholder.dataStreamSize;
  2177. WsbAffirmNtStatus( NtSetInformationFile( m_handleVerify,
  2178. &IoStatusBlock,
  2179. &sizeInformation,
  2180. sizeof( sizeInformation ),
  2181. FileEndOfFileInformation ) );
  2182. //
  2183. // Now that the truncation is complete we set the OFFLINE attribute.
  2184. //
  2185. basicInformation.CreationTime.QuadPart = -1; // Make sure we do nothing with dates
  2186. basicInformation.LastAccessTime.QuadPart = -1;
  2187. basicInformation.LastWriteTime.QuadPart = -1;
  2188. basicInformation.ChangeTime.QuadPart = -1;
  2189. basicInformation.FileAttributes = m_findData.dwFileAttributes;
  2190. WsbAffirmNtStatus(NtSetInformationFile( m_handleVerify,
  2191. &IoStatusBlock,
  2192. (PVOID)&basicInformation,
  2193. sizeof( basicInformation ),
  2194. FileBasicInformation ));
  2195. // Since we have restored the original attributes we can reset the flag that was possibly set by MakeReadWrite
  2196. m_changedAttributes = FALSE;
  2197. hr = S_OK;
  2198. } WsbCatch(hr);
  2199. //
  2200. // if we opened the file we need to close it
  2201. //
  2202. if( INVALID_HANDLE_VALUE != m_handleVerify) {
  2203. NtClose( m_handleVerify );
  2204. m_handleVerify = INVALID_HANDLE_VALUE;
  2205. }
  2206. // If the file data had changed (so we didn't truncate it) log event and
  2207. // remove placeholder info
  2208. if (FSA_E_ITEMCHANGED == hr) {
  2209. WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISCHANGED, 0, NULL,
  2210. (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 80),
  2211. WsbHrAsString(hr), NULL);
  2212. DeletePlaceholder(offset, size);
  2213. }
  2214. WsbTraceOut(OLESTR("CFsaScanItem::TruncateInternal"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  2215. return(hr);
  2216. }
  2217. HRESULT
  2218. CFsaScanItem::Verify(
  2219. IN LONGLONG offset,
  2220. IN LONGLONG size
  2221. )
  2222. /*++
  2223. Implements:
  2224. IFsaScanItem::Verify().
  2225. --*/
  2226. {
  2227. HRESULT hr = E_FAIL;
  2228. CWsbStringPtr path;
  2229. ULONG DesiredAccess;
  2230. IO_STATUS_BLOCK IoStatusBlock;
  2231. WsbTraceIn(OLESTR("CFsaScanItem::Verify"), OLESTR(""));
  2232. m_handleVerify = INVALID_HANDLE_VALUE;
  2233. try {
  2234. WsbAssert( m_gotPlaceholder, E_UNEXPECTED );
  2235. //
  2236. // Create the real file name we need to open, under the covers this
  2237. // allocates the buffer since the path pointer is null
  2238. //
  2239. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  2240. //
  2241. // Open the file
  2242. //
  2243. DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
  2244. WsbAffirmHr( OpenObject( path,
  2245. FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH,
  2246. DesiredAccess,
  2247. EXCLUSIVE_FLAG,
  2248. FILE_OPEN,
  2249. &IoStatusBlock,
  2250. &m_handleVerify ) );
  2251. //
  2252. // The open worked, our handle should be valid but we check to be
  2253. // safe and sure
  2254. //
  2255. WsbAssertHandle( m_handleVerify );
  2256. //
  2257. // Do the check to see if the file changed
  2258. // Note that it throws rather than affirms because FSA_E_ITEMCHANGED is a success
  2259. WsbThrow(VerifyInternal(offset, size, 0, 0));
  2260. } WsbCatch(hr);
  2261. //
  2262. // if we opened the file we need to close it
  2263. //
  2264. if( INVALID_HANDLE_VALUE != m_handleVerify) {
  2265. NtClose( m_handleVerify );
  2266. m_handleVerify = INVALID_HANDLE_VALUE;
  2267. }
  2268. WsbTraceOut(OLESTR("CFsaScanItem::Verify"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  2269. return(hr);
  2270. }
  2271. HRESULT
  2272. CFsaScanItem::VerifyInternal(
  2273. IN LONGLONG offset,
  2274. IN LONGLONG size,
  2275. IN LONGLONG compareUsn1,
  2276. IN LONGLONG compareUsn2
  2277. )
  2278. /*++
  2279. Implements:
  2280. IFsaScanItem::VerifyInternal().
  2281. Note: This requires that m_handleVerify is set up with a handle to the file being verified.
  2282. --*/
  2283. {
  2284. HRESULT hr = E_FAIL;
  2285. IO_STATUS_BLOCK IoStatusBlock;
  2286. FILE_BASIC_INFORMATION basicInformation;
  2287. CWsbStringPtr fileName;
  2288. CWsbStringPtr jobName;
  2289. LONGLONG fileVersionId;
  2290. ULONG i = 0;
  2291. CWsbStringPtr volName;
  2292. LONGLONG realFileSize;
  2293. RP_MSG in, out;
  2294. DWORD outSize;
  2295. WsbTraceIn(OLESTR("CFsaScanItem::VerifyInternal"), OLESTR(""));
  2296. // Get strings for tracing and error logging (ignore errors?!)
  2297. GetFullPathAndName( 0, 0, &fileName, 0);
  2298. m_pSession->GetName(&jobName, 0);
  2299. try {
  2300. BOOL DoCRC = FALSE;
  2301. BOOL DoUsnCheck = FALSE;
  2302. LONGLONG premigListUsn;
  2303. LONGLONG fileUsn;
  2304. WsbAffirmHr(GetLogicalSize(&realFileSize));
  2305. //
  2306. // Currently, avoid offset and size verification:
  2307. // - Since we are not doing partial file migration, offset is always 0.
  2308. // - Size in Validate would always be the same since it is taken from GetLogicalSize as well.
  2309. // - Size in case of auto-truncation is not reliable since it is taken from the premigrated db,
  2310. // where there could be bogus records from previous migrations of the file
  2311. //
  2312. /*** if ( (realFileSize != size) || (offset != 0) ) {
  2313. WsbThrow(FSA_E_ITEMCHANGED);
  2314. } ***/
  2315. UNREFERENCED_PARAMETER(size);
  2316. //
  2317. WsbAssertHandle( m_handleVerify );
  2318. //
  2319. // Get the USN from the premigration list and the USN from the file.
  2320. // We need to get the USN from the file now, before any NtSetInformationFile
  2321. // is called because this changes the USN value.
  2322. // If we have trouble getting the USN, just set them
  2323. // to 0 and go on, we check for 0 as a special case.
  2324. //
  2325. if (S_OK != GetPremigratedUsn(&premigListUsn)) {
  2326. premigListUsn = 0;
  2327. }
  2328. if (S_OK != WsbGetUsnFromFileHandle(m_handleVerify, FALSE, &fileUsn)) {
  2329. fileUsn = 0;
  2330. }
  2331. WsbTrace(OLESTR("CFsaScanItem::VerifyInternal: premig USN <%I64d>, file USN <%I64d>\n"),
  2332. premigListUsn, fileUsn );
  2333. WsbTrace(OLESTR("CFsaScanItem::VerifyInternal: Compare1 USN <%I64d>, Compare2 USN <%I64d>\n"),
  2334. compareUsn1, compareUsn2 );
  2335. //
  2336. // Get the current attributes of the file and the times
  2337. //
  2338. WsbAssertNtStatus( NtQueryInformationFile( m_handleVerify,
  2339. &IoStatusBlock,
  2340. (PVOID)&basicInformation,
  2341. sizeof( basicInformation ),
  2342. FileBasicInformation ) );
  2343. fileVersionId = basicInformation.LastWriteTime.QuadPart;
  2344. //
  2345. // Verify that the modify date & time has not changed since we
  2346. // took the data
  2347. //
  2348. if( fileVersionId != m_placeholder.fileVersionId ) {
  2349. WsbThrow(FSA_E_ITEMCHANGED);
  2350. }
  2351. //
  2352. // If the file is memory mapped by another process and the original handle was closed we
  2353. // are still able to open it for exclusive access here. We have to determine if the file
  2354. // is mapped and if so we cannot truncate it. The only way to do this is from kernel
  2355. // mode so we call our filter to do the check.
  2356. //
  2357. in.inout.command = RP_CHECK_HANDLE;
  2358. WsbAssertStatus(DeviceIoControl(m_handleVerify, FSCTL_HSM_MSG, &in,
  2359. sizeof(RP_MSG), &out, sizeof(RP_MSG), &outSize, NULL));
  2360. if (!out.msg.hRep.canTruncate) {
  2361. hr = FSA_E_ITEMINUSE;
  2362. WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISMAPPED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
  2363. WsbThrow(hr);
  2364. }
  2365. // If the USN's don't match, then we need to check the
  2366. // journal for changes
  2367. // premigListUsn: The usn of the file immediately after it was migrated
  2368. // compareUsn1: If not 0, the usn of the file before we (possibly) removed a read-only attribute
  2369. // compareUsn2: If not 0,
  2370. //
  2371. if ((0 == fileUsn) || (0 == premigListUsn)) {
  2372. // We don't have USN Journal info so force a CRC comparison
  2373. DoCRC = TRUE;
  2374. } else if ((compareUsn1 != 0) && (compareUsn2 != 0)) {
  2375. // Need to compare with these input usn instead of a direct compare
  2376. if ((premigListUsn != compareUsn1) || (fileUsn != compareUsn2)) {
  2377. DoUsnCheck = TRUE;
  2378. }
  2379. } else if (fileUsn != premigListUsn) {
  2380. DoUsnCheck = TRUE;
  2381. }
  2382. // Current usn indicates that file may have changed
  2383. if (DoUsnCheck) {
  2384. BOOL UsnChanged = FALSE;
  2385. hr = CheckUsnJournalForChanges(premigListUsn, fileUsn, &UsnChanged);
  2386. if (S_OK == hr) {
  2387. if (UsnChanged) {
  2388. // File changed, skip it
  2389. WsbThrow(FSA_E_ITEMCHANGED);
  2390. }
  2391. } else {
  2392. // Something failed, force a CRC comparison
  2393. DoCRC = TRUE;
  2394. WsbLogEvent(FSA_MESSAGE_USN_CHECK_FAILED, 0, NULL,
  2395. WsbAbbreviatePath(fileName,120),
  2396. WsbHrAsString(hr), NULL);
  2397. hr = S_OK;
  2398. }
  2399. }
  2400. // If the USNJ indicated a possible change, then we need to CRC
  2401. // the data.
  2402. if (DoCRC) {
  2403. //
  2404. // Check to be sure that the CRC in the placeholder matches
  2405. // that of the file
  2406. //
  2407. ULONG currentCRC;
  2408. #if defined(TEMPORARY_TRUNCATE_STATISTICS)
  2409. // Try to increment the truncate-CRC count in the registry
  2410. WsbIncRegistryValueDWORD(NULL, FSA_REGISTRY_PARMS,
  2411. OLESTR("TruncateCRCs"));
  2412. #endif
  2413. WsbAffirmHr(CalculateCurrentCRCInternal(m_handleVerify, offset, realFileSize, &currentCRC));
  2414. WsbTrace(OLESTR("CFsaScanItem::VerifyInternal: Current CRC <%ul>, Reparse CRC <%ls>\n"),
  2415. currentCRC, WsbLonglongAsString( m_placeholder.dataStreamCRC ) );
  2416. if (currentCRC != m_placeholder.dataStreamCRC) {
  2417. //
  2418. // The file has changed since we migrated it so
  2419. // don't truncate it.
  2420. WsbThrow(FSA_E_ITEMCHANGED);
  2421. }
  2422. }
  2423. hr = S_OK;
  2424. } WsbCatch(hr);
  2425. // If the file data had changed (so we didn't truncate it) log event
  2426. // (cannot remove placeholder with DeletePlaceholder since the file is already opened exclusively
  2427. if (FSA_E_ITEMCHANGED == hr) {
  2428. WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISCHANGED, 0, NULL,
  2429. (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120),
  2430. WsbHrAsString(hr), NULL);
  2431. }
  2432. WsbTraceOut(OLESTR("CFsaScanItem::VerifyInternal"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  2433. return(hr);
  2434. }
  2435. HRESULT
  2436. CFsaScanItem::CheckIfSparse(
  2437. IN LONGLONG offset,
  2438. IN LONGLONG size
  2439. )
  2440. /*++
  2441. Implements:
  2442. IFsaScanItem::CheckIfSparse()
  2443. Determines if the specified section is on disk or not (is sparse)
  2444. FSA_E_FILE_IS_TOTALLY_SPARSE - There are no resident portions of the section
  2445. FSA_E_FILE_IS_PARTIALLY_SPARSE - The section of the file has some resident/some sparse
  2446. sections
  2447. FSA_E_FILE_IS_NOT_SPARSE - The section is totally resident
  2448. any others - error and we don't know the state of the file
  2449. --*/
  2450. {
  2451. HRESULT hr = E_FAIL;
  2452. HANDLE handle = INVALID_HANDLE_VALUE;
  2453. CWsbStringPtr path;
  2454. ULONG DesiredAccess;
  2455. IO_STATUS_BLOCK IoStatusBlock;
  2456. NTSTATUS ntStatus;
  2457. FILE_ALLOCATED_RANGE_BUFFER inRange;
  2458. #define NUM_RANGE 10
  2459. FILE_ALLOCATED_RANGE_BUFFER outRange[NUM_RANGE];
  2460. PFILE_ALLOCATED_RANGE_BUFFER cRange;
  2461. int idx;
  2462. WsbTraceIn(OLESTR("CFsaScanItem::CheckIfSparse"), OLESTR("offset = <%I64d>, size = <%I64d>"),
  2463. offset, size);
  2464. //
  2465. // If the file is really managed then we can check the allocation map
  2466. // Otherwise we indicate that the data is all resident
  2467. //
  2468. try {
  2469. //
  2470. // Create the real file name we need to open, under the covers this
  2471. // allocates the buffer since the path pointer is null
  2472. //
  2473. WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
  2474. //WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
  2475. //
  2476. // Open the file to check the allocation
  2477. //
  2478. DesiredAccess = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
  2479. WsbAffirmHr( OpenObject( path,
  2480. FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
  2481. DesiredAccess,
  2482. SHARE_FLAGS,
  2483. FILE_OPEN,
  2484. &IoStatusBlock,
  2485. &handle ) );
  2486. //
  2487. // The open worked, our handle should be valid but we check to be
  2488. // safe and sure
  2489. //
  2490. WsbAssertHandle( handle );
  2491. memset(&outRange, 0, sizeof(FILE_ALLOCATED_RANGE_BUFFER) * NUM_RANGE);
  2492. //
  2493. // Check the allocation of the specified range
  2494. //
  2495. inRange.FileOffset.QuadPart = offset;
  2496. inRange.Length.QuadPart = size;
  2497. ntStatus = NtFsControlFile( handle,
  2498. NULL,
  2499. NULL,
  2500. NULL,
  2501. &IoStatusBlock,
  2502. FSCTL_QUERY_ALLOCATED_RANGES,
  2503. &inRange,
  2504. sizeof(FILE_ALLOCATED_RANGE_BUFFER),
  2505. &outRange,
  2506. sizeof(FILE_ALLOCATED_RANGE_BUFFER) * NUM_RANGE);
  2507. //
  2508. // Check the return code but STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW are valid
  2509. //
  2510. if ( STATUS_SUCCESS != ntStatus && STATUS_BUFFER_OVERFLOW != ntStatus ) {
  2511. WsbAssertNtStatus( ntStatus );
  2512. }
  2513. cRange = (PFILE_ALLOCATED_RANGE_BUFFER) &outRange;
  2514. for (idx = 0; idx < NUM_RANGE; idx++) {
  2515. if (cRange->Length.QuadPart != 0) {
  2516. WsbTrace(OLESTR("CFsaScanItem::CheckIfSparse - Resident range %u Offset: %I64u, length: %I64u\n"),
  2517. idx, cRange->FileOffset.QuadPart, cRange->Length.QuadPart);
  2518. }
  2519. cRange++;
  2520. }
  2521. //
  2522. // Close the file since we are done with it and set the handle to invalid
  2523. //
  2524. NtClose(handle);
  2525. handle = INVALID_HANDLE_VALUE;
  2526. //
  2527. // If the initial allocated range does begin where we said to start and the length of the
  2528. // allocated area is equal to the length we asked about then none of the data is sparse
  2529. //
  2530. if ( (outRange[0].FileOffset.QuadPart == offset) && (outRange[0].Length.QuadPart == size) ) {
  2531. hr = FSA_E_FILE_IS_NOT_SPARSE;
  2532. } else if (outRange[0].Length.QuadPart == 0) {
  2533. hr = FSA_E_FILE_IS_TOTALLY_SPARSE;
  2534. } else {
  2535. hr = FSA_E_FILE_IS_PARTIALLY_SPARSE;
  2536. }
  2537. } WsbCatch(hr);
  2538. //
  2539. // if we opened the file we need to close it
  2540. //
  2541. if( INVALID_HANDLE_VALUE != handle) {
  2542. NtClose( handle );
  2543. }
  2544. WsbTraceOut(OLESTR("CFsaScanItem::CheckIfSparse"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  2545. return(hr);
  2546. }
  2547. HRESULT
  2548. CFsaScanItem::CheckIfDiskFull(
  2549. void
  2550. )
  2551. /*++
  2552. Implements:
  2553. CFsaScanItem::CheckIfDiskFull()
  2554. Determines whether a disk is full or not - will consider full if free-space is below X,
  2555. where X default is 10 MB
  2556. Returns:
  2557. S_OK Disk is full
  2558. S_FALSE Disk is not full
  2559. other Unexpected error
  2560. --*/
  2561. {
  2562. HRESULT hr = S_OK;
  2563. WsbTraceIn(OLESTR("CFsaScanItem::CheckIfDiskFull"), OLESTR(""));
  2564. try {
  2565. ULARGE_INTEGER FreeBytesAvailableToCaller;
  2566. ULARGE_INTEGER TotalNumberOfBytes;
  2567. ULARGE_INTEGER TotalNumberOfFreeBytes;
  2568. // Get volume free space
  2569. CWsbStringPtr volumePath;
  2570. WsbAffirmHr(m_pResource->GetPath(&volumePath, 0));
  2571. WsbAffirmStatus(GetDiskFreeSpaceEx(volumePath,
  2572. &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes));
  2573. // Get default from the Registry and compare
  2574. ULONG maxSizeDiskFullKB = FSA_MAX_SIZE_DISK_FULL_DEFAULT;
  2575. WsbAffirmHr(WsbRegistryValueUlongAsString(NULL, FSA_REGISTRY_PARMS, FSA_MAX_SIZE_DISK_FULL,
  2576. &maxSizeDiskFullKB));
  2577. if (maxSizeDiskFullKB == 0) {
  2578. // This means turning off quota check, i.e. always condiser disk as full
  2579. hr = S_OK;
  2580. } else if (TotalNumberOfFreeBytes.QuadPart <= (ULONGLONG )(maxSizeDiskFullKB * 1024)) {
  2581. // Disk is considered full
  2582. hr = S_OK;
  2583. } else {
  2584. // Disk free space is above the threshold
  2585. hr = S_FALSE;
  2586. }
  2587. } WsbCatch(hr);
  2588. WsbTraceOut(OLESTR("CFsaScanItem::CheckIfDiskFull"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  2589. return(hr);
  2590. }