Source code of Windows XP (NT5)
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.

1076 lines
30 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. cmwrapr.c
  5. Abstract:
  6. This module contains the source for wrapper routines called by the
  7. hive code, which in turn call the appropriate NT routines.
  8. Author:
  9. Bryan M. Willman (bryanwi) 16-Dec-1991
  10. Revision History:
  11. --*/
  12. #include "cmp.h"
  13. VOID
  14. CmpUnmapCmViewSurroundingOffset(
  15. IN PCMHIVE CmHive,
  16. IN ULONG FileOffset
  17. );
  18. #ifdef ALLOC_DATA_PRAGMA
  19. #pragma data_seg("PAGEDATA")
  20. #endif
  21. ULONG perftouchbuffer = 0;
  22. #ifdef ALLOC_PRAGMA
  23. #pragma alloc_text(PAGE,CmpAllocate)
  24. #ifdef POOL_TAGGING
  25. #pragma alloc_text(PAGE,CmpAllocateTag)
  26. #endif
  27. #pragma alloc_text(PAGE,CmpFree)
  28. #pragma alloc_text(PAGE,CmpDoFileSetSize)
  29. #pragma alloc_text(PAGE,CmpCreateEvent)
  30. #pragma alloc_text(PAGE,CmpFileRead)
  31. #pragma alloc_text(PAGE,CmpFileWrite)
  32. #pragma alloc_text(PAGE,CmpFileFlush)
  33. #pragma alloc_text(PAGE,CmpFileWriteThroughCache)
  34. #endif
  35. extern BOOLEAN CmpNoWrite;
  36. //
  37. // never read more than 64k, neither the filesystem nor some disk drivers
  38. // like it much.
  39. //
  40. #define MAX_FILE_IO 0x10000
  41. #define CmpIoFileRead 1
  42. #define CmpIoFileWrite 2
  43. #define CmpIoFileSetSize 3
  44. #define CmpIoFileFlush 4
  45. extern struct {
  46. ULONG Action;
  47. HANDLE Handle;
  48. NTSTATUS Status;
  49. } CmRegistryIODebug;
  50. extern BOOLEAN CmpFlushOnLockRelease;
  51. //
  52. // Storage management
  53. //
  54. PVOID
  55. CmpAllocate(
  56. ULONG Size,
  57. BOOLEAN UseForIo,
  58. ULONG Tag
  59. )
  60. /*++
  61. Routine Description:
  62. This routine makes more memory available to a hive.
  63. It is environment specific.
  64. Arguments:
  65. Size - amount of space caller wants
  66. UseForIo - TRUE if object allocated will be target of I/O,
  67. FALSE if not.
  68. Return Value:
  69. NULL if failure, address of allocated block if not.
  70. --*/
  71. {
  72. PVOID result;
  73. ULONG pooltype;
  74. #if DBG
  75. PVOID Caller;
  76. PVOID CallerCaller;
  77. RtlGetCallersAddress(&Caller, &CallerCaller);
  78. #endif
  79. if (CmpClaimGlobalQuota(Size) == FALSE) {
  80. return NULL;
  81. }
  82. pooltype = (UseForIo) ? PagedPoolCacheAligned : PagedPool;
  83. result = ExAllocatePoolWithTag(
  84. pooltype,
  85. Size,
  86. Tag
  87. );
  88. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"**CmpAllocate: allocate:%08lx, ", Size));
  89. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"type:%d, at:%08lx ", PagedPool, result));
  90. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"c:%p cc:%p\n", Caller, CallerCaller));
  91. if (result == NULL) {
  92. CmpReleaseGlobalQuota(Size);
  93. }
  94. return result;
  95. }
  96. #ifdef POOL_TAGGING
  97. PVOID
  98. CmpAllocateTag(
  99. ULONG Size,
  100. BOOLEAN UseForIo,
  101. ULONG Tag
  102. )
  103. /*++
  104. Routine Description:
  105. This routine makes more memory available to a hive.
  106. It is environment specific.
  107. Arguments:
  108. Size - amount of space caller wants
  109. UseForIo - TRUE if object allocated will be target of I/O,
  110. FALSE if not.
  111. Return Value:
  112. NULL if failure, address of allocated block if not.
  113. --*/
  114. {
  115. PVOID result;
  116. ULONG pooltype;
  117. #if DBG
  118. PVOID Caller;
  119. PVOID CallerCaller;
  120. RtlGetCallersAddress(&Caller, &CallerCaller);
  121. #endif
  122. if (CmpClaimGlobalQuota(Size) == FALSE) {
  123. return NULL;
  124. }
  125. pooltype = (UseForIo) ? PagedPoolCacheAligned : PagedPool;
  126. result = ExAllocatePoolWithTag(
  127. pooltype,
  128. Size,
  129. Tag
  130. );
  131. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"**CmpAllocate: allocate:%08lx, ", Size));
  132. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"type:%d, at:%08lx ", PagedPool, result));
  133. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"c:%p cc:%p\n", Caller, CallerCaller));
  134. if (result == NULL) {
  135. CmpReleaseGlobalQuota(Size);
  136. }
  137. return result;
  138. }
  139. #endif
  140. VOID
  141. CmpFree(
  142. PVOID MemoryBlock,
  143. ULONG GlobalQuotaSize
  144. )
  145. /*++
  146. Routine Description:
  147. This routine frees memory that has been allocated by the registry.
  148. It is environment specific
  149. Arguments:
  150. MemoryBlock - supplies address of memory object to free
  151. GlobalQuotaSize - amount of global quota to release
  152. Return Value:
  153. NONE
  154. --*/
  155. {
  156. #if DBG
  157. PVOID Caller;
  158. PVOID CallerCaller;
  159. RtlGetCallersAddress(&Caller, &CallerCaller);
  160. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"**FREEING:%08lx c,cc:%p,%p\n", MemoryBlock, Caller, CallerCaller));
  161. #endif
  162. ASSERT(GlobalQuotaSize > 0);
  163. CmpReleaseGlobalQuota(GlobalQuotaSize);
  164. ExFreePool(MemoryBlock);
  165. return;
  166. }
  167. NTSTATUS
  168. CmpDoFileSetSize(
  169. PHHIVE Hive,
  170. ULONG FileType,
  171. ULONG FileSize,
  172. ULONG OldFileSize
  173. )
  174. /*++
  175. Routine Description:
  176. This routine sets the size of a file. It must not return until
  177. the size is guaranteed.
  178. It is environment specific.
  179. Must be running in the context of the cmp worker thread.
  180. Arguments:
  181. Hive - Hive we are doing I/O for
  182. FileType - which supporting file to use
  183. FileSize - 32 bit value to set the file's size to
  184. OldFileSize - old file size, in order to determine if this is a shrink;
  185. - ignored if file type is not primary, or hive doesn't use
  186. the mapped views technique
  187. Return Value:
  188. FALSE if failure
  189. TRUE if success
  190. --*/
  191. {
  192. PCMHIVE CmHive;
  193. HANDLE FileHandle;
  194. NTSTATUS Status;
  195. FILE_END_OF_FILE_INFORMATION FileInfo;
  196. IO_STATUS_BLOCK IoStatus;
  197. BOOLEAN oldFlag;
  198. LARGE_INTEGER FileOffset; // where the mapping starts
  199. ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
  200. CmHive = (PCMHIVE)Hive;
  201. FileHandle = CmHive->FileHandles[FileType];
  202. if (FileHandle == NULL) {
  203. return TRUE;
  204. }
  205. //
  206. // disable hard error popups, to avoid self deadlock on bogus devices
  207. //
  208. oldFlag = IoSetThreadHardErrorMode(FALSE);
  209. FileInfo.EndOfFile.HighPart = 0L;
  210. if( FileType == HFILE_TYPE_PRIMARY ) {
  211. FileInfo.EndOfFile.LowPart = ROUND_UP(FileSize, CM_FILE_GROW_INCREMENT);
  212. } else {
  213. FileInfo.EndOfFile.LowPart = FileSize;
  214. }
  215. ASSERT_PASSIVE_LEVEL();
  216. Status = ZwSetInformationFile(
  217. FileHandle,
  218. &IoStatus,
  219. (PVOID)&FileInfo,
  220. sizeof(FILE_END_OF_FILE_INFORMATION),
  221. FileEndOfFileInformation
  222. );
  223. if (NT_SUCCESS(Status)) {
  224. ASSERT(IoStatus.Status == Status);
  225. } else {
  226. //
  227. // set debugging info
  228. //
  229. CmRegistryIODebug.Action = CmpIoFileSetSize;
  230. CmRegistryIODebug.Handle = FileHandle;
  231. CmRegistryIODebug.Status = Status;
  232. #ifndef _CM_LDR_
  233. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileSetSize:\tHandle=%08lx OldLength = %08lx NewLength=%08lx \n",
  234. FileHandle, OldFileSize, FileSize);
  235. #endif //_CM_LDR_
  236. if( (Status == STATUS_DISK_FULL) && ExIsResourceAcquiredExclusiveLite(&CmpRegistryLock) ) {
  237. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Disk is full while attempting to grow file %lx; will flush upon lock release\n",FileHandle);
  238. CmpFlushOnLockRelease = TRUE;;
  239. }
  240. }
  241. //
  242. // restore hard error popups mode
  243. //
  244. IoSetThreadHardErrorMode(oldFlag);
  245. //
  246. // purge
  247. //
  248. if( HiveWritesThroughCache(Hive,FileType) && (OldFileSize > FileSize)) {
  249. //
  250. // first we have to unmap any possible mapped views in the last 256K window
  251. // to avoid deadlock on CcWaitOnActiveCount inside CcPurgeCacheSection call bellow
  252. //
  253. ULONG Offset = FileSize & (~(_256K - 1));
  254. //
  255. // we are not allowed to shrink in shared mode.
  256. //
  257. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  258. while( Offset < OldFileSize ) {
  259. CmpUnmapCmViewSurroundingOffset((PCMHIVE)Hive,Offset);
  260. Offset += CM_VIEW_SIZE;
  261. }
  262. //
  263. // we need to take extra precaution here and unmap the very last view too
  264. //
  265. //CmpUnmapCmViewSurroundingOffset((PCMHIVE)Hive,OldFileSize-HBLOCK_SIZE);
  266. FileOffset.HighPart = 0;
  267. FileOffset.LowPart = FileSize;
  268. //
  269. // This is a shrink; Inform cache manager of the change of the size
  270. //
  271. CcPurgeCacheSection( ((PCMHIVE)Hive)->FileObject->SectionObjectPointer, (PLARGE_INTEGER)(((ULONG_PTR)(&FileOffset)) + 1),
  272. OldFileSize - FileSize, FALSE );
  273. //
  274. // Flush out this view to clear out the Cc dirty hints
  275. //
  276. CcFlushCache( ((PCMHIVE)Hive)->FileObject->SectionObjectPointer, (PLARGE_INTEGER)(((ULONG_PTR)(&FileOffset)) + 1),/*we are private writers*/
  277. OldFileSize - FileSize,NULL);
  278. }
  279. return Status;
  280. }
  281. NTSTATUS
  282. CmpCreateEvent(
  283. IN EVENT_TYPE eventType,
  284. OUT PHANDLE eventHandle,
  285. OUT PKEVENT *event
  286. )
  287. {
  288. NTSTATUS status;
  289. OBJECT_ATTRIBUTES obja;
  290. InitializeObjectAttributes( &obja, NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL );
  291. status = ZwCreateEvent(
  292. eventHandle,
  293. EVENT_ALL_ACCESS,
  294. &obja,
  295. eventType,
  296. FALSE);
  297. if (!NT_SUCCESS(status)) {
  298. return status;
  299. }
  300. status = ObReferenceObjectByHandle(
  301. *eventHandle,
  302. EVENT_ALL_ACCESS,
  303. NULL,
  304. KernelMode,
  305. event,
  306. NULL);
  307. if (!NT_SUCCESS(status)) {
  308. ZwClose(*eventHandle);
  309. return status;
  310. }
  311. return status;
  312. }
  313. BOOLEAN
  314. CmpFileRead (
  315. PHHIVE Hive,
  316. ULONG FileType,
  317. PULONG FileOffset,
  318. PVOID DataBuffer,
  319. ULONG DataLength
  320. )
  321. /*++
  322. Routine Description:
  323. This routine reads in a buffer from a file.
  324. It is environment specific.
  325. NOTE: We assume the handle is opened for asynchronous access,
  326. and that we, and not the IO system, are keeping the
  327. offset pointer.
  328. NOTE: Only 32bit offsets are supported, even though the underlying
  329. IO system on NT supports 64 bit offsets.
  330. Arguments:
  331. Hive - Hive we are doing I/O for
  332. FileType - which supporting file to use
  333. FileOffset - pointer to variable providing 32bit offset on input,
  334. and receiving new 32bit offset on output.
  335. DataBuffer - pointer to buffer
  336. DataLength - length of buffer
  337. Return Value:
  338. FALSE if failure
  339. TRUE if success
  340. --*/
  341. {
  342. NTSTATUS status;
  343. LARGE_INTEGER Offset;
  344. IO_STATUS_BLOCK IoStatus;
  345. PCMHIVE CmHive;
  346. HANDLE FileHandle;
  347. ULONG LengthToRead;
  348. HANDLE eventHandle = NULL;
  349. PKEVENT eventObject = NULL;
  350. ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
  351. CmHive = (PCMHIVE)Hive;
  352. FileHandle = CmHive->FileHandles[FileType];
  353. if (FileHandle == NULL) {
  354. return TRUE;
  355. }
  356. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileRead:\n"));
  357. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"\tHandle=%08lx Offset=%08lx ", FileHandle, *FileOffset));
  358. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"Buffer=%p Length=%08lx\n", DataBuffer, DataLength));
  359. //
  360. // Detect attempt to read off end of 2gig file (this should be irrelevent)
  361. //
  362. if ((0xffffffff - *FileOffset) < DataLength) {
  363. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileRead: runoff\n"));
  364. return FALSE;
  365. }
  366. status = CmpCreateEvent(
  367. SynchronizationEvent,
  368. &eventHandle,
  369. &eventObject);
  370. if (!NT_SUCCESS(status))
  371. return FALSE;
  372. //
  373. // We'd really like to just call the filesystems and have them do
  374. // the right thing. But the filesystem will attempt to lock our
  375. // entire buffer into memory, and that may fail for large requests.
  376. // So we split our reads into 64k chunks and call the filesystem for
  377. // each one.
  378. //
  379. ASSERT_PASSIVE_LEVEL();
  380. while (DataLength > 0) {
  381. //
  382. // Convert ULONG to Large
  383. //
  384. Offset.LowPart = *FileOffset;
  385. Offset.HighPart = 0L;
  386. //
  387. // trim request down if necessary.
  388. //
  389. if (DataLength > MAX_FILE_IO) {
  390. LengthToRead = MAX_FILE_IO;
  391. } else {
  392. LengthToRead = DataLength;
  393. }
  394. status = ZwReadFile(
  395. FileHandle,
  396. eventHandle,
  397. NULL, // apcroutine
  398. NULL, // apccontext
  399. &IoStatus,
  400. DataBuffer,
  401. LengthToRead,
  402. &Offset,
  403. NULL // key
  404. );
  405. if (STATUS_PENDING == status) {
  406. status = KeWaitForSingleObject(eventObject, Executive,
  407. KernelMode, FALSE, NULL);
  408. ASSERT(STATUS_SUCCESS == status);
  409. status = IoStatus.Status;
  410. }
  411. //
  412. // adjust offsets
  413. //
  414. *FileOffset = Offset.LowPart + LengthToRead;
  415. DataLength -= LengthToRead;
  416. (PUCHAR)DataBuffer += LengthToRead;
  417. if (NT_SUCCESS(status)) {
  418. ASSERT(IoStatus.Status == status);
  419. if (IoStatus.Information != LengthToRead) {
  420. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileRead:\n\t"));
  421. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"Failure1: status = %08lx ", status));
  422. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"IoInformation = %08lx\n", IoStatus.Information));
  423. ObDereferenceObject(eventObject);
  424. ZwClose(eventHandle);
  425. return FALSE;
  426. }
  427. } else {
  428. //
  429. // set debugging info
  430. //
  431. CmRegistryIODebug.Action = CmpIoFileRead;
  432. CmRegistryIODebug.Handle = FileHandle;
  433. CmRegistryIODebug.Status = status;
  434. #ifndef _CM_LDR_
  435. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileRead:\tFailure2: status = %08lx IoStatus = %08lx\n", status, IoStatus.Status);
  436. #endif //_CM_LDR_
  437. ObDereferenceObject(eventObject);
  438. ZwClose(eventHandle);
  439. return FALSE;
  440. }
  441. }
  442. ObDereferenceObject(eventObject);
  443. ZwClose(eventHandle);
  444. return TRUE;
  445. }
  446. BOOLEAN
  447. CmpFileWriteThroughCache(
  448. PHHIVE Hive,
  449. ULONG FileType,
  450. PCMP_OFFSET_ARRAY offsetArray,
  451. ULONG offsetArrayCount
  452. )
  453. /*++
  454. Routine Description:
  455. This is routine writes dirty ranges of data using Cc mapped views.
  456. The benefit is that writes don't go through Cc Lazy Writer, so there
  457. is no danger to be throttled or deffered.
  458. It also flushes the cache for the written ranges, guaranteeing that
  459. the data was commited to the disk upon return.
  460. Arguments:
  461. Hive - Hive we are doing I/O for
  462. FileType - which supporting file to use
  463. offsetArray - array of structures where each structure holds a 32bit offset
  464. into the Hive file and pointer the a buffer written to that
  465. file offset.
  466. offsetArrayCount - number of elements in the offsetArray.
  467. Return Value:
  468. FALSE if failure
  469. TRUE if success
  470. Note:
  471. This routine is intended to deal only with paged bins (i.e. bins crossing the
  472. CM_VIEW_SIZE boundary or bins that were added after the last sync)
  473. Assumption:
  474. We work on the assumption that the data to be written at one iteration never spans
  475. over the CM_VIEW_SIZE boundary. HvpFindNextDirtyBlock takes care of that !!!
  476. --*/
  477. {
  478. ULONG i;
  479. PVOID DataBuffer;
  480. ULONG DataLength;
  481. ULONG FileOffset;
  482. PCMHIVE CmHive;
  483. NTSTATUS Status;
  484. PVOID Bcb;
  485. PVOID FileBuffer;
  486. LARGE_INTEGER Offset;
  487. IO_STATUS_BLOCK IoStatus;
  488. ASSERT_PASSIVE_LEVEL();
  489. CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
  490. ASSERT( ((FileType == HFILE_TYPE_EXTERNAL) && (CmHive->FileObject != NULL)) || HiveWritesThroughCache(Hive,FileType) );
  491. //ASSERT( IsListEmpty(&(CmHive->PinViewListHead)) == TRUE);
  492. //ASSERT( CmHive->PinnedViews == 0 );
  493. Offset.HighPart = 0;
  494. //
  495. // iterate through the array of data
  496. //
  497. for(i=0;i<offsetArrayCount;i++) {
  498. DataBuffer = offsetArray[i].DataBuffer;
  499. DataLength = offsetArray[i].DataLength;
  500. FileOffset = offsetArray[i].FileOffset;
  501. //
  502. // data should never span over CM_VIEW_SIZE boundary
  503. //
  504. ASSERT( (FileOffset & (~(CM_VIEW_SIZE - 1))) == ((FileOffset + DataLength - 1) & (~(CM_VIEW_SIZE - 1))) );
  505. //
  506. // unmap any possible mapped view that could overlapp with this range ; not needed !!!!
  507. //
  508. //CmpUnmapCmViewSurroundingOffset(CmHive,FileOffset);
  509. //
  510. // map and pin data
  511. //
  512. Offset.LowPart = FileOffset;
  513. try {
  514. if( !CcPinRead (CmHive->FileObject,&Offset,DataLength,PIN_WAIT,&Bcb,&FileBuffer) ) {
  515. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWriteThroughCache - could not pin read view i= %lu\n",i));
  516. #if DBG
  517. DbgBreakPoint();
  518. #endif //DBG
  519. return FALSE;
  520. }
  521. //
  522. // copy data to pinned view; we need to do it inside try except, to protect against devices/volumes
  523. // dismounting from under us.
  524. //
  525. RtlCopyMemory(FileBuffer,DataBuffer, DataLength);
  526. } except (EXCEPTION_EXECUTE_HANDLER) {
  527. //
  528. // in low-memory scenarios, CcPinRead throws a STATUS_INSUFFICIENT_RESOURCES
  529. // We want to catch this and treat as a "not enough resources" problem,
  530. // rather than letting it to surface the kernel call
  531. //
  532. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWriteThroughCache : CcPinRead has raised :%08lx\n",GetExceptionCode()));
  533. return FALSE;
  534. }
  535. //
  536. // dirty, unpin and flush
  537. //
  538. CcSetDirtyPinnedData (Bcb,NULL);
  539. CcUnpinData( Bcb );
  540. CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)(&Offset)) + 1)/*we are private writers*/,DataLength,&IoStatus);
  541. if(!NT_SUCCESS(IoStatus.Status) ) {
  542. return FALSE;
  543. }
  544. }
  545. return TRUE;
  546. }
  547. FAST_MUTEX CmpWriteLock; // used to syncronize access to the bellow;
  548. // the only case we ned this is when NtSaveKey is called by different threads
  549. // at the same time; all other calls to CmpFileWrite are made with the reg_lock
  550. // held exclusively
  551. CM_WRITE_BLOCK CmpWriteBlock;
  552. BOOLEAN
  553. CmpFileWrite(
  554. PHHIVE Hive,
  555. ULONG FileType,
  556. PCMP_OFFSET_ARRAY offsetArray,
  557. ULONG offsetArrayCount,
  558. PULONG FileOffset
  559. )
  560. /*++
  561. Routine Description:
  562. This routine writes an array of buffers out to a file.
  563. It is environment specific.
  564. NOTE: We assume the handle is opened for asynchronous access,
  565. and that we, and not the IO system, are keeping the
  566. offset pointer.
  567. NOTE: Only 32bit offsets are supported, even though the underlying
  568. IO system on NT supports 64 bit offsets.
  569. Arguments:
  570. Hive - Hive we are doing I/O for
  571. FileType - which supporting file to use
  572. offsetArray - array of structures where each structure holds a 32bit offset
  573. into the Hive file and pointer the a buffer written to that
  574. file offset.
  575. offsetArrayCount - number of elements in the offsetArray.
  576. FileOffset - returns the file offset after the last write to the file.
  577. Return Value:
  578. FALSE if failure
  579. TRUE if success
  580. --*/
  581. {
  582. NTSTATUS status;
  583. LARGE_INTEGER Offset;
  584. PCMHIVE CmHive;
  585. HANDLE FileHandle;
  586. ULONG LengthToWrite;
  587. LONG WaitBufferCount = 0;
  588. LONG idx;
  589. ULONG arrayCount = 0;
  590. PVOID DataBuffer;
  591. ULONG DataLength;
  592. BOOLEAN ret_val = TRUE;
  593. if (CmpNoWrite) {
  594. return TRUE;
  595. }
  596. ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
  597. CmHive = (PCMHIVE)Hive;
  598. FileHandle = CmHive->FileHandles[FileType];
  599. if (FileHandle == NULL) {
  600. return TRUE;
  601. }
  602. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileWrite:\n"));
  603. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"\tHandle=%08lx ", FileHandle));
  604. //ASSERT( !HiveWritesThroughCache(Hive,FileType) );
  605. ExAcquireFastMutexUnsafe(&CmpWriteLock);
  606. for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; idx++) {
  607. CmpWriteBlock.EventHandles[idx] = NULL;
  608. #if DBG
  609. CmpWriteBlock.EventObjects[idx] = NULL;
  610. #endif
  611. }
  612. //
  613. // decide whether we wait for IOs to complete or just issue them and
  614. // rely on the CcFlushCache to do the job
  615. //
  616. // Bring pages being written into memory first to allow disk to write
  617. // buffer contiguously.
  618. for (idx = 0; (ULONG) idx < offsetArrayCount; idx++) {
  619. char * start = offsetArray[idx].DataBuffer;
  620. char * end = (char *) start + offsetArray[idx].DataLength;
  621. while (start < end) {
  622. // perftouchbuffer globally declared so that compiler won't try
  623. // to remove it and this loop (if its smart enough?).
  624. perftouchbuffer += (ULONG) *start;
  625. start += PAGE_SIZE;
  626. }
  627. }
  628. //
  629. // We'd really like to just call the filesystems and have them do
  630. // the right thing. But the filesystem will attempt to lock our
  631. // entire buffer into memory, and that may fail for large requests.
  632. // So we split our reads into 64k chunks and call the filesystem for
  633. // each one.
  634. //
  635. ASSERT_PASSIVE_LEVEL();
  636. arrayCount = 0;
  637. DataLength = 0;
  638. // This outer loop is hit more than once if the MAXIMUM_WAIT_OBJECTS limit
  639. // is hit before the offset array is drained.
  640. while (arrayCount < offsetArrayCount) {
  641. WaitBufferCount = 0;
  642. // This loop fills the wait buffer.
  643. while ((arrayCount < offsetArrayCount) &&
  644. (WaitBufferCount < MAXIMUM_WAIT_OBJECTS)) {
  645. // If data length isn't zero than the wait buffer filled before the
  646. // buffer in the last offsetArray element was sent to write file.
  647. if (DataLength == 0) {
  648. *FileOffset = offsetArray[arrayCount].FileOffset;
  649. DataBuffer = offsetArray[arrayCount].DataBuffer;
  650. DataLength = offsetArray[arrayCount].DataLength;
  651. //
  652. // Detect attempt to read off end of 2gig file
  653. // (this should be irrelevent)
  654. //
  655. if ((0xffffffff - *FileOffset) < DataLength) {
  656. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileWrite: runoff\n"));
  657. goto Error_Exit;
  658. }
  659. }
  660. // else still more to write out of last buffer.
  661. while ((DataLength > 0) && (WaitBufferCount < MAXIMUM_WAIT_OBJECTS)) {
  662. //
  663. // Convert ULONG to Large
  664. //
  665. Offset.LowPart = *FileOffset;
  666. Offset.HighPart = 0L;
  667. //
  668. // trim request down if necessary.
  669. //
  670. if (DataLength > MAX_FILE_IO) {
  671. LengthToWrite = MAX_FILE_IO;
  672. } else {
  673. LengthToWrite = DataLength;
  674. }
  675. // Previously created events are reused.
  676. if (CmpWriteBlock.EventHandles[WaitBufferCount] == NULL) {
  677. status = CmpCreateEvent(SynchronizationEvent,
  678. &(CmpWriteBlock.EventHandles[WaitBufferCount]),
  679. &(CmpWriteBlock.EventObjects[WaitBufferCount]));
  680. if (!NT_SUCCESS(status)) {
  681. // Make sure we don't try to clean this up.
  682. CmpWriteBlock.EventHandles[WaitBufferCount] = NULL;
  683. goto Error_Exit;
  684. }
  685. }
  686. status = ZwWriteFile(FileHandle,
  687. CmpWriteBlock.EventHandles[WaitBufferCount],
  688. NULL, // apcroutine
  689. NULL, // apccontext
  690. &(CmpWriteBlock.IoStatus[WaitBufferCount]),
  691. DataBuffer,
  692. LengthToWrite,
  693. &Offset,
  694. NULL);
  695. if (!NT_SUCCESS(status)) {
  696. goto Error_Exit;
  697. }
  698. WaitBufferCount++;
  699. //
  700. // adjust offsets
  701. //
  702. *FileOffset = Offset.LowPart + LengthToWrite;
  703. DataLength -= LengthToWrite;
  704. (PUCHAR)DataBuffer += LengthToWrite;
  705. } // while (DataLength > 0 && WaitBufferCount < MAXIMUM_WAIT_OBJECTS)
  706. arrayCount++;
  707. } // while (arrayCount < offsetArrayCount &&
  708. // WaitBufferCount < MAXIMUM_WAIT_OBJECTS)
  709. status = KeWaitForMultipleObjects(WaitBufferCount,
  710. CmpWriteBlock.EventObjects,
  711. WaitAll,
  712. Executive,
  713. KernelMode,
  714. FALSE,
  715. NULL,
  716. CmpWriteBlock.WaitBlockArray);
  717. if (!NT_SUCCESS(status))
  718. goto Error_Exit;
  719. for (idx = 0; idx < WaitBufferCount; idx++) {
  720. if (!NT_SUCCESS(CmpWriteBlock.IoStatus[idx].Status)) {
  721. status = CmpWriteBlock.IoStatus[idx].Status;
  722. ret_val = FALSE;
  723. goto Done;
  724. }
  725. }
  726. // There may still be more to do if the last element held a big buffer
  727. // and the wait buffer filled before it was all sent to the file.
  728. if (DataLength > 0) {
  729. arrayCount--;
  730. }
  731. } // while (arrayCount < offsetArrayCount)
  732. ret_val = TRUE;
  733. goto Done;
  734. Error_Exit:
  735. //
  736. // set debugging info
  737. //
  738. CmRegistryIODebug.Action = CmpIoFileWrite;
  739. CmRegistryIODebug.Handle = FileHandle;
  740. CmRegistryIODebug.Status = status;
  741. #ifndef _CM_LDR_
  742. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileWrite: error exiting %d\n", status);
  743. #endif //_CM_LDR_
  744. //
  745. // if WaitBufferCount > 0 then we have successfully issued
  746. // some I/Os, but not all of them. This is an error, but we
  747. // cannot return from this routine until all the successfully
  748. // issued I/Os have completed.
  749. //
  750. if (WaitBufferCount > 0) {
  751. //
  752. // only if we decided that we want to wait for the write to complete
  753. // (log files and hives not using the mapped views technique)
  754. //
  755. status = KeWaitForMultipleObjects(WaitBufferCount,
  756. CmpWriteBlock.EventObjects,
  757. WaitAll,
  758. Executive,
  759. KernelMode,
  760. FALSE,
  761. NULL,
  762. CmpWriteBlock.WaitBlockArray);
  763. }
  764. ret_val = FALSE;
  765. Done:
  766. idx = 0;
  767. // Clean up open event handles and objects.
  768. while ((idx < MAXIMUM_WAIT_OBJECTS) && (CmpWriteBlock.EventHandles[idx] != NULL)) {
  769. ASSERT( CmpWriteBlock.EventObjects[idx] );
  770. ObDereferenceObject(CmpWriteBlock.EventObjects[idx]);
  771. ZwClose(CmpWriteBlock.EventHandles[idx]);
  772. idx++;
  773. }
  774. ExReleaseFastMutexUnsafe(&CmpWriteLock);
  775. return ret_val;
  776. }
  777. BOOLEAN
  778. CmpFileFlush (
  779. PHHIVE Hive,
  780. ULONG FileType,
  781. PLARGE_INTEGER FileOffset,
  782. ULONG Length
  783. )
  784. /*++
  785. Routine Description:
  786. This routine performs a flush on a file handle.
  787. Arguments:
  788. Hive - Hive we are doing I/O for
  789. FileType - which supporting file to use
  790. FileOffset - If this parameter is supplied (not NULL), then only the
  791. byte range specified by FileOffset and Length are flushed.
  792. Length - Defines the length of the byte range to flush, starting at
  793. FileOffset. This parameter is ignored if FileOffset is
  794. specified as NULL.
  795. Return Value:
  796. FALSE if failure
  797. TRUE if success
  798. Note:
  799. FileOffset and Length are only taken into account when FileType == HFILE_TYPE_PRIMARY
  800. and the hive uses the mapped-views method.
  801. --*/
  802. {
  803. NTSTATUS status;
  804. IO_STATUS_BLOCK IoStatus;
  805. PCMHIVE CmHive;
  806. HANDLE FileHandle;
  807. ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
  808. CmHive = (PCMHIVE)Hive;
  809. FileHandle = CmHive->FileHandles[FileType];
  810. if (FileHandle == NULL) {
  811. return TRUE;
  812. }
  813. if (CmpNoWrite) {
  814. return TRUE;
  815. }
  816. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileFlush:\n\tHandle = %08lx\n", FileHandle));
  817. ASSERT_PASSIVE_LEVEL();
  818. if( HiveWritesThroughCache(Hive,FileType) == TRUE ) {
  819. //
  820. // OK, we need to flush using CcFlushCache
  821. //
  822. CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)((ULONG_PTR)FileOffset + 1)/*we are private writers*/,Length,&IoStatus);
  823. status = IoStatus.Status;
  824. } else {
  825. //
  826. // hive loaded into paged pool; or not primary
  827. //
  828. status = ZwFlushBuffersFile(
  829. FileHandle,
  830. &IoStatus
  831. );
  832. }
  833. if (NT_SUCCESS(status)) {
  834. ASSERT(IoStatus.Status == status);
  835. return TRUE;
  836. } else {
  837. //
  838. // set debugging info
  839. //
  840. CmRegistryIODebug.Action = CmpIoFileFlush;
  841. CmRegistryIODebug.Handle = FileHandle;
  842. CmRegistryIODebug.Status = status;
  843. #ifndef _CM_LDR_
  844. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileFlush:\tFailure1: status = %08lx IoStatus = %08lx\n",status,IoStatus.Status);
  845. #endif //_CM_LDR_
  846. #ifdef DRAGOSS_PRIVATE_DEBUG
  847. DbgBreakPoint();
  848. #endif //DRAGOSS_PRIVATE_DEBUG
  849. return FALSE;
  850. }
  851. return TRUE;
  852. }