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.

1577 lines
42 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. cow.c
  5. Abstract:
  6. Copy on write support for the single instance store
  7. Authors:
  8. Bill Bolosky, Summer, 1997
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include "sip.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(PAGE, SipBltRange)
  16. #pragma alloc_text(PAGE, SipBltRangeByObject)
  17. #endif // ALLOC_PRAGMA
  18. LIST_ENTRY CopyList[1];
  19. KSPIN_LOCK CopyListLock[1];
  20. KSEMAPHORE CopySemaphore[1];
  21. NTSTATUS
  22. SipBltRange(
  23. IN PDEVICE_EXTENSION deviceExtension,
  24. IN HANDLE sourceHandle,
  25. IN OUT HANDLE dstHandle,
  26. IN LONGLONG startingOffset,
  27. IN LONGLONG length,
  28. IN HANDLE copyEventHandle,
  29. IN PKEVENT copyEvent,
  30. IN PKEVENT abortEvent,
  31. IN OUT PLONGLONG checksum)
  32. /*++
  33. Routine Description:
  34. Wrapper for SipBltRangeByObject that takes a source handle rather than
  35. a file object pointer. All this function does is to get the file object
  36. from the handle and call SipBltRangeByObject.
  37. This function must be called in the PsInitialSystemProcess context.
  38. Arguments:
  39. sourceHandle - handle to the file from which to copy
  40. others - see SipBltRangeByObject for description
  41. Return Value:
  42. status of the copy
  43. --*/
  44. {
  45. PFILE_OBJECT srcFileObject = NULL;
  46. NTSTATUS status;
  47. OBJECT_HANDLE_INFORMATION handleInformation[1];
  48. PAGED_CODE();
  49. status = ObReferenceObjectByHandle(
  50. sourceHandle,
  51. FILE_READ_DATA,
  52. *IoFileObjectType,
  53. KernelMode,
  54. (PVOID *) &srcFileObject,
  55. handleInformation);
  56. if (!NT_SUCCESS(status)) {
  57. ASSERT(NULL == srcFileObject);
  58. SIS_MARK_POINT_ULONG(status);
  59. goto done;
  60. }
  61. status = SipBltRangeByObject(
  62. deviceExtension,
  63. srcFileObject,
  64. dstHandle,
  65. startingOffset,
  66. length,
  67. copyEventHandle,
  68. copyEvent,
  69. abortEvent,
  70. checksum);
  71. done:
  72. if (NULL != srcFileObject) {
  73. ObDereferenceObject(srcFileObject);
  74. }
  75. return status;
  76. }
  77. NTSTATUS
  78. SipBltRangeByObject(
  79. IN PDEVICE_EXTENSION deviceExtension,
  80. IN PFILE_OBJECT srcFileObject,
  81. IN OUT HANDLE dstHandle,
  82. IN LONGLONG startingOffset,
  83. IN LONGLONG length,
  84. IN HANDLE copyEventHandle,
  85. IN PKEVENT copyEvent,
  86. IN PKEVENT abortEvent,
  87. IN OUT PLONGLONG checksum)
  88. /*++
  89. Routine Description:
  90. Copy a range of a file from one place to another. Maps the destination
  91. and does noncached reads from the source into the mapped
  92. region. Does not return STATUS_SUCCESS until all of the bits
  93. of the new file are on the disk.
  94. This function must be called in the PsInitialSystemProcess context
  95. (ie., from a worker thread).
  96. This function may also be used to simply compute the checksum on a range
  97. of a file without doing a file copy. Pass in srcFileObject == NULL and
  98. dstHandle == file to compute checksum on.
  99. NTRAID#65184-2000/03/09-nealch SIS needs to check for sparse ranges when copying a file and not copy them
  100. Arguments:
  101. deviceExtension - For the volume on which we're copying
  102. srcFileObject - the file from which to copy. If NULL, checksum on
  103. dstHandle file will be computed.
  104. dstHandle - Handle for the file into which to copy
  105. startingOffset - offset within the files from which to start copying
  106. length - the number of bytes to copy
  107. copyEventHandle - handle to an event that no one else
  108. is using now
  109. copyEvent - pointer to the same event as copyEventHandle
  110. abortEvent - pointer to an event that signals an abort request
  111. checksum - pointer to a place to hold the checksum of the blt'ed range.
  112. if NULL no checksum is computed. Note that this must be
  113. initialized to 0 by the caller, unless a partial checksum has
  114. already beem computed, in which case it should contain the
  115. partial checksum.
  116. Return Value:
  117. status of the copy
  118. --*/
  119. {
  120. #define MM_MAP_ALIGNMENT (64 * 1024 /*VACB_MAPPING_GRANULARITY*/) // The file offset granularity that MM enforces.
  121. #define COPY_AMOUNT (64 * 1024) // How much we read or write at a time. Must be >= MM_MAP_ALIGNMENT
  122. HANDLE sectionHandle = NULL;
  123. NTSTATUS status;
  124. LARGE_INTEGER byteOffset;
  125. LONGLONG finalOffset;
  126. IO_STATUS_BLOCK Iosb[1];
  127. LARGE_INTEGER maxSectionSize;
  128. ULONG sectorSize = deviceExtension->FilesystemVolumeSectorSize;
  129. PIRP readIrp = NULL;
  130. PDEVICE_OBJECT srcFileRelatedDeviceObject;
  131. PIO_STACK_LOCATION irpSp;
  132. PAGED_CODE();
  133. UNREFERENCED_PARAMETER( copyEventHandle );
  134. ASSERT(PASSIVE_LEVEL == KeGetCurrentIrql());
  135. ASSERT(checksum || srcFileObject);
  136. SIS_MARK_POINT_ULONG(startingOffset);
  137. SIS_MARK_POINT_ULONG(length);
  138. if (srcFileObject) {
  139. srcFileRelatedDeviceObject = IoGetRelatedDeviceObject(srcFileObject);
  140. } else {
  141. srcFileRelatedDeviceObject = NULL;
  142. }
  143. finalOffset = startingOffset + length;
  144. maxSectionSize.QuadPart = finalOffset;
  145. status = ZwCreateSection(
  146. &sectionHandle,
  147. SECTION_MAP_WRITE | STANDARD_RIGHTS_REQUIRED | SECTION_MAP_READ | SECTION_QUERY,
  148. NULL,
  149. &maxSectionSize,
  150. PAGE_READWRITE,
  151. SEC_COMMIT,
  152. dstHandle);
  153. if (!NT_SUCCESS(status)) {
  154. SIS_MARK_POINT_ULONG(status);
  155. goto done;
  156. }
  157. ASSERT(status == STATUS_SUCCESS); // and not STATUS_PENDING or anything weird
  158. for (byteOffset.QuadPart = startingOffset; byteOffset.QuadPart < finalOffset;) {
  159. ULONG validBytes, bytesToCopy;
  160. PCHAR mappedBuffer = NULL, flushBuffer;
  161. LARGE_INTEGER mappedOffset;
  162. ULONG_PTR viewSize;
  163. ULONG_PTR flushSize;
  164. PCHAR copyIntoAddress;
  165. ULONG bytesCopied;
  166. SIS_MARK_POINT_ULONG(byteOffset.LowPart);
  167. mappedOffset.QuadPart = byteOffset.QuadPart - (byteOffset.QuadPart % MM_MAP_ALIGNMENT);
  168. ASSERT(mappedOffset.QuadPart <= byteOffset.QuadPart && mappedOffset.QuadPart + MM_MAP_ALIGNMENT > byteOffset.QuadPart);
  169. //
  170. // Abort if an oplock break has been received.
  171. //
  172. if (SipAbort(abortEvent)) {
  173. SIS_MARK_POINT();
  174. status = STATUS_OPLOCK_BREAK_IN_PROGRESS;
  175. goto done;
  176. }
  177. if (finalOffset - mappedOffset.QuadPart > COPY_AMOUNT) {
  178. //
  179. // We can't map enough of the file to do the whole copy
  180. // here, so only map COPY_AMOUNT on this pass.
  181. //
  182. viewSize = COPY_AMOUNT;
  183. } else {
  184. //
  185. // We can map all the way to the end of the file.
  186. //
  187. viewSize = (ULONG)(finalOffset - mappedOffset.QuadPart);
  188. }
  189. ASSERT(viewSize >=
  190. (ULONG_PTR)(byteOffset.QuadPart - mappedOffset.QuadPart));
  191. validBytes = (ULONG)(viewSize - (ULONG)(byteOffset.QuadPart - mappedOffset.QuadPart));
  192. //
  193. // Now round validBytes up to a sector size.
  194. //
  195. bytesToCopy = ((validBytes + sectorSize - 1) / sectorSize) * sectorSize;
  196. ASSERT(bytesToCopy <= COPY_AMOUNT);
  197. //
  198. // Map in the region to which we're about to copy.
  199. //
  200. status = ZwMapViewOfSection(
  201. sectionHandle,
  202. NtCurrentProcess(),
  203. &mappedBuffer,
  204. 0, // zero bits
  205. 0, // commit size (ignored for mapped files)
  206. &mappedOffset,
  207. &viewSize,
  208. ViewUnmap,
  209. 0, // allocation type
  210. PAGE_READWRITE);
  211. if (!NT_SUCCESS(status)) {
  212. SIS_MARK_POINT_ULONG(status);
  213. goto done;
  214. }
  215. ASSERT(viewSize >= bytesToCopy); // We have enough space allocated for the rounded up read
  216. copyIntoAddress = mappedBuffer + (ULONG)(byteOffset.QuadPart - mappedOffset.QuadPart);
  217. if (srcFileObject) {
  218. // Now, read the bits in from the source file
  219. readIrp = IoBuildSynchronousFsdRequest(
  220. IRP_MJ_READ,
  221. srcFileRelatedDeviceObject,
  222. copyIntoAddress,
  223. bytesToCopy,
  224. &byteOffset,
  225. copyEvent,
  226. Iosb);
  227. if (NULL == readIrp) {
  228. status = STATUS_INSUFFICIENT_RESOURCES;
  229. goto done;
  230. }
  231. irpSp = IoGetNextIrpStackLocation(readIrp);
  232. irpSp->FileObject = srcFileObject;
  233. status = IoCallDriver(srcFileRelatedDeviceObject, readIrp);
  234. if (STATUS_PENDING == status) {
  235. status = KeWaitForSingleObject(copyEvent, Executive, KernelMode, FALSE, NULL);
  236. ASSERT(status == STATUS_SUCCESS);
  237. status = Iosb->Status;
  238. } else {
  239. KeClearEvent(copyEvent);
  240. }
  241. if (!NT_SUCCESS(status)) {
  242. SIS_MARK_POINT_ULONG(status);
  243. ZwUnmapViewOfSection(NtCurrentProcess(),mappedBuffer);
  244. goto done;
  245. }
  246. //
  247. // Iosb->Information returns the actual number of bytes read from the
  248. // file and into the mapped CS file, hence the actual number of bytes we
  249. // copied on this trip through the loop.
  250. //
  251. bytesCopied = (ULONG)Iosb->Information;
  252. } else {
  253. bytesCopied = validBytes;
  254. }
  255. //
  256. // Abort if an oplock break has been received.
  257. //
  258. if (SipAbort(abortEvent)) {
  259. status = STATUS_OPLOCK_BREAK_IN_PROGRESS;
  260. ZwUnmapViewOfSection(NtCurrentProcess(),mappedBuffer);
  261. goto done;
  262. }
  263. if (NULL != checksum) {
  264. SipComputeChecksum(copyIntoAddress,bytesCopied,checksum);
  265. }
  266. flushBuffer = mappedBuffer;
  267. flushSize = viewSize;
  268. status = ZwFlushVirtualMemory(
  269. NtCurrentProcess(),
  270. &flushBuffer,
  271. &flushSize,
  272. Iosb);
  273. ZwUnmapViewOfSection(NtCurrentProcess(),mappedBuffer);
  274. if (!NT_SUCCESS(status)) {
  275. SIS_MARK_POINT_ULONG(status);
  276. goto done;
  277. }
  278. //
  279. // Add in the number of bytes that we actually copied into the file.
  280. //
  281. byteOffset.QuadPart += bytesCopied;
  282. }
  283. done:
  284. if (sectionHandle != NULL) {
  285. ZwClose(sectionHandle);
  286. sectionHandle = NULL;
  287. }
  288. return status;
  289. #undef COPY_AMOUNT
  290. #undef MM_MAP_ALIGNMENT
  291. }
  292. VOID
  293. SiCopyThreadStart(
  294. IN PVOID parameter)
  295. /*++
  296. Routine Description:
  297. A thread to handle SIS copy on write operations. Because these operations may
  298. take a very large amount of time, we have our own thread rather than holding an
  299. ExWorker thread. This thread waits for requests for initial or final copies to
  300. be posted, and then processes them. The thread uses a global work queue, not
  301. one per volume.
  302. Arguments:
  303. parameter - PVOID NULL.
  304. Return Value:
  305. never returns.
  306. --*/
  307. {
  308. NTSTATUS status;
  309. PSI_COPY_THREAD_REQUEST copyRequest;
  310. HANDLE copyEventHandle;
  311. PKEVENT copyEvent;
  312. UNREFERENCED_PARAMETER( parameter );
  313. ASSERT(parameter == NULL);
  314. status = SipCreateEvent(
  315. SynchronizationEvent,
  316. &copyEventHandle,
  317. &copyEvent);
  318. if (!NT_SUCCESS(status)) {
  319. DbgPrint("SipCopyThreadStart: unable to allocate copyevent, 0x%x\n",status);
  320. return; // now what??
  321. }
  322. while (TRUE) {
  323. status = KeWaitForSingleObject(CopySemaphore,Executive,KernelMode,FALSE,NULL);
  324. ASSERT(status == STATUS_SUCCESS);
  325. copyRequest = (PSI_COPY_THREAD_REQUEST)ExInterlockedRemoveHeadList(CopyList,CopyListLock);
  326. ASSERT(copyRequest != NULL); // Else the semaphore isn't working right
  327. if (copyRequest) {
  328. status = SipCompleteCopyWork(copyRequest->scb,copyEventHandle,copyEvent,copyRequest->fromCleanup);
  329. ExFreePool(copyRequest);
  330. }
  331. }
  332. }
  333. NTSTATUS
  334. SipOpenFileById(
  335. IN PDEVICE_EXTENSION deviceExtension,
  336. IN PLARGE_INTEGER linkFileNtfsId,
  337. IN ACCESS_MASK desiredAccess,
  338. IN ULONG shareAccess,
  339. IN ULONG createOptions,
  340. OUT PHANDLE openedFileHandle)
  341. /*++
  342. Routine Description:
  343. Open a file by file id and return a handle to it. Must be called in the
  344. PsInitialSystemProcess context.
  345. Arguments:
  346. deviceExtension - For the device on which this file is to be opened.
  347. linkFileNtfsId - A pointer to the file ID of the file to open
  348. desiredAccess - Access to request to the file.
  349. shareAccess - Sharing mode.
  350. createOptions - Options for the open.
  351. openedFileHandle- The opened handle
  352. Return Value:
  353. the status of the open
  354. --*/
  355. {
  356. OBJECT_ATTRIBUTES Obja[1];
  357. IO_STATUS_BLOCK Iosb[1];
  358. UNICODE_STRING fileNameString;
  359. CHAR fileNameBuffer[sizeof(LARGE_INTEGER)];
  360. NTSTATUS status;
  361. fileNameString.MaximumLength = sizeof(LARGE_INTEGER);
  362. fileNameString.Buffer = (PWCHAR)fileNameBuffer;
  363. RtlCopyMemory(fileNameString.Buffer,linkFileNtfsId,sizeof(*linkFileNtfsId));
  364. fileNameString.Length = sizeof(LARGE_INTEGER);
  365. //
  366. // We don't need to hold GrovelerFileResource here because we're only accessing
  367. // the handle, and the worst thing that an invalid handle will do here is to
  368. // make the create file call fail. Also, we don't have to worry about malicious
  369. // messing with the handle since we're in the system process context.
  370. //
  371. InitializeObjectAttributes(
  372. Obja,
  373. &fileNameString,
  374. OBJ_CASE_INSENSITIVE,
  375. deviceExtension->GrovelerFileHandle,
  376. NULL);
  377. status = NtCreateFile(
  378. openedFileHandle,
  379. desiredAccess,
  380. Obja,
  381. Iosb,
  382. NULL, // Allocation size
  383. 0, // file attributes
  384. shareAccess,
  385. FILE_OPEN,
  386. createOptions |
  387. FILE_OPEN_BY_FILE_ID,
  388. NULL, // EA buffer
  389. 0); // EA length
  390. #if DBG
  391. if (!NT_SUCCESS(status) && STATUS_SHARING_VIOLATION != status) {
  392. DbgPrint("SIS: SipOpenFileById failed 0x%x\n",status);
  393. }
  394. #endif // DBG
  395. return status;
  396. }
  397. NTSTATUS
  398. SipCompleteCopy(
  399. IN PSIS_SCB scb,
  400. OUT BOOLEAN fromCleanup)
  401. /*++
  402. Routine Description:
  403. Post a request to do a final copy on a file. Does not wait for its
  404. completion.
  405. Arguments:
  406. scb - The scb for the file on which to try the final copy.
  407. fromCleanup - TRUE iff this call was made from cleanup rather than from
  408. close. Errors on final copies generated from cleanup
  409. are ignored, ones from close are retried on an exponential
  410. backoff schedule.
  411. Return Value:
  412. STATUS_SUCCESS
  413. --*/
  414. {
  415. PSI_COPY_THREAD_REQUEST copyRequest;
  416. copyRequest = ExAllocatePoolWithTag(NonPagedPool, sizeof (SI_COPY_THREAD_REQUEST), ' siS');
  417. if (NULL == copyRequest) {
  418. //NTRAID#65186-2000/03/10-nealch Handle out of memory without droping entry
  419. #if DBG
  420. DbgPrint("SIS: SipCompleteCopy: Unable to allocate copy request for scb 0x%x\n",scb);
  421. ASSERT(FALSE);
  422. #endif // DBG
  423. SIS_MARK_POINT_ULONG(scb);
  424. return STATUS_INSUFFICIENT_RESOURCES;
  425. }
  426. ASSERT(copyRequest == (PSI_COPY_THREAD_REQUEST)copyRequest->listEntry); // This is assumed at the dequeue side
  427. copyRequest->scb = scb;
  428. copyRequest->fromCleanup = fromCleanup;
  429. ExInterlockedInsertTailList(CopyList,copyRequest->listEntry,CopyListLock);
  430. KeReleaseSemaphore(CopySemaphore,IO_NO_INCREMENT,1,FALSE);
  431. return STATUS_SUCCESS;
  432. }
  433. //
  434. // A context record used for final copies that have failed and are to be retried later.
  435. //
  436. typedef struct _SIS_RETRY_FINAL_COPY_CONTEXT {
  437. //
  438. // The timer DPC that executes to kick off the retry
  439. //
  440. KDPC dpc[1];
  441. //
  442. // The timer that's used to start the DPC.
  443. //
  444. KTIMER timer[1];
  445. //
  446. // The scb of the file on which to retry the final copy.
  447. //
  448. PSIS_SCB scb;
  449. //
  450. // A work queue item to be used to start the actual retry.
  451. //
  452. WORK_QUEUE_ITEM workItem[1];
  453. } SIS_RETRY_FINAL_COPY_CONTEXT, *PSIS_RETRY_FINAL_COPY_CONTEXT;
  454. VOID
  455. SiRetryFinalCopyWork(
  456. IN PVOID parameter)
  457. /*++
  458. Routine Description:
  459. A worker thread routine to retry a final copy that's failed. We don't really directly
  460. retry the final copy here, but instead we just drop the reference to the scb that we've
  461. been holding. Once all other references are also dropped to this scb, the final copy
  462. will be re-attempted.
  463. Arguments:
  464. parameter - a pointer to a SIS_RETRY_FINAL_COPY_CONTEXT. We free this when we're done.
  465. Return Value:
  466. void
  467. --*/
  468. {
  469. PSIS_RETRY_FINAL_COPY_CONTEXT retryContext = (PSIS_RETRY_FINAL_COPY_CONTEXT)parameter;
  470. PDEVICE_EXTENSION deviceExtension;
  471. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  472. ASSERT(NULL != retryContext);
  473. deviceExtension = retryContext->scb->PerLink->CsFile->DeviceObject->DeviceExtension;
  474. InterlockedDecrement(&deviceExtension->OutstandingFinalCopyRetries);
  475. SipDereferenceScb(retryContext->scb,RefsFinalCopyRetry);
  476. ExFreePool(retryContext);
  477. }
  478. VOID
  479. SiRetryFinalCopyDpc(
  480. IN PKDPC dpc,
  481. IN PVOID context,
  482. IN PVOID systemArg1,
  483. IN PVOID systemArg2)
  484. /*++
  485. Routine Description:
  486. A timer-fired DPC routine that handles retrying a failed final copy.
  487. This just queues up a work item to do the final copy.
  488. Arguments:
  489. dpc - The DPC that's executing
  490. context - a PSIS_RETRY_FINAL_COPY_CONTEXT; see the definition of that structure
  491. for a description of the fields
  492. systemArg 1 & 2 - unused DPC parameters
  493. Return Value:
  494. void
  495. --*/
  496. {
  497. PSIS_RETRY_FINAL_COPY_CONTEXT retryContext = (PSIS_RETRY_FINAL_COPY_CONTEXT)context;
  498. UNREFERENCED_PARAMETER( dpc );
  499. UNREFERENCED_PARAMETER( systemArg1 );
  500. UNREFERENCED_PARAMETER( systemArg2 );
  501. ASSERT(NULL != retryContext);
  502. ASSERT(retryContext->dpc == dpc);
  503. ExQueueWorkItem(retryContext->workItem,DelayedWorkQueue);
  504. }
  505. NTSTATUS
  506. SipCompleteCopyWork(
  507. IN PSIS_SCB scb,
  508. IN HANDLE eventHandle,
  509. IN PKEVENT event,
  510. IN BOOLEAN fromCleanup)
  511. /*++
  512. Routine Description:
  513. A worker thread routine that does final copy on a file. This function
  514. itself just checks some things (like whether the file's been deleted in the
  515. interim), calls SipFinalCopy to actually do the final copy, and then deals
  516. appropriately with errors if there are any. If this is a close-generated
  517. call, we retry errors on an expeonential backoff schedule (with a time limit);
  518. if from cleanup, we ignore them on the theory that a close will eventually come
  519. along and we'll deal with it then.
  520. If we get an OPLOCK_BREAK_IN_PROGRESS, then someone else wants to use the file
  521. and we just stop.
  522. Arguments:
  523. scb - The scb on which to do the final copy
  524. eventHandle - Handle to an event that we can use exclusively for now
  525. event - Pointer to the same event as represented by eventHandle
  526. fromCleanup - Whether this call originated in cleanup (TRUE) or close (FALSE)
  527. Return Value:
  528. status of the final copy operation
  529. --*/
  530. {
  531. PSIS_PER_LINK perLink = scb->PerLink;
  532. PDEVICE_EXTENSION deviceExtension = perLink->CsFile->DeviceObject->DeviceExtension;
  533. NTSTATUS status;
  534. KIRQL OldIrql;
  535. BOOLEAN wakeupNeeded;
  536. BOOLEAN deleted;
  537. ASSERT(sizeof(scb->PerLink->LinkFileNtfsId) == sizeof(LARGE_INTEGER));
  538. SIS_MARK_POINT_ULONG(scb);
  539. //
  540. // The last reference to the SCB for a copied file has been dropped.
  541. // If there have been any writes into the file, then fill in any regions
  542. // that are unwritten from the underlying file. If there have been no
  543. // writes into the file (which can happen for a mapped file) then
  544. // revert it into a reparse point.
  545. //
  546. //
  547. // Check to see whether the file was written into (a file can be
  548. // "copied" without being dirty if it's ever mapped). We could probably
  549. // get away without taking the lock here because we hold the last
  550. // reference.
  551. //
  552. KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
  553. wakeupNeeded = (perLink->Flags & SIS_PER_LINK_FINAL_COPY_WAITERS) ? TRUE : FALSE;
  554. deleted = (perLink->Flags & SIS_PER_LINK_BACKPOINTER_GONE) ? TRUE : FALSE;
  555. #if DBG
  556. if (BJBDebug & 0x20000) {
  557. DbgPrint("SIS: SipCompleteCopyWork: scb %p, wakeupNeeded %d, deleted %d\n",scb,wakeupNeeded,deleted);
  558. }
  559. ASSERT((0 == scb->referencesByType[RefsFinalCopyRetry]) || fromCleanup);
  560. ASSERT((1 == scb->referencesByType[RefsFinalCopy]) || fromCleanup);
  561. #endif // DBG
  562. SIS_MARK_POINT_ULONG(wakeupNeeded);
  563. ASSERT((perLink->Flags &
  564. (SIS_PER_LINK_FINAL_COPY |
  565. SIS_PER_LINK_DIRTY |
  566. SIS_PER_LINK_FINAL_COPY_DONE)) ==
  567. (SIS_PER_LINK_DIRTY |
  568. SIS_PER_LINK_FINAL_COPY));
  569. if (deleted) {
  570. SIS_MARK_POINT();
  571. //
  572. // This file was deleted after (possibly) being modified. Make it look like
  573. // it's finished its final copy.
  574. //
  575. scb->PerLink->Flags |= SIS_PER_LINK_FINAL_COPY_DONE;
  576. scb->PerLink->Flags &= ~( SIS_PER_LINK_FINAL_COPY |
  577. SIS_PER_LINK_FINAL_COPY_WAITERS);
  578. if (wakeupNeeded) {
  579. KeSetEvent(perLink->Event, IO_NO_INCREMENT, FALSE);
  580. }
  581. KeReleaseSpinLock(perLink->SpinLock, OldIrql);
  582. SipDereferenceScb(scb, RefsFinalCopy);
  583. return STATUS_SUCCESS;
  584. }
  585. KeReleaseSpinLock(perLink->SpinLock, OldIrql);
  586. #if DBG
  587. if (BJBDebug & 0x200000) {
  588. DbgPrint("SIS: SipCompleteCopyWork: skipping final copy because of set debug bit.\n");
  589. status = STATUS_SUCCESS;
  590. } else
  591. #endif // DBG
  592. SIS_MARK_POINT_ULONG(scb->PerLink->Flags);
  593. status = SipFinalCopy(
  594. deviceExtension,
  595. &perLink->LinkFileNtfsId,
  596. scb,
  597. eventHandle,
  598. event);
  599. if (!NT_SUCCESS(status)) {
  600. SIS_MARK_POINT_ULONG(scb);
  601. goto done;
  602. }
  603. done:
  604. //
  605. // Set the flag indicating that the final copy is finished; this will result in
  606. // the dereference of the SCB actually deallocating it rather than just calling
  607. // us again.
  608. //
  609. KeAcquireSpinLock(scb->PerLink->SpinLock, &OldIrql);
  610. ASSERT(scb->PerLink->Flags & SIS_PER_LINK_FINAL_COPY);
  611. ASSERT(perLink->COWingThread == NULL);
  612. wakeupNeeded = (perLink->Flags & SIS_PER_LINK_FINAL_COPY_WAITERS) ? TRUE : FALSE;
  613. if (STATUS_OPLOCK_BREAK_IN_PROGRESS == status) {
  614. //
  615. // The final copy didn't complete because of an oplock break (ie,. someone else wanted
  616. // to use the file). We'll leave it a SIS file for now and allow others to use it; we'll
  617. // complete the final copy later.
  618. //
  619. SIS_MARK_POINT_ULONG(status);
  620. } else if (NT_SUCCESS(status)) {
  621. scb->PerLink->Flags |= SIS_PER_LINK_FINAL_COPY_DONE;
  622. SIS_MARK_POINT_ULONG(scb->PerLink->Flags);
  623. } else if (!fromCleanup) {
  624. //
  625. // The final copy failed for some reason other than an oplock break.
  626. // If we haven't retried too many times, schedule a retry for later.
  627. // We use an exponential backoff on the retries so they don't do
  628. // too much work in case it's a persistent error. If the copy failed
  629. // with STATUS_INVALID_PARAMETER, it's probably becauce the file is
  630. // gone, so don't bother to try to retry in that case.
  631. //
  632. // The way that the retry works is that we just take a reference to the
  633. // scb and hold it until the timer expires. We never go into final copy
  634. // when there are outstanding references, so this will prevent final
  635. // copy from happening until then.
  636. //
  637. // If this final copy was generated from cleanup rather than from close,
  638. // we don't do the error retry, but rather just ignore it. Most likely it
  639. // was a sharing violation opening the file because another user opened it.
  640. // In any case, final copy will be rerun on close, so we needn't do anything here.
  641. //
  642. scb->ConsecutiveFailedFinalCopies++;
  643. SIS_MARK_POINT_ULONG(scb->PerLink->Flags);
  644. SIS_MARK_POINT_ULONG(scb->ConsecutiveFailedFinalCopies);
  645. if ((deviceExtension->OutstandingFinalCopyRetries < 130)
  646. && (scb->ConsecutiveFailedFinalCopies <= (13 - (deviceExtension->OutstandingFinalCopyRetries / 10)))
  647. && (STATUS_INVALID_PARAMETER != status)) {
  648. PSIS_RETRY_FINAL_COPY_CONTEXT retryContext;
  649. LARGE_INTEGER dueTime;
  650. retryContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(SIS_RETRY_FINAL_COPY_CONTEXT), ' siS');
  651. if (NULL == retryContext) {
  652. //
  653. // Too bad. Treat this like an unrecoverable failure and get out of the way.
  654. //
  655. SIS_MARK_POINT_ULONG(scb);
  656. goto doneCheckingRetry;
  657. }
  658. SipReferenceScb(scb,RefsFinalCopyRetry);
  659. InterlockedIncrement(&deviceExtension->OutstandingFinalCopyRetries);
  660. KeInitializeTimer(retryContext->timer);
  661. KeInitializeDpc(retryContext->dpc, SiRetryFinalCopyDpc, retryContext);
  662. ExInitializeWorkItem(retryContext->workItem, SiRetryFinalCopyWork, retryContext);
  663. retryContext->scb = scb;
  664. //
  665. // We sleep for 2 ^ RetryCount seconds before retrying (ie., exponential backoff).
  666. //
  667. dueTime.QuadPart = -10 * 1000 * 1000 * (1 << scb->ConsecutiveFailedFinalCopies);
  668. KeSetTimerEx(
  669. retryContext->timer,
  670. dueTime,
  671. 0, // period (ie., non-recurring)
  672. retryContext->dpc);
  673. } else {
  674. //
  675. // We've retried too many times, just give up on the final copy.
  676. //
  677. SIS_MARK_POINT_ULONG(scb);
  678. scb->PerLink->Flags |= SIS_PER_LINK_FINAL_COPY_DONE;
  679. }
  680. }
  681. doneCheckingRetry:
  682. scb->PerLink->Flags &= ~( SIS_PER_LINK_FINAL_COPY |
  683. SIS_PER_LINK_FINAL_COPY_WAITERS);
  684. SIS_MARK_POINT_ULONG(scb->PerLink->Flags);
  685. if (wakeupNeeded) {
  686. KeSetEvent(perLink->Event, IO_NO_INCREMENT, FALSE);
  687. }
  688. KeReleaseSpinLock(scb->PerLink->SpinLock, OldIrql);
  689. SipDereferenceScb(scb, RefsFinalCopy);
  690. return status;
  691. }
  692. NTSTATUS
  693. SipFinalCopy(
  694. IN PDEVICE_EXTENSION deviceExtension,
  695. IN PLARGE_INTEGER linkFileNtfsId,
  696. IN OUT PSIS_SCB scb,
  697. IN HANDLE eventHandle,
  698. IN PKEVENT event)
  699. /*++
  700. Routine Description:
  701. Perform the final copy from the copied file area into a file
  702. that has been copied-on-write.
  703. Arguments:
  704. deviceExtension - the device extension for the volume on which
  705. we're working.
  706. linkFileNtfsId - the file id (as in FILE_OPEN_BY_FILE_ID) for
  707. the final file into which we're to copy
  708. scb - the scb for the file (NB: should really be a per-link)
  709. copyEventHandle - A handle to an event used for internal synchronization
  710. copyEvent - a PKEVENT for the event reprsented by copyEventHandle
  711. Return Value:
  712. Returns the status of the copy.
  713. --*/
  714. {
  715. HANDLE linkFileHandle = NULL;
  716. NTSTATUS status;
  717. NTSTATUS queryAllocatedStatus;
  718. NTSTATUS failureStatus = STATUS_SUCCESS;
  719. PSIS_PER_LINK perLink = scb->PerLink;
  720. HANDLE underlyingFileHandle = perLink->CsFile->UnderlyingFileHandle;
  721. LONGLONG fileOffset;
  722. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  723. UCHAR ReparseBuffer[SIS_REPARSE_DATA_SIZE];
  724. PFILE_OBJECT fileObject = NULL;
  725. BOOLEAN prepareWorked;
  726. KIRQL OldIrql;
  727. HANDLE oplockEventHandle = NULL;
  728. PKEVENT oplockEvent = NULL;
  729. IO_STATUS_BLOCK oplockIosb[1];
  730. BOOLEAN deleteReparsePoint;
  731. BOOLEAN foundRange;
  732. SIS_RANGE_STATE rangeState;
  733. LONGLONG rangeLength;
  734. #if DBG
  735. BOOLEAN deletedReparsePoint = FALSE;
  736. #endif // DBG
  737. #define OUT_ARB_COUNT 10
  738. FILE_ALLOCATED_RANGE_BUFFER inArb[1];
  739. FILE_ALLOCATED_RANGE_BUFFER outArb[OUT_ARB_COUNT];
  740. ULONG returnedLength;
  741. ULONG i;
  742. LARGE_INTEGER zero;
  743. SipAcquireScb(scb);
  744. ASSERT(perLink->COWingThread == NULL);
  745. perLink->COWingThread = PsGetCurrentThread();
  746. SipReleaseScb(scb);
  747. status = SipCreateEvent(
  748. NotificationEvent,
  749. &oplockEventHandle,
  750. &oplockEvent);
  751. if (!NT_SUCCESS(status)) {
  752. SIS_MARK_POINT_ULONG(status);
  753. #if DBG
  754. DbgPrint("SIS: SipFinalCopy: unable to create event, 0x%x\n",status);
  755. #endif // DBG
  756. goto done;
  757. }
  758. status = SipOpenFileById(
  759. deviceExtension,
  760. linkFileNtfsId,
  761. GENERIC_READ | GENERIC_WRITE,
  762. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  763. 0, // createOptions
  764. &linkFileHandle);
  765. if (!NT_SUCCESS(status)) {
  766. SIS_MARK_POINT_ULONG(status);
  767. #if DBG
  768. if (STATUS_SHARING_VIOLATION != status) {
  769. DbgPrint("SIS: SipFinalCopy failed open, 0x%x\n", status);
  770. }
  771. #endif // DBG
  772. goto done;
  773. }
  774. //
  775. // Place a batch oplock on the file so that if someone tries to open it we'll
  776. // get a chance to finish/stop our copy without having them fail the open, but
  777. // rather just wait for us.
  778. //
  779. status = NtFsControlFile(
  780. linkFileHandle,
  781. oplockEventHandle,
  782. NULL, // APC routine
  783. NULL, // APC context
  784. oplockIosb,
  785. FSCTL_REQUEST_BATCH_OPLOCK,
  786. NULL, // input buffer
  787. 0, // i.b. length
  788. NULL, // output buffer
  789. 0); // output buffer length
  790. if (!NT_SUCCESS(status)) {
  791. SIS_MARK_POINT_ULONG(status);
  792. #if DBG
  793. if (STATUS_OPLOCK_NOT_GRANTED != status) {
  794. DbgPrint("SIS: SipFinalCopy: request batch oplock failed, 0x%x\n",status);
  795. }
  796. #endif // DBG
  797. if (STATUS_OPLOCK_NOT_GRANTED == status) {
  798. //
  799. // Treat this as an oplock break, which will cause us to retry later.
  800. //
  801. status = STATUS_OPLOCK_BREAK_IN_PROGRESS;
  802. }
  803. goto done;
  804. }
  805. ASSERT(STATUS_PENDING == status);
  806. status = ObReferenceObjectByHandle(
  807. linkFileHandle,
  808. FILE_WRITE_DATA,
  809. *IoFileObjectType,
  810. KernelMode,
  811. &fileObject,
  812. NULL); // Handle information
  813. if (!NT_SUCCESS(status)) {
  814. SIS_MARK_POINT_ULONG(status);
  815. #if DBG
  816. DbgPrint("SIS: SipFinalCopy failed ObReferenceObjectByHandle, 0x%x\n",status);
  817. #endif // DBG
  818. goto done;
  819. }
  820. #if DBG
  821. if (BJBDebug & 0x01000000) {
  822. DbgPrint("SIS: SipFinalCopy: failing request because of set BJBDebug bit\n");
  823. status = STATUS_UNSUCCESSFUL;
  824. goto done;
  825. }
  826. #endif // DBG
  827. //
  828. // See if the file has any user mapped sections, in which case we can't do a final copy yet.
  829. // We'll probably have to wait for the reference count to go to 0. We'll fail with oplock
  830. // break in progress, which will cause us to not set up a failure retry.
  831. //
  832. zero.QuadPart = 0;
  833. if ((NULL != fileObject->SectionObjectPointer) &&
  834. !MmCanFileBeTruncated(fileObject->SectionObjectPointer, &zero)) {
  835. SIS_MARK_POINT_ULONG(fileObject->FsContext);
  836. status = STATUS_OPLOCK_BREAK_IN_PROGRESS;
  837. goto done;
  838. }
  839. //
  840. // Flush the file. We need to do this because we could have dirty data that came in through a mapped
  841. // file write, and we wouldn't notice that it's dirty yet.
  842. //
  843. status = SipFlushBuffersFile(
  844. fileObject,
  845. deviceExtension->DeviceObject);
  846. ASSERT(STATUS_PENDING != status);
  847. if (!NT_SUCCESS(status)) {
  848. SIS_MARK_POINT_ULONG(status);
  849. goto done;
  850. }
  851. //
  852. // Cruise through the file and find all of the allocated ranges. Fill in any clean portions
  853. // of those allocated ranges. We do this regardless of whether we're doing a "partial" final
  854. // copy, because these copies are less likely to fail with a "disk full" error. Attempt the copyout
  855. // to all clean, allocated regions regardless of errors.
  856. //
  857. inArb->FileOffset.QuadPart = 0;
  858. inArb->Length.QuadPart = MAXLONGLONG;
  859. for (;;) {
  860. //
  861. // Query the allocated ranges for this file.
  862. //
  863. queryAllocatedStatus = SipFsControlFile(
  864. fileObject,
  865. deviceExtension->DeviceObject,
  866. FSCTL_QUERY_ALLOCATED_RANGES,
  867. inArb,
  868. sizeof(FILE_ALLOCATED_RANGE_BUFFER),
  869. outArb,
  870. sizeof(FILE_ALLOCATED_RANGE_BUFFER) * OUT_ARB_COUNT,
  871. &returnedLength);
  872. //
  873. // Run through all of the returned allocated ranges and find any clean regions within them.
  874. //
  875. ASSERT((returnedLength % sizeof(FILE_ALLOCATED_RANGE_BUFFER) == 0) &&
  876. (returnedLength / sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= OUT_ARB_COUNT));
  877. //
  878. // If the query allocated ranges failed for some other reason that having too much to write
  879. // into the buffer we provided, then pretend that the rest of the file is allocated
  880. // and fill it all in.
  881. //
  882. if (!NT_SUCCESS(queryAllocatedStatus) && (STATUS_BUFFER_OVERFLOW != queryAllocatedStatus)) {
  883. returnedLength = sizeof(FILE_ALLOCATED_RANGE_BUFFER);
  884. outArb->FileOffset.QuadPart = inArb->FileOffset.QuadPart;
  885. outArb->Length.QuadPart = scb->SizeBackedByUnderlyingFile - outArb->FileOffset.QuadPart;
  886. ASSERT(outArb->Length.QuadPart >= 0);
  887. }
  888. for (i = 0; i < returnedLength/sizeof(FILE_ALLOCATED_RANGE_BUFFER); i++) {
  889. //
  890. // Assert that the allocated ranges are in order; if this isn't true the code will still work, but it
  891. // will query the same range repetedly.
  892. //
  893. ASSERT(i == 0 || outArb[i].FileOffset.QuadPart > outArb[i-1].FileOffset.QuadPart);
  894. //
  895. // Figure out if there's anything clean in the allocated range, and if so do a copy out to it.
  896. //
  897. fileOffset = outArb[i].FileOffset.QuadPart;
  898. while (fileOffset < outArb[i].FileOffset.QuadPart + outArb[i].Length.QuadPart) {
  899. if (fileOffset >= scb->SizeBackedByUnderlyingFile) {
  900. goto CheckedAllRanges;
  901. }
  902. SipAcquireScb(scb);
  903. foundRange = SipGetRangeEntry(
  904. deviceExtension,
  905. scb,
  906. fileOffset,
  907. &rangeLength,
  908. &rangeState);
  909. if (!foundRange) {
  910. //
  911. // This and everything up to SizeBackedByUnderlyingFile are clean.
  912. //
  913. rangeLength = outArb[i].Length.QuadPart - (fileOffset - outArb[i].FileOffset.QuadPart);
  914. rangeState = Untouched;
  915. } else {
  916. //
  917. // If this range extends beyond the end of the allocated region, truncate it.
  918. //
  919. if (rangeLength > outArb[i].Length.QuadPart - (fileOffset - outArb[i].FileOffset.QuadPart)) {
  920. rangeLength = outArb[i].Length.QuadPart - (fileOffset - outArb[i].FileOffset.QuadPart);
  921. }
  922. }
  923. ASSERT(fileOffset + rangeLength <= outArb[i].FileOffset.QuadPart + outArb[i].Length.QuadPart);
  924. //
  925. // Don't let this extend beyond sizeBacked.
  926. //
  927. if (fileOffset + rangeLength > scb->SizeBackedByUnderlyingFile) {
  928. rangeLength = scb->SizeBackedByUnderlyingFile - fileOffset;
  929. }
  930. SipReleaseScb(scb);
  931. if (rangeState == Untouched || rangeState == Faulted) {
  932. //
  933. // We need to copy into this range. Do it now.
  934. //
  935. SIS_MARK_POINT_ULONG(fileOffset);
  936. status = SipBltRange(
  937. deviceExtension,
  938. underlyingFileHandle,
  939. linkFileHandle,
  940. fileOffset,
  941. rangeLength,
  942. eventHandle,
  943. event,
  944. #if INTERRUPTABLE_FINAL_COPY
  945. oplockEvent,
  946. #else // INTERRUPTABLE_FINAL_COPY
  947. NULL,
  948. #endif // INTERRUPTABLE_FINAL_COPY
  949. NULL // checksum
  950. );
  951. if (!NT_SUCCESS(status)) {
  952. SIS_MARK_POINT_ULONG(status);
  953. #if DBG
  954. if (STATUS_FILE_LOCK_CONFLICT != status) {
  955. DbgPrint("SIS: SipFinalCopy failed blt, 0x%x\n", status);
  956. }
  957. #endif // DBG
  958. failureStatus = status;
  959. } else if (STATUS_OPLOCK_BREAK_IN_PROGRESS == status) {
  960. SIS_MARK_POINT_ULONG(scb);
  961. goto done;
  962. }
  963. }
  964. //
  965. // update fileOffset and continue checking within this outArb entry.
  966. //
  967. fileOffset += rangeLength;
  968. } // while loop of SIS ranges within the NTFS allocated range
  969. } // for loop of outArb entries
  970. //
  971. // If this isn't the last iteration, update the inArb.
  972. //
  973. if (STATUS_BUFFER_OVERFLOW == queryAllocatedStatus) {
  974. //
  975. // Assert that we're making progress.
  976. //
  977. ASSERT((outArb[OUT_ARB_COUNT-1].FileOffset.QuadPart >= inArb->FileOffset.QuadPart) && (outArb[OUT_ARB_COUNT-1].Length.QuadPart > 0));
  978. //
  979. // Move up our input range.
  980. //
  981. inArb->FileOffset.QuadPart = outArb[OUT_ARB_COUNT-1].FileOffset.QuadPart + outArb[OUT_ARB_COUNT-1].Length.QuadPart;
  982. inArb->Length.QuadPart = MAXLONGLONG - inArb->FileOffset.QuadPart;
  983. } else {
  984. break;
  985. }
  986. } // for loop of calls to QueryAllocatedRanges
  987. CheckedAllRanges:
  988. #if ENABLE_PARTIAL_FINAL_COPY
  989. //
  990. // If any of the copies failed, then just punt the whole thing.
  991. //
  992. if (!NT_SUCCESS(failureStatus)) {
  993. SIS_MARK_POINT_ULONG(failureStatus);
  994. status = failureStatus;
  995. goto done;
  996. }
  997. //
  998. // Figue out if we want to delete the reparse point. We do this if and only if the file is dirty all the way from 0
  999. // to SizeBackedByUnderlyingFile. Note that the SipBltFile call above actually will set the ranges dirty because it's
  1000. // just a normal (mapped) write that goes through SiWrite.
  1001. //
  1002. fileOffset = 0;
  1003. SipAcquireScb(scb);
  1004. rangeState = SipGetRangeDirty(
  1005. deviceExtension,
  1006. scb,
  1007. (PLARGE_INTEGER)&fileOffset, // we rely on LARGE_INTEGER and LONGLONG being the same thing
  1008. scb->SizeBackedByUnderlyingFile,
  1009. FALSE);
  1010. if (Dirty == rangeState) {
  1011. deleteReparsePoint = TRUE;
  1012. } else {
  1013. deleteReparsePoint = FALSE;
  1014. }
  1015. SipReleaseScb(scb);
  1016. #undef OUT_ARB_COUNT
  1017. #else // ENABLE_PARTIAL_FINAL_COPY
  1018. //
  1019. // We don't care if any of the copies in the allocated range pass failed, because
  1020. // the following code is sufficient for all cases.
  1021. //
  1022. SIS_MARK_POINT_ULONG(failureStatus);
  1023. //
  1024. // Look through all of the ranges of the file up to the
  1025. // maximum possible size backed by the underlying file,
  1026. // and copy any ranges that aren't written from the underlying
  1027. // file to the copied file.
  1028. //
  1029. fileOffset = 0;
  1030. while (fileOffset < scb->SizeBackedByUnderlyingFile) {
  1031. BOOLEAN waiters;
  1032. #if INTERRUPTABLE_FINAL_COPY
  1033. if (fileOffset + 0x10000 < scb->SizeBackedByUnderlyingFile) {
  1034. //
  1035. // We've got a decent amount of the file left to cover. Check to
  1036. // see if we should abort because someone wants the file.
  1037. //
  1038. KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
  1039. waiters = (perLink->Flags & SIS_PER_LINK_FINAL_COPY_WAITERS) ? TRUE : FALSE;
  1040. KeReleaseSpinLock(perLink->SpinLock, OldIrql);
  1041. if (waiters) {
  1042. //
  1043. // Someone'e waiting for the file, and we're more than 64K from the end, so
  1044. // abort the final copy now.
  1045. //
  1046. SIS_MARK_POINT_ULONG(scb);
  1047. status = STATUS_OPLOCK_BREAK_IN_PROGRESS;
  1048. goto done;
  1049. }
  1050. }
  1051. #endif // INTERRUPTABLE_FINAL_COPY
  1052. SipAcquireScb(scb);
  1053. foundRange = SipGetRangeEntry(
  1054. deviceExtension,
  1055. scb,
  1056. fileOffset,
  1057. &rangeLength,
  1058. &rangeState);
  1059. if (!foundRange) {
  1060. //
  1061. // The range was never filled in in the MCB, and hence everything
  1062. // from here to SizeBackedByUnderlyingFile is untouched. Munge
  1063. // the locals to look like that.
  1064. //
  1065. rangeLength = scb->SizeBackedByUnderlyingFile - fileOffset;
  1066. rangeState = Untouched;
  1067. } else if (fileOffset + rangeLength > scb->SizeBackedByUnderlyingFile) {
  1068. //
  1069. // This range extends beyond sizeBacked, so truncate it so that it
  1070. // just meets the size.
  1071. //
  1072. rangeLength = (ULONG)(scb->SizeBackedByUnderlyingFile - fileOffset);
  1073. }
  1074. ASSERT(rangeLength > 0);
  1075. ASSERT(fileOffset + rangeLength <= scb->SizeBackedByUnderlyingFile);
  1076. SipReleaseScb(scb);
  1077. if (rangeState == Untouched || rangeState == Faulted) {
  1078. //
  1079. // The bytes in this range have never been written into the backing file.
  1080. // write them in now.
  1081. //
  1082. SIS_MARK_POINT_ULONG(fileOffset);
  1083. status = SipBltRange(
  1084. deviceExtension,
  1085. underlyingFileHandle,
  1086. linkFileHandle,
  1087. fileOffset,
  1088. rangeLength,
  1089. eventHandle,
  1090. event,
  1091. #if INTERRUPTABLE_FINAL_COPY
  1092. oplockEvent,
  1093. #else // INTERRUPTABLE_FINAL_COPY
  1094. NULL,
  1095. #endif // INTERRUPTABLE_FINAL_COPY
  1096. NULL // checksum
  1097. );
  1098. if (!NT_SUCCESS(status)) {
  1099. SIS_MARK_POINT_ULONG(status);
  1100. #if DBG
  1101. if (STATUS_FILE_LOCK_CONFLICT != status) {
  1102. DbgPrint("SIS: SipFinalCopy failed blt, 0x%x\n", status);
  1103. }
  1104. #endif // DBG
  1105. goto done;
  1106. } else if (STATUS_OPLOCK_BREAK_IN_PROGRESS == status) {
  1107. SIS_MARK_POINT_ULONG(scb);
  1108. goto done;
  1109. }
  1110. } else {
  1111. //
  1112. // The range is written, meaning that the correct bytes are in the
  1113. // copied file already, and we don't need to do a thing.
  1114. //
  1115. ASSERT(rangeState == Written);
  1116. }
  1117. //
  1118. // Update our pointer to show we've covered this range, and move on.
  1119. //
  1120. fileOffset += rangeLength;
  1121. }
  1122. deleteReparsePoint = TRUE; // the full final copy always deletes the reparse point
  1123. #endif // ENABLE_PARTIAL_FINAL_COPY
  1124. if (deleteReparsePoint) {
  1125. //
  1126. // Prepare to change the CS file reference count. We need to do this
  1127. // before we can delete the reparse point.
  1128. //
  1129. status = SipPrepareCSRefcountChange(
  1130. perLink->CsFile,
  1131. &perLink->Index,
  1132. linkFileNtfsId,
  1133. SIS_REFCOUNT_UPDATE_LINK_OVERWRITTEN);
  1134. if (!NT_SUCCESS(status)) {
  1135. //
  1136. // The prepare failed. We'll just delete the reparse point and leak the reference.
  1137. //
  1138. SIS_MARK_POINT_ULONG(status);
  1139. #if DBG
  1140. DbgPrint("SIS: SipFinalCopy: prepare failed 0x%x\n",status);
  1141. #endif // DBG
  1142. prepareWorked = FALSE;
  1143. } else {
  1144. prepareWorked = TRUE;
  1145. }
  1146. //
  1147. // Now, delete the reparse point.
  1148. //
  1149. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  1150. ReparseBufferHeader->ReparseTag = IO_REPARSE_TAG_SIS;
  1151. ReparseBufferHeader->ReparseDataLength = 0;
  1152. ReparseBufferHeader->Reserved = 0xcabd; // ???
  1153. SIS_MARK_POINT_ULONG(scb);
  1154. status = SipFsControlFile(
  1155. fileObject,
  1156. deviceExtension->DeviceObject,
  1157. FSCTL_DELETE_REPARSE_POINT,
  1158. ReparseBuffer,
  1159. FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer),
  1160. NULL, // output buffer
  1161. 0, // output buffer length
  1162. NULL); // returned output buffer length
  1163. if (!NT_SUCCESS(status)) {
  1164. SIS_MARK_POINT_ULONG(status);
  1165. if (prepareWorked) {
  1166. SipCompleteCSRefcountChange(
  1167. perLink,
  1168. &perLink->Index,
  1169. perLink->CsFile,
  1170. FALSE,
  1171. FALSE);
  1172. }
  1173. goto done;
  1174. }
  1175. ASSERT(status != STATUS_PENDING);
  1176. #if DBG
  1177. deletedReparsePoint = TRUE;
  1178. #endif // DBG
  1179. if (prepareWorked) {
  1180. SIS_MARK_POINT_ULONG(perLink->CsFile);
  1181. status = SipCompleteCSRefcountChange(
  1182. perLink,
  1183. &perLink->Index,
  1184. perLink->CsFile,
  1185. TRUE,
  1186. FALSE);
  1187. if (!NT_SUCCESS(status)) {
  1188. SIS_MARK_POINT_ULONG(status);
  1189. #if DBG
  1190. DbgPrint("SIS: SipFinalCopy: complete failed 0x%x\n",status);
  1191. #endif // DBG
  1192. }
  1193. }
  1194. } // if delete reparse point
  1195. done:
  1196. ASSERT(deletedReparsePoint || !NT_SUCCESS(status) || (STATUS_OPLOCK_BREAK_IN_PROGRESS == status));
  1197. if (NULL != fileObject) {
  1198. ObDereferenceObject(fileObject);
  1199. #if DBG
  1200. fileObject = NULL;
  1201. #endif // DBG
  1202. }
  1203. if (NULL != linkFileHandle) {
  1204. SIS_MARK_POINT_ULONG(scb);
  1205. NtClose(linkFileHandle);
  1206. SIS_MARK_POINT_ULONG(scb);
  1207. }
  1208. if (NULL != oplockEvent) {
  1209. ObDereferenceObject(oplockEvent);
  1210. #if DBG
  1211. oplockEvent = NULL;
  1212. #endif // DBG
  1213. }
  1214. if (NULL != oplockEventHandle) {
  1215. NtClose(oplockEventHandle);
  1216. }
  1217. SipAcquireScb(scb);
  1218. ASSERT(perLink->COWingThread == PsGetCurrentThread());
  1219. perLink->COWingThread = NULL;
  1220. SipReleaseScb(scb);
  1221. return status;
  1222. }