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.

909 lines
23 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. ScLastGood.cxx
  5. Abstract:
  6. This module implements various functions required to clean-up last known
  7. good information.
  8. Author:
  9. Adrian J. Oney - April 4, 2000
  10. Revision History:
  11. --*/
  12. #include "precomp.hxx"
  13. #include "ScpLastGood.h"
  14. //
  15. // DeleteFile is a member of a structure we use below. Here we keep Windows.h
  16. // from redefining the structure member to DeleteFileW.
  17. //
  18. #ifdef DeleteFile
  19. #undef DeleteFile
  20. #endif
  21. DWORD
  22. ScLastGoodFileCleanup(
  23. VOID
  24. )
  25. /*++
  26. Routine Description:
  27. This routine does the neccessary processing to mark a boot "good".
  28. Specifically, the last known good directory is emptied of any files or
  29. directories.
  30. Arguments:
  31. None.
  32. Return Value:
  33. NTSTATUS.
  34. --*/
  35. {
  36. OBJECT_ATTRIBUTES objectAttributes;
  37. UNICODE_STRING lastKnownGoodTmpSubTree;
  38. UNICODE_STRING lastKnownGoodTmpRegKey;
  39. HANDLE regKeyHandle;
  40. NTSTATUS status;
  41. RtlInitUnicodeString(
  42. &lastKnownGoodTmpSubTree,
  43. L"\\SystemRoot\\LastGood.Tmp"
  44. );
  45. RtlInitUnicodeString(
  46. &lastKnownGoodTmpRegKey,
  47. L"\\Registry\\Machine\\System\\LastKnownGoodRecovery\\LastGood.Tmp"
  48. );
  49. //
  50. // Delete the temp tree.
  51. //
  52. ScLastGoodWalkDirectoryTreeBottomUp(
  53. &lastKnownGoodTmpSubTree,
  54. ( DIRWALK_INCLUDE_FILES | DIRWALK_INCLUDE_DIRECTORIES |
  55. DIRWALK_CULL_DOTPATHS | DIRWALK_TRAVERSE ),
  56. ScpLastGoodDeleteFiles,
  57. NULL
  58. );
  59. InitializeObjectAttributes(
  60. &objectAttributes,
  61. &lastKnownGoodTmpSubTree,
  62. OBJ_CASE_INSENSITIVE,
  63. NULL,
  64. NULL
  65. );
  66. NtDeleteFile(&objectAttributes);
  67. //
  68. // Now delete the corresponding registry key info.
  69. //
  70. InitializeObjectAttributes(
  71. &objectAttributes,
  72. &lastKnownGoodTmpRegKey,
  73. OBJ_CASE_INSENSITIVE,
  74. NULL,
  75. NULL
  76. );
  77. status = NtOpenKey(
  78. &regKeyHandle,
  79. KEY_ALL_ACCESS,
  80. &objectAttributes
  81. );
  82. if (NT_SUCCESS(status)) {
  83. NtDeleteKey(regKeyHandle);
  84. NtClose(regKeyHandle);
  85. }
  86. return NO_ERROR;
  87. }
  88. //
  89. // This function works, but it is not needed today.
  90. //
  91. DWORD
  92. ScLastGoodWalkDirectoryTreeTopDown(
  93. IN PUNICODE_STRING Directory,
  94. IN ULONG Flags,
  95. IN DIRWALK_CALLBACK CallbackFunction,
  96. IN PVOID Context
  97. )
  98. /*++
  99. Routine Description:
  100. This funcion walks a directory tree *top down*, passing each entry to the
  101. callback with the below restrictions. Note that the root directory itself
  102. is not included in the callback!
  103. Arguments:
  104. Directory - Supplies the NT Path to the directory to walk.
  105. Flags - Specifies constraints on how the directory tree should be walked:
  106. DIRWALK_INCLUDE_FILES - Files should be included in the dump.
  107. DIRWALK_INCLUDE_DIRECTORIES - Directories should be included in the
  108. dump.
  109. DIRWALK_CULL_DOTPATHS - "." and ".." should *not* be included
  110. in the list of directories passed to
  111. the callback function.
  112. DIRWALK_TRAVERSE - Each subdirectory should be traversed
  113. in turn.
  114. DIRWALK_TRAVERSE_MOUNTPOINTS - Set if mountpoints/symlinks should
  115. be traversed as well.
  116. CallbackFunction - Pointer to a function to call for each entry in the
  117. directory/subtree.
  118. Context - Context to pass to the callback function.
  119. Return Value:
  120. DWORD - status of the operation, NO_ERROR on success.
  121. --*/
  122. {
  123. PDIRWALK_ENTRY pDirEntry;
  124. PLIST_ENTRY pListEntry;
  125. NTSTATUS status;
  126. UCHAR buffer[1024];
  127. LIST_ENTRY dirListHead;
  128. InitializeListHead(&dirListHead);
  129. //
  130. // Walk the first directory.
  131. //
  132. status = ScpLastGoodWalkDirectoryTreeHelper(
  133. Directory,
  134. Flags,
  135. CallbackFunction,
  136. Context,
  137. buffer,
  138. sizeof(buffer),
  139. &dirListHead
  140. );
  141. //
  142. // Each directory that WalkDirectory finds gets added to the list.
  143. // process the list until we have no more directories.
  144. //
  145. while((!IsListEmpty(&dirListHead)) && NT_SUCCESS(status)) {
  146. pListEntry = RemoveHeadList(&dirListHead);
  147. pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
  148. status = ScpLastGoodWalkDirectoryTreeHelper(
  149. &pDirEntry->Directory,
  150. Flags,
  151. CallbackFunction,
  152. Context,
  153. buffer,
  154. sizeof(buffer),
  155. &dirListHead
  156. );
  157. LocalFree(pDirEntry);
  158. }
  159. //
  160. // If we failed we need to empty out our directory list.
  161. //
  162. if (!NT_SUCCESS(status)) {
  163. while (!IsListEmpty(&dirListHead)) {
  164. pListEntry = RemoveHeadList(&dirListHead);
  165. pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
  166. LocalFree(pDirEntry);
  167. }
  168. }
  169. return RtlNtStatusToDosError(status);
  170. }
  171. DWORD
  172. ScLastGoodWalkDirectoryTreeBottomUp(
  173. IN PUNICODE_STRING Directory,
  174. IN ULONG Flags,
  175. IN DIRWALK_CALLBACK CallbackFunction,
  176. IN PVOID Context
  177. )
  178. /*++
  179. Routine Description:
  180. This funcion walks a directory tree *bottom up*, passing each entry to the
  181. callback with the below restrictions. Note that the root directory itself
  182. is not included in the callback!
  183. Arguments:
  184. Directory - Supplies the NT Path to the directory to walk.
  185. Flags - Specifies constraints on how the directory tree should be walked:
  186. DIRWALK_INCLUDE_FILES - Files should be included in the dump.
  187. DIRWALK_INCLUDE_DIRECTORIES - Directories should be included in the
  188. dump.
  189. DIRWALK_CULL_DOTPATHS - "." and ".." should *not* be included
  190. in the list of directories passed to
  191. the callback function.
  192. DIRWALK_TRAVERSE - Each subdirectory should be traversed
  193. in turn.
  194. DIRWALK_TRAVERSE_MOUNTPOINTS - Set if mountpoints/symlinks should
  195. be traversed as well.
  196. CallbackFunction - Pointer to a function to call for each entry in the
  197. directory/subtree.
  198. Context - Context to pass to the callback function.
  199. Return Value:
  200. DWORD - status of the operation, NO_ERROR on success.
  201. --*/
  202. {
  203. PDIRWALK_ENTRY pDirEntry;
  204. PLIST_ENTRY pListEntry;
  205. NTSTATUS status = STATUS_SUCCESS;
  206. UCHAR buffer[1024];
  207. LIST_ENTRY dirListHead, dirNothingHead;
  208. InitializeListHead(&dirListHead);
  209. InitializeListHead(&dirNothingHead);
  210. //
  211. // Create an entry for the root directory.
  212. //
  213. pDirEntry = (PDIRWALK_ENTRY) LocalAlloc(
  214. LPTR,
  215. sizeof(DIRWALK_ENTRY) + Directory->Length - sizeof(WCHAR)
  216. );
  217. if (pDirEntry == NULL) {
  218. return RtlNtStatusToDosError(STATUS_INSUFFICIENT_RESOURCES);
  219. }
  220. pDirEntry->Directory.Length = 0;
  221. pDirEntry->Directory.MaximumLength = Directory->Length;
  222. pDirEntry->Directory.Buffer = &pDirEntry->Name[0];
  223. RtlCopyUnicodeString(&pDirEntry->Directory, Directory);
  224. InsertHeadList(&dirListHead, &pDirEntry->Link);
  225. //
  226. // Collect the directory trees. When we are done we will walk the list in
  227. // reverse.
  228. //
  229. status = STATUS_SUCCESS;
  230. if (Flags & DIRWALK_TRAVERSE) {
  231. for(pListEntry = dirListHead.Flink;
  232. pListEntry != &dirListHead;
  233. pListEntry = pListEntry->Flink) {
  234. pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
  235. status = ScpLastGoodWalkDirectoryTreeHelper(
  236. &pDirEntry->Directory,
  237. DIRWALK_TRAVERSE,
  238. NULL,
  239. NULL,
  240. buffer,
  241. sizeof(buffer),
  242. &dirListHead
  243. );
  244. if (!NT_SUCCESS(status)) {
  245. break;
  246. }
  247. }
  248. }
  249. //
  250. // Each directory that WalkDirectory finds gets added to the list.
  251. // process the list until we have no more directories.
  252. //
  253. while((!IsListEmpty(&dirListHead)) && NT_SUCCESS(status)) {
  254. pListEntry = RemoveTailList(&dirListHead);
  255. pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
  256. status = ScpLastGoodWalkDirectoryTreeHelper(
  257. &pDirEntry->Directory,
  258. Flags & ~DIRWALK_TRAVERSE,
  259. CallbackFunction,
  260. Context,
  261. buffer,
  262. sizeof(buffer),
  263. &dirNothingHead
  264. );
  265. LocalFree(pDirEntry);
  266. ASSERT(IsListEmpty(&dirNothingHead));
  267. }
  268. //
  269. // Now do any final cleanup.
  270. //
  271. if (!NT_SUCCESS(status)) {
  272. while (!IsListEmpty(&dirListHead)) {
  273. pListEntry = RemoveHeadList(&dirListHead);
  274. pDirEntry = (PDIRWALK_ENTRY) CONTAINING_RECORD(pListEntry, DIRWALK_ENTRY, Link);
  275. LocalFree(pDirEntry);
  276. }
  277. }
  278. return RtlNtStatusToDosError(status);
  279. }
  280. NTSTATUS
  281. ScpLastGoodWalkDirectoryTreeHelper(
  282. IN PUNICODE_STRING Directory,
  283. IN ULONG Flags,
  284. IN DIRWALK_CALLBACK CallbackFunction OPTIONAL,
  285. IN PVOID Context,
  286. IN PUCHAR Buffer,
  287. IN ULONG BufferSize,
  288. IN OUT PLIST_ENTRY DirList
  289. )
  290. /*++
  291. Routine Description:
  292. This is a helper function for the ScLastGoodWalkDirectory*Tree functions.
  293. Each directory is added to the list for later processing.
  294. Arguments:
  295. Directory - Supplies the NT Path to the directory to walk.
  296. Flags - Specifies constraints on how the directory tree should be walked:
  297. DIRWALK_INCLUDE_FILES - Files should be included in the dump.
  298. DIRWALK_INCLUDE_DIRECTORIES - Directories should be included in the
  299. dump.
  300. DIRWALK_CULL_DOTPATHS - "." and ".." should *not* be included
  301. in the list of directories passed to
  302. the callback function.
  303. DIRWALK_TRAVERSE - Each subdirectory should be traversed
  304. in turn.
  305. DIRWALK_TRAVERSE_MOUNTPOINTS - Set if mountpoints/symlinks should
  306. be traversed as well.
  307. CallbackFunction - Pointer to a function to call for each entry in the
  308. directory/subtree.
  309. Context - Context to pass to the callback function.
  310. Buffer - A scratch buffer to use.
  311. BufferSize - The length of Buffer. Must be greater than sizeof(WCHAR).
  312. DirList - Recieves list of new directories to scan after completion of this
  313. directory. Each entry is a member of the DIRECTORY_ENTRY
  314. structure.
  315. Return Value:
  316. NTSTATUS - status of the operation.
  317. --*/
  318. {
  319. NTSTATUS status;
  320. HANDLE fileHandle;
  321. OBJECT_ATTRIBUTES objectAttributes;
  322. IO_STATUS_BLOCK ioStatus;
  323. BOOLEAN bRestartScan, bIsDotPath;
  324. WCHAR savedChar;
  325. PFILE_BOTH_DIR_INFORMATION pFileInfo;
  326. UNICODE_STRING entryName;
  327. USHORT newNameLength;
  328. PDIRWALK_ENTRY pDirEntry;
  329. ULONG OpenFlags;
  330. //
  331. // Setup initial values
  332. //
  333. bRestartScan = TRUE;
  334. //
  335. // Open the file for list directory access
  336. //
  337. if (Flags & DIRWALK_TRAVERSE_MOUNTPOINTS) {
  338. OpenFlags = FILE_DIRECTORY_FILE |
  339. FILE_OPEN_FOR_BACKUP_INTENT;
  340. } else {
  341. OpenFlags = FILE_OPEN_REPARSE_POINT |
  342. FILE_DIRECTORY_FILE |
  343. FILE_OPEN_FOR_BACKUP_INTENT;
  344. }
  345. InitializeObjectAttributes(
  346. &objectAttributes,
  347. Directory,
  348. OBJ_CASE_INSENSITIVE,
  349. NULL,
  350. NULL
  351. );
  352. status = NtOpenFile(
  353. &fileHandle,
  354. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  355. &objectAttributes,
  356. &ioStatus,
  357. FILE_SHARE_READ,
  358. OpenFlags
  359. );
  360. if (!NT_SUCCESS(status)) {
  361. goto cleanup;
  362. }
  363. //
  364. // Do the directory loop
  365. //
  366. while(1) {
  367. //
  368. // We subtract off a WCHAR so that we can append a terminating null as
  369. // needed.
  370. //
  371. ASSERT(BufferSize > sizeof(WCHAR));
  372. status = NtQueryDirectoryFile(
  373. fileHandle,
  374. (HANDLE)NULL,
  375. (PIO_APC_ROUTINE)NULL,
  376. (PVOID)NULL,
  377. &ioStatus,
  378. Buffer,
  379. BufferSize - sizeof(WCHAR),
  380. FileBothDirectoryInformation,
  381. FALSE,
  382. (PUNICODE_STRING)NULL,
  383. bRestartScan
  384. );
  385. if (!NT_SUCCESS(status)) {
  386. break;
  387. }
  388. //
  389. // We may come back here. Make sure the file scan doesn't start back
  390. // over.
  391. //
  392. bRestartScan = FALSE;
  393. //
  394. // Wait for the event to complete if neccessary.
  395. //
  396. if (status == STATUS_PENDING) {
  397. NtWaitForSingleObject(fileHandle, TRUE, NULL);
  398. status = ioStatus.Status;
  399. //
  400. // Check the Irp for success
  401. //
  402. if (!NT_SUCCESS(status)) {
  403. break;
  404. }
  405. }
  406. //
  407. // Walk each returned record. Note that we won't be here if there are
  408. // no records, as ioStatus will have contains STATUS_NO_MORE_FILES.
  409. //
  410. pFileInfo = (PFILE_BOTH_DIR_INFORMATION) Buffer;
  411. while(1) {
  412. //
  413. // Temporarily terminate the file. We allocated an extra WCHAR to
  414. // make sure we could safely do this.
  415. //
  416. savedChar = pFileInfo->FileName[pFileInfo->FileNameLength/sizeof(WCHAR)];
  417. pFileInfo->FileName[pFileInfo->FileNameLength/sizeof(WCHAR)] = 0;
  418. //
  419. // Build a full unicode path for the file along with a directory
  420. // entry at the same time.
  421. //
  422. RtlInitUnicodeString(&entryName, pFileInfo->FileName);
  423. newNameLength =
  424. (Directory->Length + entryName.Length + sizeof(WCHAR));
  425. pDirEntry = (PDIRWALK_ENTRY) LocalAlloc(
  426. LPTR,
  427. sizeof(DIRWALK_ENTRY) + newNameLength
  428. );
  429. if (pDirEntry == NULL) {
  430. status = STATUS_INSUFFICIENT_RESOURCES;
  431. break;
  432. }
  433. pDirEntry->Directory.Length = 0;
  434. pDirEntry->Directory.MaximumLength = newNameLength;
  435. pDirEntry->Directory.Buffer = &pDirEntry->Name[0];
  436. RtlCopyUnicodeString(&pDirEntry->Directory, Directory);
  437. RtlAppendUnicodeToString(&pDirEntry->Directory, L"\\");
  438. RtlAppendUnicodeStringToString(&pDirEntry->Directory, &entryName);
  439. if (pFileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  440. //
  441. // Check for . and ..
  442. //
  443. bIsDotPath = ((!_wcsicmp(pFileInfo->FileName, L".")) ||
  444. (!_wcsicmp(pFileInfo->FileName, L"..")));
  445. if ((Flags & DIRWALK_INCLUDE_DIRECTORIES) &&
  446. ((!(Flags & DIRWALK_CULL_DOTPATHS)) || (!bIsDotPath))) {
  447. status = CallbackFunction(
  448. &pDirEntry->Directory,
  449. &entryName,
  450. pFileInfo->FileAttributes,
  451. Context
  452. );
  453. }
  454. if ((!bIsDotPath) && (Flags & DIRWALK_TRAVERSE)) {
  455. InsertTailList(DirList, &pDirEntry->Link);
  456. } else {
  457. LocalFree(pDirEntry);
  458. }
  459. } else {
  460. if (Flags & DIRWALK_INCLUDE_FILES) {
  461. status = CallbackFunction(
  462. &pDirEntry->Directory,
  463. &entryName,
  464. pFileInfo->FileAttributes,
  465. Context
  466. );
  467. }
  468. LocalFree(pDirEntry);
  469. }
  470. if (!NT_SUCCESS(status)) {
  471. break;
  472. }
  473. //
  474. // Put back the character we wrote down. It might have been part of
  475. // the next entry.
  476. //
  477. pFileInfo->FileName[pFileInfo->FileNameLength/sizeof(WCHAR)] = savedChar;
  478. //
  479. // Check if there is another record, if there isn't then we
  480. // simply get out of this loop
  481. //
  482. if (pFileInfo->NextEntryOffset == 0) {
  483. break;
  484. }
  485. //
  486. // There is another record so advance FileInfo to the next
  487. // record
  488. //
  489. pFileInfo = (PFILE_BOTH_DIR_INFORMATION)
  490. (((PUCHAR) pFileInfo) + pFileInfo->NextEntryOffset);
  491. }
  492. if (!NT_SUCCESS(status)) {
  493. break;
  494. }
  495. }
  496. NtClose( fileHandle );
  497. if (status == STATUS_NO_MORE_FILES) {
  498. status = STATUS_SUCCESS;
  499. }
  500. cleanup:
  501. return status;
  502. }
  503. NTSTATUS
  504. ScpLastGoodDeleteFiles(
  505. IN PUNICODE_STRING FullPathName,
  506. IN PUNICODE_STRING FileName,
  507. IN ULONG FileAttributes,
  508. IN PVOID Context
  509. )
  510. /*++
  511. Routine Description:
  512. This callback routine is called for each file under the LastGood directory.
  513. It's job is to delete each such file.
  514. Arguments:
  515. None.
  516. Return Value:
  517. NTSTATUS.
  518. --*/
  519. {
  520. NTSTATUS Status;
  521. OBJECT_ATTRIBUTES ObjectAttributes;
  522. IO_STATUS_BLOCK IoStatusBlock;
  523. HANDLE FileHandle;
  524. FILE_DISPOSITION_INFORMATION Disposition;
  525. ULONG OpenFlags;
  526. //
  527. // Remove any attributes that might stop us from deleting this file.
  528. //
  529. ScLastGoodClearAttributes(
  530. FullPathName,
  531. ( FILE_ATTRIBUTE_READONLY |
  532. FILE_ATTRIBUTE_HIDDEN |
  533. FILE_ATTRIBUTE_SYSTEM )
  534. );
  535. //
  536. // Now delete the file.
  537. //
  538. InitializeObjectAttributes(
  539. &ObjectAttributes,
  540. FullPathName,
  541. OBJ_CASE_INSENSITIVE,
  542. NULL,
  543. NULL
  544. );
  545. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  546. OpenFlags = FILE_DIRECTORY_FILE |
  547. FILE_OPEN_FOR_BACKUP_INTENT |
  548. FILE_OPEN_REPARSE_POINT;
  549. } else {
  550. OpenFlags = FILE_NON_DIRECTORY_FILE |
  551. FILE_OPEN_FOR_BACKUP_INTENT |
  552. FILE_OPEN_REPARSE_POINT;
  553. }
  554. Status = NtOpenFile(
  555. &FileHandle,
  556. DELETE | FILE_READ_ATTRIBUTES,
  557. &ObjectAttributes,
  558. &IoStatusBlock,
  559. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  560. OpenFlags
  561. );
  562. if (!NT_SUCCESS(Status)) {
  563. KdPrintEx((
  564. DPFLTR_SCSERVER_ID,
  565. DEBUG_CONFIG_API,
  566. "SERVICES: Cannot open last known good file: %Zw %x\n",
  567. FullPathName,
  568. Status
  569. ));
  570. return Status;
  571. }
  572. Disposition.DeleteFile = TRUE;
  573. Status = NtSetInformationFile(
  574. FileHandle,
  575. &IoStatusBlock,
  576. &Disposition,
  577. sizeof(Disposition),
  578. FileDispositionInformation
  579. );
  580. if (!NT_SUCCESS(Status)) {
  581. KdPrintEx((
  582. DPFLTR_SCSERVER_ID,
  583. DEBUG_CONFIG_API,
  584. "SERVICES: Cannot delete last known good file: %Zw %x\n",
  585. FullPathName,
  586. Status
  587. ));
  588. }
  589. NtClose(FileHandle);
  590. //
  591. // If we can't delete one file, keep trying to delete the rest.
  592. //
  593. return STATUS_SUCCESS;
  594. }
  595. NTSTATUS
  596. ScLastGoodClearAttributes(
  597. IN PUNICODE_STRING FullPathName,
  598. IN ULONG FileAttributes
  599. )
  600. /*++
  601. Routine Description:
  602. This function clears the passed in attributes off the specified file.
  603. Arguments:
  604. FullPathName - Full path name of the identified file.
  605. FileAttributes - Attributes to clear.
  606. Return Value:
  607. NTSTATUS.
  608. --*/
  609. {
  610. HANDLE fileHandle;
  611. OBJECT_ATTRIBUTES objectAttributes;
  612. IO_STATUS_BLOCK ioStatus;
  613. FILE_BASIC_INFORMATION fileBasicInformation;
  614. ULONG newAttributes;
  615. NTSTATUS status;
  616. //
  617. // First we open the file.
  618. //
  619. InitializeObjectAttributes(
  620. &objectAttributes,
  621. FullPathName,
  622. OBJ_CASE_INSENSITIVE,
  623. NULL,
  624. NULL
  625. );
  626. status = NtOpenFile(
  627. &fileHandle,
  628. FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  629. &objectAttributes,
  630. &ioStatus,
  631. FILE_SHARE_READ | FILE_SHARE_WRITE,
  632. FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT |
  633. FILE_OPEN_FOR_BACKUP_INTENT | FILE_WRITE_THROUGH
  634. );
  635. if (!NT_SUCCESS(status)) {
  636. return status;
  637. }
  638. //
  639. // Then we get the file attributes.
  640. //
  641. status = NtQueryInformationFile(
  642. fileHandle,
  643. &ioStatus,
  644. &fileBasicInformation,
  645. sizeof(fileBasicInformation),
  646. FileBasicInformation
  647. );
  648. if (!NT_SUCCESS(status)) {
  649. NtClose(fileHandle);
  650. return status;
  651. }
  652. //
  653. // Anything to do?
  654. //
  655. if (fileBasicInformation.FileAttributes & FileAttributes) {
  656. //
  657. // Clear the specified bits.
  658. //
  659. newAttributes = fileBasicInformation.FileAttributes;
  660. newAttributes &= ~FileAttributes;
  661. if (newAttributes == 0) {
  662. newAttributes = FILE_ATTRIBUTE_NORMAL;
  663. }
  664. //
  665. // Zero fields that shouldn't be touched.
  666. //
  667. RtlZeroMemory(
  668. &fileBasicInformation,
  669. sizeof(FILE_BASIC_INFORMATION)
  670. );
  671. fileBasicInformation.FileAttributes = newAttributes;
  672. //
  673. // Commit the changes.
  674. //
  675. status = NtSetInformationFile(
  676. fileHandle,
  677. &ioStatus,
  678. &fileBasicInformation,
  679. sizeof(fileBasicInformation),
  680. FileBasicInformation
  681. );
  682. }
  683. NtClose(fileHandle);
  684. return status;
  685. }