|
|
1.1 root 1: /* 1.1.1.8 ! root 2: Copyright (c) 2008-2009 TrueCrypt Foundation. All rights reserved. 1.1 root 3: 1.1.1.6 root 4: Governed by the TrueCrypt License 2.6 the full text of which is contained 1.1 root 5: in the file License.txt included in TrueCrypt binary and source code 6: distribution packages. 7: */ 8: 9: #include "TCdefs.h" 10: #include "Apidrvr.h" 11: #include "Ntdriver.h" 1.1.1.6 root 12: #include "DriveFilter.h" 1.1 root 13: #include "EncryptedIoQueue.h" 1.1.1.5 root 14: #include "EncryptionThreadPool.h" 15: #include "Volumes.h" 1.1 root 16: 17: 1.1.1.8 ! root 18: static void AcquireBufferPoolMutex (EncryptedIoQueue *queue) ! 19: { ! 20: NTSTATUS status; ! 21: ! 22: status = KeWaitForMutexObject (&queue->BufferPoolMutex, Executive, KernelMode, FALSE, NULL); ! 23: if (!NT_SUCCESS (status)) ! 24: TC_BUG_CHECK (status); ! 25: } ! 26: ! 27: ! 28: static void ReleaseBufferPoolMutex (EncryptedIoQueue *queue) ! 29: { ! 30: KeReleaseMutex (&queue->BufferPoolMutex, FALSE); ! 31: } ! 32: ! 33: ! 34: static void *GetPoolBuffer (EncryptedIoQueue *queue, ULONG requestedSize) ! 35: { ! 36: EncryptedIoQueueBuffer *buffer; ! 37: void *bufferAddress = NULL; ! 38: BOOL requestedSizePresentInPool = FALSE; ! 39: ! 40: while (TRUE) ! 41: { ! 42: AcquireBufferPoolMutex (queue); ! 43: ! 44: for (buffer = queue->FirstPoolBuffer; ; buffer = buffer->NextBuffer) ! 45: { ! 46: if (buffer && buffer->Size == requestedSize) ! 47: { ! 48: requestedSizePresentInPool = TRUE; ! 49: ! 50: if (!buffer->InUse) ! 51: { ! 52: // Reuse a free buffer ! 53: buffer->InUse = TRUE; ! 54: bufferAddress = buffer->Address; ! 55: break; ! 56: } ! 57: } ! 58: ! 59: if (!buffer || !buffer->NextBuffer) ! 60: { ! 61: // Allocate a new buffer ! 62: EncryptedIoQueueBuffer *newBuffer = TCalloc (sizeof (EncryptedIoQueueBuffer)); ! 63: if (!newBuffer) ! 64: { ! 65: bufferAddress = NULL; ! 66: break; ! 67: } ! 68: ! 69: bufferAddress = TCalloc (requestedSize); ! 70: if (bufferAddress) ! 71: { ! 72: newBuffer->NextBuffer = NULL; ! 73: newBuffer->Address = bufferAddress; ! 74: newBuffer->Size = requestedSize; ! 75: newBuffer->InUse = TRUE; ! 76: ! 77: if (!buffer) ! 78: queue->FirstPoolBuffer = newBuffer; ! 79: else ! 80: buffer->NextBuffer = newBuffer; ! 81: } ! 82: else ! 83: TCfree (newBuffer); ! 84: ! 85: break; ! 86: } ! 87: } ! 88: ! 89: ReleaseBufferPoolMutex (queue); ! 90: ! 91: if (bufferAddress || !requestedSizePresentInPool || queue->StartPending) ! 92: break; ! 93: ! 94: TCSleep (TC_ENC_IO_QUEUE_MEM_ALLOC_RETRY_DELAY); ! 95: } ! 96: ! 97: return bufferAddress; ! 98: } ! 99: ! 100: ! 101: static void ReleasePoolBuffer (EncryptedIoQueue *queue, void *address) ! 102: { ! 103: EncryptedIoQueueBuffer *buffer; ! 104: AcquireBufferPoolMutex (queue); ! 105: ! 106: for (buffer = queue->FirstPoolBuffer; buffer != NULL; buffer = buffer->NextBuffer) ! 107: { ! 108: if (buffer->Address == address) ! 109: { ! 110: ASSERT (buffer->InUse); ! 111: ! 112: buffer->InUse = FALSE; ! 113: break; ! 114: } ! 115: } ! 116: ! 117: ReleaseBufferPoolMutex (queue); ! 118: } ! 119: ! 120: ! 121: static void FreePoolBuffers (EncryptedIoQueue *queue) ! 122: { ! 123: EncryptedIoQueueBuffer *buffer; ! 124: AcquireBufferPoolMutex (queue); ! 125: ! 126: for (buffer = queue->FirstPoolBuffer; buffer != NULL; ) ! 127: { ! 128: EncryptedIoQueueBuffer *nextBuffer = buffer->NextBuffer; ! 129: ! 130: ASSERT (!buffer->InUse); ! 131: ! 132: TCfree (buffer->Address); ! 133: TCfree (buffer); ! 134: ! 135: buffer = nextBuffer; ! 136: } ! 137: ! 138: queue->FirstPoolBuffer = NULL; ! 139: ReleaseBufferPoolMutex (queue); ! 140: } ! 141: ! 142: 1.1 root 143: static void DecrementOutstandingIoCount (EncryptedIoQueue *queue) 144: { 145: if (InterlockedDecrement (&queue->OutstandingIoCount) == 0 && (queue->SuspendPending || queue->StopPending)) 1.1.1.3 root 146: KeSetEvent (&queue->NoOutstandingIoEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 147: } 148: 149: 1.1.1.5 root 150: static void OnItemCompleted (EncryptedIoQueueItem *item, BOOL freeItem) 1.1 root 151: { 152: DecrementOutstandingIoCount (item->Queue); 153: 154: if (item->Queue->IsFilterDevice) 155: IoReleaseRemoveLock (&item->Queue->RemoveLock, item->OriginalIrp); 156: 157: if (NT_SUCCESS (item->Status)) 158: { 159: if (item->Write) 160: item->Queue->TotalBytesWritten += item->OriginalLength; 161: else 162: item->Queue->TotalBytesRead += item->OriginalLength; 163: } 164: 1.1.1.5 root 165: if (freeItem) 1.1.1.8 ! root 166: ReleasePoolBuffer (item->Queue, item); 1.1 root 167: } 168: 169: 170: static NTSTATUS CompleteOriginalIrp (EncryptedIoQueueItem *item, NTSTATUS status, ULONG_PTR information) 171: { 1.1.1.8 ! root 172: #ifdef TC_TRACE_IO_QUEUE ! 173: Dump ("< %I64d [%I64d] %c status=%x info=%I64d\n", item->OriginalIrpOffset, GetElapsedTime (&item->Queue->LastPerformanceCounter), item->Write ? 'W' : 'R', status, (int64) information); ! 174: #endif ! 175: 1.1 root 176: TCCompleteDiskIrp (item->OriginalIrp, status, information); 1.1.1.5 root 177: 178: item->Status = status; 179: OnItemCompleted (item, TRUE); 180: 1.1 root 181: return status; 182: } 183: 184: 185: static void AcquireFragmentBuffer (EncryptedIoQueue *queue, byte *buffer) 186: { 187: NTSTATUS status = STATUS_INVALID_PARAMETER; 188: 189: if (buffer == queue->FragmentBufferA) 190: { 191: status = KeWaitForSingleObject (&queue->FragmentBufferAFreeEvent, Executive, KernelMode, FALSE, NULL); 192: } 193: else if (buffer == queue->FragmentBufferB) 194: { 195: status = KeWaitForSingleObject (&queue->FragmentBufferBFreeEvent, Executive, KernelMode, FALSE, NULL); 196: } 197: 198: if (!NT_SUCCESS (status)) 199: TC_BUG_CHECK (status); 200: } 201: 202: 203: static void ReleaseFragmentBuffer (EncryptedIoQueue *queue, byte *buffer) 204: { 205: if (buffer == queue->FragmentBufferA) 206: { 1.1.1.3 root 207: KeSetEvent (&queue->FragmentBufferAFreeEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 208: } 209: else if (buffer == queue->FragmentBufferB) 210: { 1.1.1.3 root 211: KeSetEvent (&queue->FragmentBufferBFreeEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 212: } 213: else 214: { 215: TC_BUG_CHECK (STATUS_INVALID_PARAMETER); 216: } 217: } 218: 219: 220: static VOID CompletionThreadProc (PVOID threadArg) 221: { 222: EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; 223: PLIST_ENTRY listEntry; 224: EncryptedIoRequest *request; 225: UINT64_STRUCT dataUnit; 226: 1.1.1.5 root 227: if (IsEncryptionThreadPoolRunning()) 228: KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); 229: 1.1 root 230: while (!queue->ThreadExitRequested) 231: { 232: if (!NT_SUCCESS (KeWaitForSingleObject (&queue->CompletionThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) 233: continue; 234: 235: if (queue->ThreadExitRequested) 236: break; 237: 238: while ((listEntry = ExInterlockedRemoveHeadList (&queue->CompletionThreadQueue, &queue->CompletionThreadQueueLock))) 239: { 240: request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, CompletionListEntry); 241: 1.1.1.3 root 242: if (request->EncryptedLength > 0 && NT_SUCCESS (request->Item->Status)) 1.1 root 243: { 244: ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length); 245: dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE; 1.1.1.3 root 246: 247: if (queue->CryptoInfo->bPartitionInInactiveSysEncScope) 248: dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value; 1.1.1.5 root 249: else if (queue->RemapEncryptedArea) 250: dataUnit.Value += queue->RemappedAreaDataUnitOffset; 1.1.1.3 root 251: 1.1 root 252: DecryptDataUnits (request->Data + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); 253: } 254: 255: if (request->CompleteOriginalIrp) 256: { 257: CompleteOriginalIrp (request->Item, request->Item->Status, 258: NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); 259: } 1.1.1.3 root 260: else 261: { 262: InterlockedDecrement (&request->Item->OutstandingRequestCount); 263: KeSetEvent (&queue->RequestCompletedEvent, IO_DISK_INCREMENT, FALSE); 264: } 1.1 root 265: 1.1.1.8 ! root 266: ReleasePoolBuffer (queue, request); 1.1 root 267: } 268: } 269: 270: PsTerminateSystemThread (STATUS_SUCCESS); 271: } 272: 273: 1.1.1.8 ! root 274: static NTSTATUS TCCachedRead (EncryptedIoQueue *queue, IO_STATUS_BLOCK *ioStatus, PVOID buffer, LARGE_INTEGER offset, ULONG length) ! 275: { ! 276: queue->LastReadOffset = offset; ! 277: queue->LastReadLength = length; ! 278: ! 279: if (queue->ReadAheadBufferValid && queue->ReadAheadOffset.QuadPart == offset.QuadPart && queue->ReadAheadLength >= length) ! 280: { ! 281: memcpy (buffer, queue->ReadAheadBuffer, length); ! 282: ! 283: if (!queue->IsFilterDevice) ! 284: { ! 285: ioStatus->Information = length; ! 286: ioStatus->Status = STATUS_SUCCESS; ! 287: } ! 288: ! 289: return STATUS_SUCCESS; ! 290: } ! 291: ! 292: if (queue->IsFilterDevice) ! 293: return TCReadDevice (queue->LowerDeviceObject, buffer, offset, length); ! 294: ! 295: return ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, ioStatus, buffer, length, &offset, NULL); ! 296: } ! 297: ! 298: 1.1 root 299: static VOID IoThreadProc (PVOID threadArg) 300: { 301: EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; 302: PLIST_ENTRY listEntry; 303: EncryptedIoRequest *request; 304: 1.1.1.4 root 305: KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); 306: 1.1.1.6 root 307: if (!queue->IsFilterDevice && queue->SecurityClientContext) 308: { 309: #ifdef DEBUG 310: NTSTATUS status = 311: #endif 312: SeImpersonateClientEx (queue->SecurityClientContext, NULL); 313: ASSERT (NT_SUCCESS (status)); 314: } 315: 1.1 root 316: while (!queue->ThreadExitRequested) 317: { 318: if (!NT_SUCCESS (KeWaitForSingleObject (&queue->IoThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) 319: continue; 320: 321: if (queue->ThreadExitRequested) 322: break; 323: 324: while ((listEntry = ExInterlockedRemoveHeadList (&queue->IoThreadQueue, &queue->IoThreadQueueLock))) 325: { 1.1.1.8 ! root 326: InterlockedDecrement (&queue->IoThreadPendingRequestCount); 1.1 root 327: request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, ListEntry); 1.1.1.3 root 328: 1.1.1.8 ! root 329: #ifdef TC_TRACE_IO_QUEUE ! 330: Dump ("%c %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->Write ? 'W' : 'R', request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), request->Offset.QuadPart, request->Length); ! 331: #endif ! 332: 1.1.1.3 root 333: // Perform IO request if no preceding request of the item failed 334: if (NT_SUCCESS (request->Item->Status)) 1.1 root 335: { 1.1.1.3 root 336: if (queue->IsFilterDevice) 337: { 1.1.1.5 root 338: if (queue->RemapEncryptedArea && request->EncryptedLength > 0) 339: { 340: if (request->EncryptedLength != request->Length) 341: { 342: // Up to three subfragments may be required to handle a partially remapped fragment 343: int subFragment; 344: byte *subFragmentData = request->Data; 345: 346: for (subFragment = 0 ; subFragment < 3; ++subFragment) 347: { 348: LARGE_INTEGER subFragmentOffset; 349: ULONG subFragmentLength; 350: subFragmentOffset.QuadPart = request->Offset.QuadPart; 351: 352: switch (subFragment) 353: { 354: case 0: 355: subFragmentLength = (ULONG) request->EncryptedOffset; 356: break; 357: 358: case 1: 359: subFragmentOffset.QuadPart += request->EncryptedOffset + queue->RemappedAreaOffset; 360: subFragmentLength = request->EncryptedLength; 361: break; 362: 363: case 2: 364: subFragmentOffset.QuadPart += request->EncryptedOffset + request->EncryptedLength; 365: subFragmentLength = (ULONG) (request->Length - (request->EncryptedOffset + request->EncryptedLength)); 366: break; 367: } 368: 369: if (subFragmentLength > 0) 370: { 371: if (request->Item->Write) 372: request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, subFragmentData, subFragmentOffset, subFragmentLength); 373: else 1.1.1.8 ! root 374: request->Item->Status = TCCachedRead (queue, NULL, subFragmentData, subFragmentOffset, subFragmentLength); 1.1.1.5 root 375: 376: subFragmentData += subFragmentLength; 377: } 378: } 379: } 380: else 381: { 382: // Remap the fragment 383: LARGE_INTEGER remappedOffset; 384: remappedOffset.QuadPart = request->Offset.QuadPart + queue->RemappedAreaOffset; 385: 386: if (request->Item->Write) 387: request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, remappedOffset, request->Length); 388: else 1.1.1.8 ! root 389: request->Item->Status = TCCachedRead (queue, NULL, request->Data, remappedOffset, request->Length); 1.1.1.5 root 390: } 391: } 1.1.1.3 root 392: else 1.1.1.5 root 393: { 394: if (request->Item->Write) 395: request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, request->Offset, request->Length); 396: else 1.1.1.8 ! root 397: request->Item->Status = TCCachedRead (queue, NULL, request->Data, request->Offset, request->Length); 1.1.1.5 root 398: } 1.1.1.3 root 399: } 1.1 root 400: else 1.1.1.3 root 401: { 402: IO_STATUS_BLOCK ioStatus; 1.1 root 403: 1.1.1.3 root 404: if (request->Item->Write) 405: request->Item->Status = ZwWriteFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, request->Data, request->Length, &request->Offset, NULL); 406: else 1.1.1.8 ! root 407: request->Item->Status = TCCachedRead (queue, &ioStatus, request->Data, request->Offset, request->Length); 1.1.1.5 root 408: 409: if (NT_SUCCESS (request->Item->Status) && ioStatus.Information != request->Length) 410: request->Item->Status = STATUS_END_OF_FILE; 1.1.1.3 root 411: } 1.1 root 412: } 413: 1.1.1.3 root 414: if (request->Item->Write) 1.1 root 415: { 1.1.1.8 ! root 416: queue->ReadAheadBufferValid = FALSE; ! 417: 1.1 root 418: ReleaseFragmentBuffer (queue, request->Data); 419: 420: if (request->CompleteOriginalIrp) 421: { 422: CompleteOriginalIrp (request->Item, request->Item->Status, 423: NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); 424: } 425: else 426: { 1.1.1.3 root 427: InterlockedDecrement (&request->Item->OutstandingRequestCount); 428: KeSetEvent (&queue->RequestCompletedEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 429: } 430: 1.1.1.8 ! root 431: ReleasePoolBuffer (queue, request); 1.1 root 432: } 1.1.1.3 root 433: else 434: { 1.1.1.8 ! root 435: BOOL readAhead = FALSE; ! 436: 1.1.1.3 root 437: if (NT_SUCCESS (request->Item->Status)) 438: memcpy (request->OrigDataBufferFragment, request->Data, request->Length); 439: 440: ReleaseFragmentBuffer (queue, request->Data); 441: request->Data = request->OrigDataBufferFragment; 442: 1.1.1.8 ! root 443: if (request->CompleteOriginalIrp ! 444: && queue->LastReadLength > 0 ! 445: && NT_SUCCESS (request->Item->Status) ! 446: && InterlockedExchangeAdd (&queue->IoThreadPendingRequestCount, 0) == 0) ! 447: { ! 448: readAhead = TRUE; ! 449: InterlockedIncrement (&queue->OutstandingIoCount); ! 450: } ! 451: 1.1.1.3 root 452: ExInterlockedInsertTailList (&queue->CompletionThreadQueue, &request->CompletionListEntry, &queue->CompletionThreadQueueLock); 453: KeSetEvent (&queue->CompletionThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); 1.1.1.8 ! root 454: ! 455: if (readAhead) ! 456: { ! 457: queue->ReadAheadBufferValid = FALSE; ! 458: queue->ReadAheadOffset.QuadPart = queue->LastReadOffset.QuadPart + queue->LastReadLength; ! 459: queue->ReadAheadLength = queue->LastReadLength; ! 460: ! 461: #ifdef TC_TRACE_IO_QUEUE ! 462: Dump ("A %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), queue->ReadAheadOffset, queue->ReadAheadLength); ! 463: #endif ! 464: ! 465: if (queue->ReadAheadOffset.QuadPart + queue->ReadAheadLength <= queue->MaxReadAheadOffset.QuadPart) ! 466: { ! 467: if (queue->IsFilterDevice) ! 468: { ! 469: queue->ReadAheadBufferValid = NT_SUCCESS (TCReadDevice (queue->LowerDeviceObject, queue->ReadAheadBuffer, queue->ReadAheadOffset, queue->ReadAheadLength)); ! 470: } ! 471: else ! 472: { ! 473: IO_STATUS_BLOCK ioStatus; ! 474: queue->ReadAheadBufferValid = NT_SUCCESS (ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, queue->ReadAheadBuffer, queue->ReadAheadLength, &queue->ReadAheadOffset, NULL)); ! 475: queue->ReadAheadLength = (ULONG) ioStatus.Information; ! 476: } ! 477: } ! 478: ! 479: DecrementOutstandingIoCount (queue); ! 480: } 1.1.1.3 root 481: } 1.1 root 482: } 483: } 484: 485: PsTerminateSystemThread (STATUS_SUCCESS); 486: } 487: 488: 489: static VOID MainThreadProc (PVOID threadArg) 490: { 491: EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; 492: PLIST_ENTRY listEntry; 493: EncryptedIoQueueItem *item; 494: 495: LARGE_INTEGER fragmentOffset; 496: ULONG dataRemaining; 497: PUCHAR activeFragmentBuffer = queue->FragmentBufferA; 498: PUCHAR dataBuffer; 499: EncryptedIoRequest *request; 500: uint64 intersectStart; 501: uint32 intersectLength; 502: 1.1.1.8 ! root 503: int64 mdlWaitTime; ! 504: LARGE_INTEGER mdlWaitPerfCounter; 1.1.1.5 root 505: 506: if (IsEncryptionThreadPoolRunning()) 507: KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); 508: 1.1 root 509: while (!queue->ThreadExitRequested) 510: { 511: if (!NT_SUCCESS (KeWaitForSingleObject (&queue->MainThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) 512: continue; 513: 514: while ((listEntry = ExInterlockedRemoveHeadList (&queue->MainThreadQueue, &queue->MainThreadQueueLock))) 515: { 1.1.1.5 root 516: PIRP irp = CONTAINING_RECORD (listEntry, IRP, Tail.Overlay.ListEntry); 517: PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); 518: 1.1 root 519: if (queue->Suspended) 520: KeWaitForSingleObject (&queue->QueueResumedEvent, Executive, KernelMode, FALSE, NULL); 1.1.1.5 root 521: 1.1.1.8 ! root 522: item = GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem)); 1.1.1.5 root 523: if (!item) 524: { 525: EncryptedIoQueueItem stackItem; 526: stackItem.Queue = queue; 527: stackItem.OriginalIrp = irp; 528: stackItem.Status = STATUS_INSUFFICIENT_RESOURCES; 529: 530: TCCompleteDiskIrp (irp, STATUS_INSUFFICIENT_RESOURCES, 0); 531: OnItemCompleted (&stackItem, FALSE); 532: continue; 1.1 root 533: } 1.1.1.5 root 534: 535: item->Queue = queue; 536: item->OriginalIrp = irp; 537: item->OutstandingRequestCount = 0; 538: item->Status = STATUS_SUCCESS; 539: 540: IoSetCancelRoutine (irp, NULL); 541: if (irp->Cancel) 1.1 root 542: { 543: CompleteOriginalIrp (item, STATUS_CANCELLED, 0); 544: continue; 545: } 546: 1.1.1.5 root 547: switch (irpSp->MajorFunction) 548: { 549: case IRP_MJ_READ: 550: item->Write = FALSE; 551: item->OriginalOffset = irpSp->Parameters.Read.ByteOffset; 552: item->OriginalLength = irpSp->Parameters.Read.Length; 553: break; 554: 555: case IRP_MJ_WRITE: 556: item->Write = TRUE; 557: item->OriginalOffset = irpSp->Parameters.Write.ByteOffset; 558: item->OriginalLength = irpSp->Parameters.Write.Length; 559: break; 560: 561: default: 562: CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); 563: continue; 564: } 565: 1.1.1.8 ! root 566: #ifdef TC_TRACE_IO_QUEUE ! 567: item->OriginalIrpOffset = item->OriginalOffset; ! 568: #endif 1.1 root 569: 1.1.1.8 ! root 570: // Handle misaligned read operations to work around a bug in Windows System Assessment Tool which does not follow FILE_FLAG_NO_BUFFERING requirements when benchmarking disk devices 1.1.1.7 root 571: if (queue->IsFilterDevice 572: && !item->Write 573: && item->OriginalLength > 0 574: && (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) == 0 575: && (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) 576: { 577: byte *buffer; 578: ULONG alignedLength = item->OriginalLength + ENCRYPTION_DATA_UNIT_SIZE; 579: LARGE_INTEGER alignedOffset; 580: alignedOffset.QuadPart = item->OriginalOffset.QuadPart & ~((LONGLONG) ENCRYPTION_DATA_UNIT_SIZE - 1); 581: 582: buffer = TCalloc (alignedLength); 583: if (!buffer) 584: { 585: CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); 586: continue; 587: } 588: 589: item->Status = TCReadDevice (queue->LowerDeviceObject, buffer, alignedOffset, alignedLength); 590: 591: if (NT_SUCCESS (item->Status)) 592: { 593: UINT64_STRUCT dataUnit; 594: 595: dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority); 596: if (!dataBuffer) 597: { 598: TCfree (buffer); 599: CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); 600: continue; 601: } 602: 1.1.1.8 ! root 603: if (queue->EncryptedAreaStart != -1 && queue->EncryptedAreaEnd != -1) 1.1.1.7 root 604: { 1.1.1.8 ! root 605: GetIntersection (alignedOffset.QuadPart, alignedLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength); ! 606: if (intersectLength > 0) ! 607: { ! 608: dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; ! 609: DecryptDataUnits (buffer + (intersectStart - alignedOffset.QuadPart), &dataUnit, intersectLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); ! 610: } 1.1.1.7 root 611: } 612: 613: memcpy (dataBuffer, buffer + (item->OriginalOffset.LowPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)), item->OriginalLength); 614: } 615: 616: TCfree (buffer); 617: CompleteOriginalIrp (item, item->Status, NT_SUCCESS (item->Status) ? item->OriginalLength : 0); 618: continue; 619: } 620: 1.1 root 621: // Validate offset and length 1.1.1.3 root 622: if (item->OriginalLength == 0 623: || (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0 624: || (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0 1.1 root 625: || (!queue->IsFilterDevice && item->OriginalOffset.QuadPart + item->OriginalLength > queue->VirtualDeviceLength)) 626: { 627: CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); 628: continue; 629: } 630: 1.1.1.8 ! root 631: #ifdef TC_TRACE_IO_QUEUE ! 632: Dump ("Q %I64d [%I64d] %c len=%d\n", item->OriginalOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), item->Write ? 'W' : 'R', item->OriginalLength); ! 633: #endif 1.1.1.5 root 634: 1.1 root 635: if (!queue->IsFilterDevice) 636: { 1.1.1.3 root 637: // Adjust the offset for host file or device 638: if (queue->CryptoInfo->hiddenVolume) 639: item->OriginalOffset.QuadPart += queue->CryptoInfo->hiddenVolumeOffset; 640: else 641: item->OriginalOffset.QuadPart += queue->CryptoInfo->volDataAreaOffset; 642: 1.1 root 643: // Hidden volume protection 644: if (item->Write && queue->CryptoInfo->bProtectHiddenVolume) 645: { 646: // If there has already been a write operation denied in order to protect the 647: // hidden volume (since the volume mount time) 648: if (queue->CryptoInfo->bHiddenVolProtectionAction) 649: { 650: // Do not allow writing to this volume anymore. This is to fake a complete volume 651: // or system failure (otherwise certain kinds of inconsistency within the file 652: // system could indicate that this volume has used hidden volume protection). 653: CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); 654: continue; 655: } 656: 657: // Verify that no byte is going to be written to the hidden volume area 1.1.1.3 root 658: if (RegionsOverlap ((unsigned __int64) item->OriginalOffset.QuadPart, 659: (unsigned __int64) item->OriginalOffset.QuadPart + item->OriginalLength - 1, 1.1 root 660: queue->CryptoInfo->hiddenVolumeOffset, 1.1.1.5 root 661: (unsigned __int64) queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1)) 1.1 root 662: { 1.1.1.5 root 663: Dump ("Hidden volume protection triggered: write %I64d-%I64d (protected %I64d-%I64d)\n", item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, queue->CryptoInfo->hiddenVolumeOffset, queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1); 1.1 root 664: queue->CryptoInfo->bHiddenVolProtectionAction = TRUE; 665: 666: // Deny this write operation to prevent the hidden volume from being overwritten 667: CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); 668: continue; 669: } 670: } 671: } 1.1.1.6 root 672: else if (item->Write && IsHiddenSystemRunning() 673: && (RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, SECTOR_SIZE, TC_BOOT_LOADER_AREA_SECTOR_COUNT * SECTOR_SIZE - 1) 674: || RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, GetBootDriveLength(), _I64_MAX))) 675: { 676: Dump ("Preventing write to boot loader or host protected area\n"); 677: CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0); 678: continue; 679: } 1.1 root 680: 681: // Original IRP data buffer 1.1.1.5 root 682: mdlWaitTime = 0; 1.1.1.8 ! root 683: mdlWaitPerfCounter.QuadPart = 0; 1.1.1.5 root 684: while (TRUE) 685: { 686: dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority); 687: 688: if (dataBuffer || mdlWaitTime >= TC_ENC_IO_QUEUE_MEM_ALLOC_TIMEOUT) 689: break; 690: 1.1.1.8 ! root 691: if (mdlWaitPerfCounter.QuadPart == 0) ! 692: GetElapsedTimeInit (&mdlWaitPerfCounter); ! 693: ! 694: TCSleep (TC_ENC_IO_QUEUE_MEM_ALLOC_RETRY_DELAY); ! 695: mdlWaitTime += GetElapsedTime (&mdlWaitPerfCounter) / 1000; 1.1.1.5 root 696: } 697: 1.1 root 698: if (dataBuffer == NULL) 699: { 700: CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); 701: continue; 702: } 703: 704: // Divide data block to fragments to enable efficient overlapping of encryption and IO operations 705: 706: dataRemaining = item->OriginalLength; 707: fragmentOffset = item->OriginalOffset; 708: 709: while (dataRemaining > 0) 710: { 711: BOOL isLastFragment = dataRemaining <= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; 712: 713: ULONG dataFragmentLength = isLastFragment ? dataRemaining : TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; 714: activeFragmentBuffer = (activeFragmentBuffer == queue->FragmentBufferA ? queue->FragmentBufferB : queue->FragmentBufferA); 715: 716: // Create IO request 1.1.1.8 ! root 717: request = GetPoolBuffer (queue, sizeof (EncryptedIoRequest)); 1.1 root 718: if (!request) 719: { 1.1.1.3 root 720: while (InterlockedExchangeAdd (&item->OutstandingRequestCount, 0) > 0) 721: KeWaitForSingleObject (&queue->RequestCompletedEvent, Executive, KernelMode, FALSE, NULL); 722: 1.1 root 723: CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); 724: break; 725: } 726: 1.1.1.8 ! root 727: InterlockedIncrement (&queue->IoThreadPendingRequestCount); ! 728: 1.1 root 729: request->Item = item; 730: request->CompleteOriginalIrp = isLastFragment; 731: request->Offset = fragmentOffset; 732: request->Data = activeFragmentBuffer; 733: request->OrigDataBufferFragment = dataBuffer; 734: request->Length = dataFragmentLength; 735: 736: if (queue->IsFilterDevice) 737: { 1.1.1.8 ! root 738: if (queue->EncryptedAreaStart == -1 || queue->EncryptedAreaEnd == -1) ! 739: { ! 740: request->EncryptedLength = 0; ! 741: } ! 742: else ! 743: { ! 744: // Get intersection of data fragment with encrypted area ! 745: GetIntersection (fragmentOffset.QuadPart, dataFragmentLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength); 1.1 root 746: 1.1.1.8 ! root 747: request->EncryptedOffset = intersectStart - fragmentOffset.QuadPart; ! 748: request->EncryptedLength = intersectLength; ! 749: } 1.1 root 750: } 751: else 752: { 753: request->EncryptedOffset = 0; 754: request->EncryptedLength = dataFragmentLength; 755: } 756: 757: AcquireFragmentBuffer (queue, activeFragmentBuffer); 758: 759: if (item->Write) 760: { 761: // Encrypt data 762: memcpy (activeFragmentBuffer, dataBuffer, dataFragmentLength); 763: 764: if (request->EncryptedLength > 0) 765: { 766: UINT64_STRUCT dataUnit; 767: ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length); 768: 769: dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE; 770: 1.1.1.3 root 771: if (queue->CryptoInfo->bPartitionInInactiveSysEncScope) 772: dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value; 1.1.1.5 root 773: else if (queue->RemapEncryptedArea) 774: dataUnit.Value += queue->RemappedAreaDataUnitOffset; 775: 1.1.1.3 root 776: EncryptDataUnits (activeFragmentBuffer + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); 1.1 root 777: } 778: } 779: 780: // Queue IO request 1.1.1.3 root 781: InterlockedIncrement (&item->OutstandingRequestCount); 782: 1.1 root 783: ExInterlockedInsertTailList (&queue->IoThreadQueue, &request->ListEntry, &queue->IoThreadQueueLock); 1.1.1.3 root 784: KeSetEvent (&queue->IoThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 785: 786: if (isLastFragment) 787: break; 788: 789: dataRemaining -= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; 790: dataBuffer += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; 791: fragmentOffset.QuadPart += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; 792: } 793: } 794: } 795: 796: PsTerminateSystemThread (STATUS_SUCCESS); 797: } 798: 799: 800: NTSTATUS EncryptedIoQueueAddIrp (EncryptedIoQueue *queue, PIRP irp) 801: { 802: NTSTATUS status; 803: 804: InterlockedIncrement (&queue->OutstandingIoCount); 805: if (queue->StopPending) 806: { 807: Dump ("STATUS_DEVICE_NOT_READY out=%d\n", queue->OutstandingIoCount); 808: status = STATUS_DEVICE_NOT_READY; 809: goto err; 810: } 811: 812: if (queue->IsFilterDevice) 813: { 814: status = IoAcquireRemoveLock (&queue->RemoveLock, irp); 815: if (!NT_SUCCESS (status)) 816: goto err; 817: } 818: 1.1.1.8 ! root 819: #ifdef TC_TRACE_IO_QUEUE ! 820: { ! 821: PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); ! 822: Dump ("* %I64d [%I64d] %c len=%d out=%d\n", irpSp->MajorFunction == IRP_MJ_WRITE ? irpSp->Parameters.Write.ByteOffset : irpSp->Parameters.Read.ByteOffset, GetElapsedTime (&queue->LastPerformanceCounter), irpSp->MajorFunction == IRP_MJ_WRITE ? 'W' : 'R', irpSp->MajorFunction == IRP_MJ_WRITE ? irpSp->Parameters.Write.Length : irpSp->Parameters.Read.Length, queue->OutstandingIoCount); ! 823: } ! 824: #endif ! 825: 1.1 root 826: IoMarkIrpPending (irp); 827: 1.1.1.5 root 828: ExInterlockedInsertTailList (&queue->MainThreadQueue, &irp->Tail.Overlay.ListEntry, &queue->MainThreadQueueLock); 1.1.1.3 root 829: KeSetEvent (&queue->MainThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 830: 831: return STATUS_PENDING; 832: 833: err: 834: DecrementOutstandingIoCount (queue); 835: return status; 836: } 837: 838: 839: NTSTATUS EncryptedIoQueueHoldWhenIdle (EncryptedIoQueue *queue, int64 timeout) 840: { 841: NTSTATUS status; 842: ASSERT (!queue->Suspended); 843: 844: queue->SuspendPending = TRUE; 845: 846: while (TRUE) 847: { 848: while (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) 849: { 850: LARGE_INTEGER waitTimeout; 851: 852: waitTimeout.QuadPart = timeout * -10000; 853: status = KeWaitForSingleObject (&queue->NoOutstandingIoEvent, Executive, KernelMode, FALSE, timeout != 0 ? &waitTimeout : NULL); 854: 855: if (status == STATUS_TIMEOUT) 856: status = STATUS_UNSUCCESSFUL; 857: 858: if (!NT_SUCCESS (status)) 1.1.1.8 ! root 859: { ! 860: queue->SuspendPending = FALSE; 1.1 root 861: return status; 1.1.1.8 ! root 862: } 1.1.1.6 root 863: 1.1.1.7 root 864: TCSleep (1); 1.1.1.6 root 865: if (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) 1.1.1.8 ! root 866: { ! 867: queue->SuspendPending = FALSE; 1.1.1.6 root 868: return STATUS_UNSUCCESSFUL; 1.1.1.8 ! root 869: } 1.1 root 870: } 871: 872: KeClearEvent (&queue->QueueResumedEvent); 873: queue->Suspended = TRUE; 874: 875: if (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) == 0) 876: break; 877: 878: queue->Suspended = FALSE; 1.1.1.3 root 879: KeSetEvent (&queue->QueueResumedEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 880: } 881: 1.1.1.8 ! root 882: queue->ReadAheadBufferValid = FALSE; 1.1 root 883: 1.1.1.8 ! root 884: queue->SuspendPending = FALSE; 1.1 root 885: return STATUS_SUCCESS; 886: } 887: 888: 889: BOOL EncryptedIoQueueIsSuspended (EncryptedIoQueue *queue) 890: { 891: return queue->Suspended; 892: } 893: 894: 895: BOOL EncryptedIoQueueIsRunning (EncryptedIoQueue *queue) 896: { 897: return !queue->StopPending; 898: } 899: 900: 901: NTSTATUS EncryptedIoQueueResumeFromHold (EncryptedIoQueue *queue) 902: { 903: ASSERT (queue->Suspended); 904: 905: queue->Suspended = FALSE; 1.1.1.3 root 906: KeSetEvent (&queue->QueueResumedEvent, IO_DISK_INCREMENT, FALSE); 1.1 root 907: 908: return STATUS_SUCCESS; 909: } 910: 911: 1.1.1.6 root 912: NTSTATUS EncryptedIoQueueStart (EncryptedIoQueue *queue) 1.1 root 913: { 914: NTSTATUS status; 1.1.1.8 ! root 915: EncryptedIoQueueBuffer *buffer; ! 916: int i; ! 917: ! 918: queue->StartPending = TRUE; 1.1 root 919: queue->ThreadExitRequested = FALSE; 920: 1.1.1.8 ! root 921: queue->OutstandingIoCount = 0; ! 922: queue->IoThreadPendingRequestCount = 0; ! 923: ! 924: queue->FirstPoolBuffer = NULL; ! 925: KeInitializeMutex (&queue->BufferPoolMutex, 0); ! 926: 1.1 root 927: KeInitializeEvent (&queue->NoOutstandingIoEvent, SynchronizationEvent, FALSE); 1.1.1.3 root 928: KeInitializeEvent (&queue->RequestCompletedEvent, SynchronizationEvent, FALSE); 1.1 root 929: KeInitializeEvent (&queue->QueueResumedEvent, SynchronizationEvent, FALSE); 930: 931: queue->FragmentBufferA = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); 932: if (!queue->FragmentBufferA) 933: goto noMemory; 934: 935: queue->FragmentBufferB = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); 936: if (!queue->FragmentBufferB) 937: goto noMemory; 938: 939: KeInitializeEvent (&queue->FragmentBufferAFreeEvent, SynchronizationEvent, TRUE); 940: KeInitializeEvent (&queue->FragmentBufferBFreeEvent, SynchronizationEvent, TRUE); 941: 1.1.1.8 ! root 942: queue->ReadAheadBufferValid = FALSE; ! 943: queue->ReadAheadBuffer = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); ! 944: if (!queue->ReadAheadBuffer) ! 945: goto noMemory; ! 946: ! 947: // Preallocate buffers ! 948: for (i = 0; i < TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT; ++i) ! 949: { ! 950: if (i < TC_ENC_IO_QUEUE_PREALLOCATED_ITEM_COUNT && !GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem))) ! 951: goto noMemory; ! 952: ! 953: if (!GetPoolBuffer (queue, sizeof (EncryptedIoRequest))) ! 954: goto noMemory; ! 955: } ! 956: ! 957: for (buffer = queue->FirstPoolBuffer; buffer != NULL; buffer = buffer->NextBuffer) ! 958: { ! 959: buffer->InUse = FALSE; ! 960: } ! 961: 1.1 root 962: // Main thread 963: InitializeListHead (&queue->MainThreadQueue); 964: KeInitializeSpinLock (&queue->MainThreadQueueLock); 965: KeInitializeEvent (&queue->MainThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); 966: 967: status = TCStartThread (MainThreadProc, queue, &queue->MainThread); 968: if (!NT_SUCCESS (status)) 969: goto err; 970: 971: // IO thread 972: InitializeListHead (&queue->IoThreadQueue); 973: KeInitializeSpinLock (&queue->IoThreadQueueLock); 974: KeInitializeEvent (&queue->IoThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); 975: 1.1.1.6 root 976: status = TCStartThread (IoThreadProc, queue, &queue->IoThread); 1.1 root 977: if (!NT_SUCCESS (status)) 978: { 979: queue->ThreadExitRequested = TRUE; 980: TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); 981: goto err; 982: } 983: 984: // Completion thread 985: InitializeListHead (&queue->CompletionThreadQueue); 986: KeInitializeSpinLock (&queue->CompletionThreadQueueLock); 987: KeInitializeEvent (&queue->CompletionThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); 988: 989: status = TCStartThread (CompletionThreadProc, queue, &queue->CompletionThread); 990: if (!NT_SUCCESS (status)) 991: { 992: queue->ThreadExitRequested = TRUE; 993: TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); 994: TCStopThread (queue->IoThread, &queue->IoThreadQueueNotEmptyEvent); 995: goto err; 996: } 997: 1.1.1.8 ! root 998: #ifdef TC_TRACE_IO_QUEUE ! 999: GetElapsedTimeInit (&queue->LastPerformanceCounter); ! 1000: #endif ! 1001: 1.1 root 1002: queue->StopPending = FALSE; 1.1.1.8 ! root 1003: queue->StartPending = FALSE; ! 1004: 1.1 root 1005: Dump ("Queue started\n"); 1006: return STATUS_SUCCESS; 1007: 1008: noMemory: 1009: status = STATUS_INSUFFICIENT_RESOURCES; 1010: 1011: err: 1012: if (queue->FragmentBufferA) 1013: TCfree (queue->FragmentBufferA); 1014: if (queue->FragmentBufferB) 1015: TCfree (queue->FragmentBufferB); 1016: 1.1.1.8 ! root 1017: FreePoolBuffers (queue); ! 1018: ! 1019: queue->StartPending = FALSE; 1.1 root 1020: return status; 1021: } 1022: 1023: 1024: NTSTATUS EncryptedIoQueueStop (EncryptedIoQueue *queue) 1025: { 1026: ASSERT (!queue->StopPending); 1027: queue->StopPending = TRUE; 1028: 1029: while (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) 1030: { 1031: KeWaitForSingleObject (&queue->NoOutstandingIoEvent, Executive, KernelMode, FALSE, NULL); 1032: } 1033: 1034: Dump ("Queue stopping out=%d\n", queue->OutstandingIoCount); 1035: 1036: queue->ThreadExitRequested = TRUE; 1037: 1038: TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); 1039: TCStopThread (queue->IoThread, &queue->IoThreadQueueNotEmptyEvent); 1040: TCStopThread (queue->CompletionThread, &queue->CompletionThreadQueueNotEmptyEvent); 1041: 1042: TCfree (queue->FragmentBufferA); 1043: TCfree (queue->FragmentBufferB); 1.1.1.8 ! root 1044: TCfree (queue->ReadAheadBuffer); ! 1045: ! 1046: FreePoolBuffers (queue); 1.1 root 1047: 1048: Dump ("Queue stopped out=%d\n", queue->OutstandingIoCount); 1049: return STATUS_SUCCESS; 1050: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.