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.

584 lines
20 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. Chunk.c
  5. Abstract:
  6. This module implements the copychunk interface for the smbcsc agent.
  7. Author:
  8. Joe Linn [JoeLinn] 10-apr-1997
  9. Revision History:
  10. Notes:
  11. The following describes the intended implementation.
  12. On win95, the following sequence is performed:
  13. Win32OpenWithCopyChunkIntent ();
  14. while (MoreToDo()) {
  15. Win32CopyChunk();
  16. }
  17. Win32CloseWithCopyChunkIntent ();
  18. Win32OpenWithCopyChunkIntent and Win32CloseWithCopyChunkIntent are implemented
  19. by Win32 open and close operations; CopyChunk is an ioctl. On NT, all three
  20. operations will be performed by ioctls. Internally, the will allow internal
  21. NT-only calls to be used as appropriate. A major advantage of implementing
  22. Win32OpenWithCopyChunkIntent as an ioctl is that the intent is unambiguously
  23. captured.
  24. A wrapper modification has been made whereby a calldown to the minirdr is
  25. made before collapsing is tried..a minirdr is able to bypass collapsing using
  26. this calldown.
  27. There are two important cases: surrogate opens and copychunk-thru opens. For
  28. surrogate opens, the mini is able to discover an existing srvopen (the
  29. surrogate) with read access. Here, the mini simply records the surrogate
  30. srvopen (and surrounding UID/PID in the smbFcb for use later with the read.
  31. For copychunk-thru opens, the mini must go on the wire with an open. When complete,
  32. it records in the smbFcb all of the appropriate stuff.
  33. Thus, when a OpenWithCopyChunkIntent comes in one of the following will obtain:
  34. 1. a surrogate can be found; information is recorded and the open succeeds
  35. 2. there is an existing open and no surrogate is found and the open fails
  36. 3. nonchunk opens are in progress..the open fails
  37. 4. a copychunk-thru is attempted at the server. Here, we must stall
  38. subsequent opens on the same fcb. When the open completes we have
  39. two cases:
  40. a. the open failed. Unblock any stalled opens and fail the open
  41. b. the open succeeded. Record the information, unblock the stalled
  42. guys and the open succeeds.
  43. A surrogate open is invalidated when the corresponding srvopen is closed..the
  44. data is in the fcb so normal Fcb serialization makes this work correctly. A
  45. copychunk-thru open is invalidated by any any nonchunk open on the same fcb.
  46. The logistics will be handled by MrxSmbCscCloseCopychunkThruOpen; the major
  47. problem will be to get into an exchange in the right security context (i.e. UID).
  48. An OpenWithCopyChunkIntent is implemented as a normal open except that it is
  49. identified (currently) by using specifying a profile of
  50. FILE_OPEN
  51. FILE_READ_ATTRIBUTES
  52. AllocationSize = {`\377ffCSC',?ioctl-irp}
  53. A ReadWithCopyChunkIntent and CloseWithCopyChunkIntent just normal read and
  54. close operations but are further identified by a bit set in the smbSrvOpen by
  55. OpenWithCopyChunkIntent. For the read, if the copychunk info in the fcb is
  56. invalid, the read just fails and copychunk fails. Otherwise the issue is again
  57. just to get into the right context (UID/TID) so that the fid will be valid.
  58. --*/
  59. #include "precomp.h"
  60. #pragma hdrstop
  61. #pragma code_seg("PAGE")
  62. //there is some toplevel irp manipulation in here.......
  63. #ifdef RX_PRIVATE_BUILD
  64. #undef IoGetTopLevelIrp
  65. #undef IoSetTopLevelIrp
  66. #endif //ifdef RX_PRIVATE_BUILD
  67. extern DEBUG_TRACE_CONTROLPOINT RX_DEBUG_TRACE_MRXSMBCSC;
  68. #define Dbg (DEBUG_TRACE_MRXSMBCSC)
  69. LONG MRxSmbSpecialCopyChunkAllocationSizeMarker = (LONG)'\377csc';
  70. typedef union _SMBMRX_COPYCHUNKCONTEXT {
  71. COPYCHUNKCONTEXT;
  72. struct {
  73. ULONG spacer[3];
  74. PRX_CONTEXT RxContext;
  75. };
  76. } SMBMRX_COPYCHUNKCONTEXT, *PSMBMRX_COPYCHUNKCONTEXT;
  77. #define UNC_PREFIX_STRING L"\\??\\UNC"
  78. PWCHAR MRxSmbCscUncPrefixString = UNC_PREFIX_STRING;
  79. #ifdef RX_PRIVATE_BUILD
  80. #if 1
  81. BOOLEAN AllowAgentOpens = TRUE;
  82. #else
  83. BOOLEAN AllowAgentOpens = FALSE;
  84. #endif
  85. #else
  86. BOOLEAN AllowAgentOpens = TRUE;
  87. #endif //ifdef RX_PRIVATE_BUILD
  88. NTSTATUS
  89. MRxSmbCscIoctlOpenForCopyChunk (
  90. PRX_CONTEXT RxContext
  91. )
  92. /*++
  93. Routine Description:
  94. This routine performs a fileopen with copychunk intent.
  95. Arguments:
  96. RxContext - the RDBSS context. this contains a pointer to the bcs text
  97. giving the UNC filename and also the copychunk context where
  98. we store various things...including the underlying filehandle.
  99. Return Value:
  100. NTSTATUS - The return status for the operation
  101. Notes:
  102. --*/
  103. {
  104. NTSTATUS Status = STATUS_SUCCESS;
  105. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  106. PWCHAR FileName = (PWCHAR)LowIoContext->ParamsFor.IoCtl.pInputBuffer;
  107. ULONG FileNameLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  108. PSMBMRX_COPYCHUNKCONTEXT CopyChunkContext =
  109. (PSMBMRX_COPYCHUNKCONTEXT)(LowIoContext->ParamsFor.IoCtl.pOutputBuffer);
  110. ULONG UncPrefixLength = sizeof(UNC_PREFIX_STRING)-sizeof(WCHAR);
  111. UNICODE_STRING FileNameU,tmpU;
  112. PWCHAR pPrefixedName = NULL;
  113. OBJECT_ATTRIBUTES ObjectAttributes;
  114. IO_STATUS_BLOCK IoStatusBlock;
  115. ULONG Disposition,ShareAccess,CreateOptions;
  116. LARGE_INTEGER SpecialCopyChunkAllocationSize;
  117. C_ASSERT(sizeof(SMBMRX_COPYCHUNKCONTEXT) == sizeof(COPYCHUNKCONTEXT));
  118. RxDbgTrace(+1, Dbg, ("MRxSmbCscIoctlOpenForCopyChunk entry...%08lx %08lx %08lx %08lx\n",
  119. RxContext, FileName, FileNameLength, CopyChunkContext));
  120. CopyChunkContext->handle = INVALID_HANDLE_VALUE;
  121. IF_DEBUG {
  122. if (!AllowAgentOpens) {
  123. Status = (STATUS_INVALID_PARAMETER);
  124. goto FINALLY;
  125. }
  126. }
  127. // Bug 554655
  128. //Make sure that the filename is atleast 2 chars (\\) long
  129. if (FileName[FileNameLength/sizeof(WCHAR)]!= 0 ||
  130. FileNameLength/sizeof(WCHAR) < 2) {
  131. RxDbgTrace(0, Dbg, ("Bad Filename passed...%08lx %08lx\n",FileName,FileNameLength));
  132. Status = (STATUS_INVALID_PARAMETER);
  133. goto FINALLY;
  134. }
  135. // we allow multiple temporary agents (spp)
  136. // if (!IsSpecialApp()) {
  137. // DbgPrint(0, Dbg, ("CopyChunk operation in wrong thread!!!\n");
  138. // Status = (STATUS_INVALID_PARAMETER);
  139. // goto FINALLY;
  140. // }
  141. RxDbgTrace(0, Dbg, ("MRxSmbCscIoctlOpenForCopyChunk name...%08lx %s\n", RxContext, FileName));
  142. pPrefixedName = (PWCHAR)RxAllocatePoolWithTag(
  143. PagedPool,
  144. UncPrefixLength + FileNameLength, // one wchar extra
  145. MRXSMB_MISC_POOLTAG );
  146. if (pPrefixedName == NULL) {
  147. Status = STATUS_INSUFFICIENT_RESOURCES;
  148. goto FINALLY;
  149. }
  150. FileNameU.Buffer = pPrefixedName;
  151. RtlCopyMemory(pPrefixedName, MRxSmbCscUncPrefixString, UncPrefixLength);
  152. // copy the UNC name, step over the first back slash of the two leading ones
  153. RtlCopyMemory(&pPrefixedName[UncPrefixLength/sizeof(WCHAR)], &FileName[1], FileNameLength-sizeof(WCHAR));
  154. FileNameU.Length = FileNameU.MaximumLength = (USHORT)(UncPrefixLength + FileNameLength-sizeof(WCHAR));
  155. RxDbgTrace(0, Dbg, ("MRxSmbCscIoctlOpenForCopyChunk Uname...%08lx %wZ\n", RxContext, &FileNameU));
  156. InitializeObjectAttributes(
  157. &ObjectAttributes,
  158. &FileNameU,
  159. OBJ_CASE_INSENSITIVE,
  160. 0,
  161. NULL
  162. );
  163. SpecialCopyChunkAllocationSize.HighPart = MRxSmbSpecialCopyChunkAllocationSizeMarker;
  164. SpecialCopyChunkAllocationSize.LowPart = ((CopyChunkContext->dwFlags & COPYCHUNKCONTEXT_FLAG_IS_AGENT_OPEN)!=0);
  165. Disposition = FILE_OPEN;
  166. ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  167. CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT
  168. | FILE_NON_DIRECTORY_FILE;
  169. //CODE.IMPROVEMENT.ASHAMED
  170. //i am doing this as unbuffered ios.....the alternative is to do
  171. //buffered ios but the problem is that we must not have any
  172. //pagingIOs issued on these handles. so, the following would also
  173. //have to be done:
  174. // 1) no fastio on these...or fix fastio so it didn;t always wait
  175. // 2) always flush....we would always want to flush at the top of read
  176. // 3) no initialize cachemap...rather, use the FO in the segment-pointers
  177. // 4) no wait on cccopyread calls.
  178. //the effect of these improvements would be pretty big: you wouldn't have to go
  179. //back to the server for stuff already in the cache. but 'til then
  180. CreateOptions |= FILE_NO_INTERMEDIATE_BUFFERING;
  181. //CODE.IMPROVEMENT if we used IoCreateFile instead and IO_NO_PARAMETER
  182. // checking, then we could pass in a nonsensical value
  183. // and have even more foolproof way of describing a chunk
  184. // open
  185. Status = ZwCreateFile(
  186. &CopyChunkContext->handle, //OUT PHANDLE FileHandle,
  187. FILE_READ_ATTRIBUTES | SYNCHRONIZE, //IN ACCESS_MASK DesiredAccess,
  188. &ObjectAttributes, //IN POBJECT_ATTRIBUTES ObjectAttributes,
  189. &IoStatusBlock, //OUT PIO_STATUS_BLOCK IoStatusBlock,
  190. &SpecialCopyChunkAllocationSize, //IN PLARGE_INTEGER AllocationSize OPTIONAL,
  191. FILE_ATTRIBUTE_NORMAL, //IN ULONG FileAttributes,
  192. ShareAccess, //IN ULONG ShareAccess,
  193. Disposition, //IN ULONG CreateDisposition,
  194. CreateOptions, //IN ULONG CreateOptions,
  195. NULL, //IN PVOID EaBuffer OPTIONAL,
  196. 0 //IN ULONG EaLength,
  197. );
  198. IF_DEBUG {
  199. //this little snippett just allows me to test the closechunkopen logic
  200. if (FALSE) {
  201. HANDLE h;
  202. NTSTATUS TestOpenStatus;
  203. RxDbgTrace(0, Dbg, ("MRxSmbCscIoctlOpenForCopyChunk...f***open %08lx\n",
  204. RxContext));
  205. TestOpenStatus = ZwCreateFile(
  206. &h, //OUT PHANDLE FileHandle,
  207. GENERIC_READ | SYNCHRONIZE, //IN ACCESS_MASK DesiredAccess,
  208. &ObjectAttributes, //IN POBJECT_ATTRIBUTES ObjectAttributes,
  209. &IoStatusBlock, //OUT PIO_STATUS_BLOCK IoStatusBlock,
  210. NULL, //IN PLARGE_INTEGER AllocationSize OPTIONAL,
  211. FILE_ATTRIBUTE_NORMAL, //IN ULONG FileAttributes,
  212. ShareAccess, //IN ULONG ShareAccess,
  213. Disposition, //IN ULONG CreateDisposition,
  214. CreateOptions, //IN ULONG CreateOptions,
  215. NULL, //IN PVOID EaBuffer OPTIONAL,
  216. 0 //IN ULONG EaLength
  217. );
  218. RxDbgTrace(0, Dbg, ("MRxSmbCscIoctlOpenForCopyChunk...f***open %08lx teststs=%08lx %08lx\n",
  219. RxContext, TestOpenStatus, h));
  220. if (NT_SUCCESS(TestOpenStatus)) {
  221. NtClose(h);
  222. }
  223. }
  224. }
  225. FINALLY:
  226. if (pPrefixedName!=NULL) {
  227. RxFreePool(pPrefixedName);
  228. }
  229. RxDbgTrace(-1, Dbg, ("MRxSmbCscIoctlOpenForCopyChunk...%08lx %08lx %08lx\n",
  230. RxContext, Status, CopyChunkContext->handle));
  231. return(Status);
  232. }
  233. NTSTATUS
  234. MRxSmbCscIoctlCloseForCopyChunk (
  235. PRX_CONTEXT RxContext
  236. )
  237. /*++
  238. Routine Description:
  239. This routine performs the special IOCTL operation for the CSC agent.
  240. Arguments:
  241. RxContext - the RDBSS context which points to the copychunk context. this contains the
  242. underlying handle to close.
  243. Return Value:
  244. NTSTATUS - The return status for the operation
  245. Notes:
  246. --*/
  247. {
  248. NTSTATUS Status;
  249. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  250. PSMBMRX_COPYCHUNKCONTEXT CopyChunkContext =
  251. (PSMBMRX_COPYCHUNKCONTEXT)(LowIoContext->ParamsFor.IoCtl.pOutputBuffer);
  252. RxDbgTrace(+1, Dbg, ("MRxSmbCscIoctlCloseForCopyChunk...%08lx %08lx %08lx\n",
  253. RxContext, 0, CopyChunkContext));
  254. if (CopyChunkContext->handle != INVALID_HANDLE_VALUE) {
  255. Status = NtClose(CopyChunkContext->handle);
  256. } else {
  257. Status = STATUS_INVALID_PARAMETER;
  258. }
  259. //FINALLY:
  260. RxDbgTrace(-1, Dbg, ("MRxSmbCscIoctlCloseForCopyChunk...%08lx %08lx\n", RxContext, Status));
  261. return(Status);
  262. }
  263. //CODE.IMPROVEMENT.NTIFS had to get this from ntifs.h since we use ntsrv.h
  264. extern POBJECT_TYPE *IoFileObjectType;
  265. NTSTATUS
  266. MRxSmbCscIoctlCopyChunk (
  267. PRX_CONTEXT RxContext
  268. )
  269. /*++
  270. Routine Description:
  271. This routine performs the copychunk function.
  272. Arguments:
  273. RxContext - the RDBSS context
  274. Return Value:
  275. NTSTATUS - The return status for the operation
  276. Notes:
  277. what we do here is
  278. 1) get the filesize of the shadow...we use the handle stored in the
  279. agent's smbsrvopen......hence complicated synchronization.
  280. 2) allocate a read buffer COODE.IMPROVEMENT...should be done in the agent
  281. 3) issue the underlying read
  282. 4) write the acquired data to the file
  283. the putaway is done in the read tail. it must seem that we go to a lot of trouble
  284. to get the filesize using the underlying handle....actually, we could just get our
  285. handle. maybe, we should do that.
  286. also, it may seem that we should just rely on the underlying read to
  287. calculate where the chunk read should start. we do not do that because that
  288. would mean that we would have to bypass the cache! actually, we bypass it
  289. now anyway but later we may stop doing that. it's really, really bad to
  290. go back to the server for data that we have in cache. as well, the
  291. cachemanager/memorymanager can turn our small IOs into large Ios. so, we would
  292. need code in the minirdr read loop to keep the Ios down to the maximum chunk size.
  293. --*/
  294. {
  295. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  296. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  297. //PBYTE FileName = (PBYTE)LowIoContext->ParamsFor.IoCtl.pInputBuffer;
  298. //ULONG FileNameLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength - 1;
  299. PSMBMRX_COPYCHUNKCONTEXT CopyChunkContext =
  300. (PSMBMRX_COPYCHUNKCONTEXT)(LowIoContext->ParamsFor.IoCtl.pOutputBuffer);
  301. int iRet,ShadowFileLength;
  302. PFILE_OBJECT FileObject;
  303. BOOLEAN ObjectReferenceTaken = FALSE;
  304. BOOLEAN FcbAcquired = FALSE;
  305. BOOLEAN CriticalSectionEntered = FALSE;
  306. PMRX_FCB underlyingFcb;
  307. PMRX_FOBX underlyingFobx;
  308. PMRX_SRV_OPEN underlyingSrvOpen;
  309. PMRX_SMB_SRV_OPEN underlyingsmbSrvOpen;
  310. PVOID hfShadow;
  311. IO_STATUS_BLOCK IoStatusBlock;
  312. PBYTE Buffer = NULL;
  313. LARGE_INTEGER ReadOffset;
  314. int iAmountRead; //need this as int
  315. PIRP TopIrp;
  316. // all probing/validation is already on entry to mrxsmbcscioctl
  317. RxDbgTrace(+1, Dbg, ("MRxSmbCscIoctlCopyChunk...%08lx %08lx\n", RxContext,CopyChunkContext));
  318. //
  319. // we have to find out the size of the shadow file. we do this by going thru
  320. // the objectmanager. in this way, we do not require any extra state about
  321. // the ongoing copy....only the underlying handle. if we did not do this, we
  322. // would have to rely on whoever had the copychunk context to preserve it
  323. // correctly.
  324. //
  325. // Reference the file object to get the pointer.
  326. //
  327. Status = ObReferenceObjectByHandle( CopyChunkContext->handle,
  328. 0,
  329. *IoFileObjectType,
  330. RxContext->CurrentIrp->RequestorMode,
  331. (PVOID *) &FileObject,
  332. NULL );
  333. if (!NT_SUCCESS( Status )) {
  334. goto FINALLY;
  335. }
  336. ObjectReferenceTaken = TRUE;
  337. // keep the reference so the handle doesn't vanish from underneath us
  338. #if 0
  339. // make sure this handle belongs to us
  340. if (FileObject->DeviceObject != (PDEVICE_OBJECT)MRxSmbDeviceObject)
  341. {
  342. Status = STATUS_INVALID_PARAMETER;
  343. RxDbgTrace(0, Dbg, ("Invalid device object, not our handle \r\n"));
  344. goto FINALLY;
  345. }
  346. #endif
  347. underlyingFcb = (PMRX_FCB)(FileObject->FsContext);
  348. underlyingFobx = (PMRX_FOBX)(FileObject->FsContext2);
  349. if(NodeType(underlyingFcb) != RDBSS_NTC_STORAGE_TYPE_FILE)
  350. {
  351. Status = STATUS_INVALID_PARAMETER;
  352. RxDbgTrace(0, Dbg, ("Invalid storage type, handle is not for a file\r\n"));
  353. goto FINALLY;
  354. }
  355. Status = RxAcquireSharedFcbResourceInMRx( underlyingFcb );
  356. if (!NT_SUCCESS( Status )) {
  357. goto FINALLY;
  358. }
  359. FcbAcquired = TRUE;
  360. underlyingSrvOpen = underlyingFobx->pSrvOpen;
  361. underlyingsmbSrvOpen = MRxSmbGetSrvOpenExtension(underlyingSrvOpen);
  362. //if this is not a copychunk handle quit
  363. if (!FlagOn(underlyingsmbSrvOpen->Flags,SMB_SRVOPEN_FLAG_COPYCHUNK_OPEN)){
  364. Status = STATUS_INVALID_PARAMETER;
  365. RxDbgTrace(0, Dbg, ("not a copychunk handle\r\n"));
  366. goto FINALLY;
  367. }
  368. hfShadow = underlyingsmbSrvOpen->hfShadow;
  369. if (hfShadow==0) {
  370. Status = STATUS_UNSUCCESSFUL;
  371. RxDbgTrace(0, Dbg, ("Nt5CSC: no shadowhandle for copychunk\n"));
  372. goto FINALLY;
  373. }
  374. ASSERT_MINIRDRFILEOBJECT((PNT5CSC_MINIFILEOBJECT)hfShadow);
  375. EnterShadowCrit();
  376. CriticalSectionEntered = TRUE;
  377. //don't need the shadowreadwritemutex here because it's not really important
  378. //to have the correct endoffile value....worst case: an extra read flows....
  379. iRet = GetFileSizeLocal(hfShadow, &ShadowFileLength);
  380. RxDbgTrace( 0, Dbg,
  381. ("MRxSmbCscIoctlCopyChunk... %08lx (st=%08lx) fsize= %08lx\n",
  382. RxContext, iRet, ShadowFileLength));
  383. if (iRet <0) {
  384. Status = STATUS_UNSUCCESSFUL;
  385. goto FINALLY;
  386. }
  387. LeaveShadowCrit();
  388. CriticalSectionEntered = FALSE;
  389. RxReleaseFcbResourceInMRx( underlyingFcb );
  390. FcbAcquired = FALSE;
  391. ObDereferenceObject( FileObject );
  392. ObjectReferenceTaken = FALSE;
  393. Buffer = RxAllocatePoolWithTag(
  394. PagedPool,
  395. CopyChunkContext->ChunkSize,
  396. MRXSMB_MISC_POOLTAG );
  397. if (Buffer == NULL) {
  398. Status = STATUS_INSUFFICIENT_RESOURCES;
  399. goto FINALLY;
  400. }
  401. RxDbgTrace( 0, Dbg,
  402. ("MRxSmbCscIoctlCopyChunk... about to read %08lx %08lx\n",
  403. RxContext, Buffer));
  404. ReadOffset.QuadPart = ShadowFileLength;
  405. try {
  406. try {
  407. TopIrp = IoGetTopLevelIrp();
  408. IoSetTopLevelIrp(NULL); //tell the underlying guy he's all clear
  409. Status = ZwReadFile(
  410. CopyChunkContext->handle, //IN HANDLE FileHandle,
  411. 0, //IN HANDLE Event OPTIONAL,
  412. 0, //IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
  413. NULL, //IN PVOID ApcContext OPTIONAL,
  414. &IoStatusBlock, //OUT PIO_STATUS_BLOCK IoStatusBlock,
  415. Buffer, //OUT PVOID Buffer,
  416. CopyChunkContext->ChunkSize, //IN ULONG Length,
  417. &ReadOffset, //IN PLARGE_INTEGER ByteOffset OPTIONAL,
  418. NULL //IN PULONG Key OPTIONAL
  419. );
  420. } finally {
  421. IoSetTopLevelIrp(TopIrp); //restore my context for unwind
  422. }
  423. } except(EXCEPTION_EXECUTE_HANDLER) {
  424. Status = STATUS_UNSUCCESSFUL;
  425. }
  426. RxDbgTrace( 0, Dbg,
  427. ("MRxSmbCscIoctlCopyChunk... back from read %08lx %08lx %08lx\n",
  428. RxContext, Status, IoStatusBlock.Information));
  429. CopyChunkContext->LastAmountRead = 0;
  430. if (Status == STATUS_END_OF_FILE) {
  431. //we're cookin'...just map it
  432. Status = STATUS_SUCCESS;
  433. goto FINALLY;
  434. }
  435. if (!NT_SUCCESS(Status)) {
  436. goto FINALLY;
  437. }
  438. CopyChunkContext->LastAmountRead = (ULONG)IoStatusBlock.Information;
  439. CopyChunkContext->TotalSizeBeforeThisRead = ShadowFileLength;
  440. FINALLY:
  441. if (Buffer != NULL) {
  442. RxFreePool(Buffer);
  443. }
  444. if (CriticalSectionEntered) {
  445. LeaveShadowCrit();
  446. }
  447. if (FcbAcquired) {
  448. RxReleaseFcbResourceInMRx( underlyingFcb );
  449. }
  450. if (ObjectReferenceTaken) {
  451. ObDereferenceObject( FileObject );
  452. }
  453. RxDbgTrace(-1, Dbg, ("MRxSmbCscIoctlCopyChunk...%08lx %08lx\n", RxContext, Status));
  454. return(Status);
  455. }