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.

1113 lines
31 KiB

  1. #include "compdir.h"
  2. #define InitializeListHead(ListHead) (\
  3. (ListHead)->Flink = (ListHead)->Blink = (ListHead) )
  4. #define IsListEmpty(ListHead) (\
  5. ( ((ListHead)->Flink == (ListHead)) ? TRUE : FALSE ) )
  6. #define RemoveHeadList(ListHead) \
  7. (ListHead)->Flink;\
  8. {\
  9. PLIST_ENTRY FirstEntry;\
  10. FirstEntry = (ListHead)->Flink;\
  11. FirstEntry->Flink->Blink = (ListHead);\
  12. (ListHead)->Flink = FirstEntry->Flink;\
  13. }
  14. #define InsertTailList(ListHead,Entry) \
  15. (Entry)->Flink = (ListHead);\
  16. (Entry)->Blink = (ListHead)->Blink;\
  17. (ListHead)->Blink->Flink = (Entry);\
  18. (ListHead)->Blink = (Entry)
  19. #define ARGUMENT_PRESENT( ArgumentPointer ) (\
  20. (LPSTR)(ArgumentPointer) != (LPSTR)(NULL) )
  21. #define ROUND_UP( Size, Amount ) (((Size) + ((Amount) - 1)) & ~((Amount) - 1))
  22. VOID
  23. ProcessRequest(
  24. IN PWORK_QUEUE_ITEM WorkItem
  25. )
  26. /*++
  27. Routine Description:
  28. This function is called whenever a work item is removed from
  29. the work queue by one of the worker threads. Which worker
  30. thread context this function is called in is arbitrary.
  31. This functions keeps a pointer to state information in
  32. thread local storage.
  33. This function is called once at the beginning with a
  34. special initialization call. During this call, this
  35. function allocates space for state information and
  36. remembers the pointer to the state information in
  37. a Thread Local Storage (TLS) slot.
  38. This function is called once at the end with a special
  39. termination call. During this call, this function
  40. frees the state information allocated during the
  41. initialization call.
  42. In between these two calls are zero or more calls to
  43. handle a work item. The work item is a copy request
  44. which is handled by the ProcessCopyFile function.
  45. Arguments:
  46. WorkItem - Supplies a pointer to the work item just removed
  47. from the work queue. It is the responsibility of this
  48. routine to free the memory used to hold the work item.
  49. Return Value:
  50. None.
  51. --*/
  52. {
  53. DWORD BytesWritten;
  54. PCOPY_REQUEST_STATE State;
  55. PCOPY_REQUEST CopyRequest;
  56. CHAR MessageBuffer[ 2 * MAX_PATH ];
  57. if (WorkItem->Reason == WORK_INITIALIZE_ITEM) {
  58. //
  59. // First time initialization call. Allocate space for
  60. // state information.
  61. //
  62. State = LocalAlloc( LMEM_ZEROINIT,
  63. sizeof( *State )
  64. );
  65. if (State != NULL) {
  66. //
  67. // Now create a virtual buffer, with an initial commitment
  68. // of zero and a maximum commitment of 128KB. This buffer
  69. // will be used to accumulate the output during the copy
  70. // operation. This is so the output can be written to
  71. // standard output with a single write call, thus insuring
  72. // that it remains contiguous in the output stream, and is
  73. // not intermingled with the output of the other worker threads.
  74. //
  75. if (CreateVirtualBuffer( &State->Buffer, 0, 2 * 64 * 1024 )) {
  76. //
  77. // The CurrentOutput field of the state block is
  78. // a pointer to where the next output goes in the
  79. // buffer. It is initialized here and reset each
  80. // time the buffer is flushed to standard output.
  81. //
  82. State->CurrentOutput = State->Buffer.Base;
  83. }
  84. else {
  85. LocalFree( State );
  86. State = NULL;
  87. }
  88. }
  89. //
  90. // Remember the pointer to the state informaiton
  91. // thread local storage.
  92. //
  93. TlsSetValue( TlsIndex, State );
  94. return;
  95. }
  96. //
  97. // Here to handle a work item or special terminate call.
  98. // Get the state pointer from thread local storage.
  99. //
  100. State = (PCOPY_REQUEST_STATE)TlsGetValue( TlsIndex );
  101. if (State == NULL) {
  102. return;
  103. }
  104. //
  105. // If this is the special terminate work item, free the virtual
  106. // buffer and state block allocated above and set the thread
  107. // local storage value to NULL. Return to caller.
  108. //
  109. if (WorkItem->Reason == WORK_TERMINATE_ITEM) {
  110. FreeVirtualBuffer( &State->Buffer );
  111. LocalFree( State );
  112. TlsSetValue( TlsIndex, NULL );
  113. return;
  114. }
  115. //
  116. // If not an initialize or terminate work item, then must be a
  117. // copy request. Calculate the address of the copy request
  118. // block, based on the position of the WorkItem field in the
  119. // COPY_REQUEST structure.
  120. //
  121. CopyRequest = CONTAINING_RECORD( WorkItem, COPY_REQUEST, WorkItem );
  122. //
  123. // Actual copy operation is protected by a try ... except
  124. // block so that any attempts to store into the virtual buffer
  125. // will be handled correctly by extending the virtual buffer.
  126. //
  127. _try {
  128. //
  129. // Perform the copy
  130. //
  131. ProcessCopyFile( CopyRequest, State );
  132. //
  133. // If any output was written to the virtual buffer,
  134. // flush the output to standard output. Trim the
  135. // virtual buffer back to zero committed pages.
  136. //
  137. if (State->CurrentOutput > (LPSTR)State->Buffer.Base) {
  138. WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
  139. State->Buffer.Base,
  140. (DWORD)(State->CurrentOutput - (LPSTR)State->Buffer.Base),
  141. &BytesWritten,
  142. NULL
  143. );
  144. TrimVirtualBuffer( &State->Buffer );
  145. State->CurrentOutput = (LPSTR)State->Buffer.Base;
  146. }
  147. }
  148. _except( VirtualBufferExceptionFilter( GetExceptionCode(),
  149. GetExceptionInformation(),
  150. &State->Buffer
  151. )
  152. ) {
  153. //
  154. // We will get here if the exception filter was unable to
  155. // commit the memory.
  156. //
  157. WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
  158. MessageBuffer,
  159. sprintf( MessageBuffer, "can't commit memory\n" ),
  160. &BytesWritten,
  161. NULL
  162. );
  163. }
  164. //
  165. // Free the storage used by the CopyRequest
  166. //
  167. LocalFree( CopyRequest );
  168. //
  169. // All done with this request. Return to the worker thread that
  170. // called us.
  171. //
  172. return;
  173. }
  174. VOID
  175. ProcessCopyFile(
  176. IN PCOPY_REQUEST CopyRequest,
  177. IN PCOPY_REQUEST_STATE State
  178. )
  179. /*++
  180. Routine Description:
  181. This function performs the actual copy of the contents of the
  182. passed file for the copy string given on the command line.
  183. If we are using synchronous I/O, then do the read operation
  184. now.
  185. Copy the contents of the file for any matches, and accumulate
  186. the match output in the virtual buffer using sprintf, which is
  187. multi-thread safe, even with the single threaded version of
  188. the libraries.
  189. Arguments:
  190. CopyRequest - Supplies a pointer to the copy request which
  191. contains the relevant information.
  192. State - Supplies a pointer to state information for the current
  193. thread.
  194. Return Value:
  195. None.
  196. --*/
  197. {
  198. LPSTR FullPathSrc, Destination;
  199. BOOL pend, CanDetectFreeSpace = TRUE;
  200. int i;
  201. DWORD sizeround;
  202. DWORD BytesPerCluster;
  203. ATTRIBUTE_TYPE Attributes;
  204. int LastErrorGot;
  205. __int64 freespac;
  206. char root[5] = {'a',':','\\','\0'};
  207. DWORD cSecsPerClus, cBytesPerSec, cFreeClus, cTotalClus;
  208. Destination = CopyRequest->Destination;
  209. FullPathSrc = CopyRequest->FullPathSrc;
  210. root[0] = *Destination;
  211. if( !GetDiskFreeSpace( root, &cSecsPerClus, &cBytesPerSec, &cFreeClus, &cTotalClus ) ) {
  212. CanDetectFreeSpace = FALSE;
  213. }
  214. else {
  215. freespac = ( (__int64)cBytesPerSec * (__int64)cSecsPerClus * (__int64)cFreeClus );
  216. BytesPerCluster = cSecsPerClus * cBytesPerSec;
  217. }
  218. if (!fDontLowerCase) {
  219. _strlwr(FullPathSrc);
  220. _strlwr(Destination);
  221. }
  222. State->CurrentOutput += sprintf( State->CurrentOutput, "%s => %s\t", FullPathSrc, Destination);
  223. if (CanDetectFreeSpace) {
  224. sizeround = CopyRequest->SizeLow;
  225. sizeround += BytesPerCluster - 1;
  226. sizeround /= BytesPerCluster;
  227. sizeround *= BytesPerCluster;
  228. if (freespac < sizeround) {
  229. State->CurrentOutput += sprintf( State->CurrentOutput, "not enough space\n");
  230. return;
  231. }
  232. }
  233. GET_ATTRIBUTES(Destination, Attributes);
  234. i = SET_ATTRIBUTES(Destination, Attributes & NONREADONLYSYSTEMHIDDEN );
  235. i = 1;
  236. do {
  237. if (!fCreateLink) {
  238. if (!fBreakLinks) {
  239. pend = MyCopyFile (FullPathSrc, Destination, FALSE);
  240. }
  241. else {
  242. _unlink(Destination);
  243. pend = MyCopyFile (FullPathSrc, Destination, FALSE);
  244. }
  245. }
  246. else {
  247. if (i == 1) {
  248. pend = MakeLink (FullPathSrc, Destination, FALSE);
  249. }
  250. else {
  251. pend = MakeLink (FullPathSrc, Destination, TRUE);
  252. }
  253. }
  254. if (SparseTree && !pend) {
  255. EnterCriticalSection( &CreatePathCriticalSection );
  256. if (!MyCreatePath(Destination, FALSE)) {
  257. State->CurrentOutput += sprintf( State->CurrentOutput, "Unable to create path %s", Destination);
  258. ExitValue = 1;
  259. }
  260. LeaveCriticalSection( &CreatePathCriticalSection );
  261. }
  262. } while ((i++ < 2) && (!pend) );
  263. if (!pend) {
  264. LastErrorGot = GetLastError ();
  265. if ((fCreateLink) && (LastErrorGot == 1)) {
  266. State->CurrentOutput += sprintf( State->CurrentOutput, "Can only make links on NTFS and OFS");
  267. }
  268. else if (fCreateLink) {
  269. State->CurrentOutput += sprintf( State->CurrentOutput, "(error = %d)", LastErrorGot);
  270. }
  271. else {
  272. State->CurrentOutput += sprintf( State->CurrentOutput, "Copy Error (error = %d)", LastErrorGot);
  273. }
  274. ExitValue = 1;
  275. }
  276. State->CurrentOutput += sprintf( State->CurrentOutput, "%s\n", pend == TRUE ? "[OK]" : "");
  277. free (CopyRequest->Destination);
  278. free (CopyRequest->FullPathSrc);
  279. //GET_ATTRIBUTES( FullPathSrc, Attributes);
  280. if ( !fDontCopyAttribs)
  281. {
  282. i = SET_ATTRIBUTES( Destination, CopyRequest->Attributes);
  283. }
  284. else
  285. {
  286. i = SET_ATTRIBUTES( Destination, FILE_ATTRIBUTE_ARCHIVE);
  287. }
  288. }
  289. PWORK_QUEUE
  290. CreateWorkQueue(
  291. IN DWORD NumberOfWorkerThreads,
  292. IN PWORKER_ROUTINE WorkerRoutine
  293. )
  294. /*++
  295. Routine Description:
  296. This function creates a work queue, with the specified number of
  297. threads to service work items placed in the queue. Work items
  298. are removed from the queue in the same order that they are placed
  299. in the queue.
  300. Arguments:
  301. NumberOfWorkerThreads - Specifies how many threads this function
  302. should create to process work items placed in the queue.
  303. Must be greater than 0 and less than 128.
  304. WorkerRoutine - Specifies the address of a routine to call
  305. for each work item as it is removed from the queue. The
  306. thread context the routine is called in is undefined.
  307. Return Value:
  308. A pointer to the work queue. Returns NULL if unable to create
  309. the work queue and its worker threads. Extended error information
  310. is available from GetLastError()
  311. --*/
  312. {
  313. PWORK_QUEUE WorkQueue;
  314. HANDLE Thread;
  315. DWORD ThreadId;
  316. DWORD i;
  317. //
  318. // Allocate space for the work queue, which includes an
  319. // array of thread handles.
  320. //
  321. WorkQueue = LocalAlloc( LMEM_ZEROINIT,
  322. sizeof( *WorkQueue ) +
  323. (NumberOfWorkerThreads * sizeof( HANDLE ))
  324. );
  325. if (WorkQueue == NULL) {
  326. return NULL;
  327. }
  328. //
  329. // The work queue is controlled by a counting semaphore that
  330. // is incremented each time a work item is placed in the queue
  331. // and decremented each time a worker thread wakes up to remove
  332. // an item from the queue.
  333. //
  334. if (WorkQueue->Semaphore = CreateSemaphore( NULL, 0, 100000, NULL )) {
  335. //
  336. // Mutual exclusion between the worker threads accessing
  337. // the work queue is done with a critical section.
  338. //
  339. InitializeCriticalSection( &WorkQueue->CriticalSection );
  340. //
  341. // The queue itself is just a doubly linked list, where
  342. // items are placed in the queue at the tail of the list
  343. // and removed from the queue from the head of the list.
  344. //
  345. InitializeListHead( &WorkQueue->Queue );
  346. //
  347. // Removed the address of the supplied worker function
  348. // in the work queue structure.
  349. //
  350. WorkQueue->WorkerRoutine = WorkerRoutine;
  351. //
  352. // Now create the requested number of worker threads.
  353. // The handle to each thread is remembered in an
  354. // array of thread handles in the work queue structure.
  355. //
  356. for (i=0; i<NumberOfWorkerThreads; i++) {
  357. Thread = CreateThread( NULL,
  358. 0,
  359. (LPTHREAD_START_ROUTINE) WorkerThread,
  360. WorkQueue,
  361. 0,
  362. &ThreadId
  363. );
  364. if (Thread == NULL) {
  365. break;
  366. }
  367. else {
  368. WorkQueue->NumberOfWorkerThreads++;
  369. WorkQueue->WorkerThreads[ i ] = Thread;
  370. SetThreadPriority( Thread, THREAD_PRIORITY_ABOVE_NORMAL );
  371. }
  372. }
  373. //
  374. // If we successfully created all of the worker threads
  375. // then return the address of the work queue structure
  376. // to indicate success.
  377. //
  378. if (i == NumberOfWorkerThreads) {
  379. return WorkQueue;
  380. }
  381. }
  382. //
  383. // Failed for some reason. Destroy whatever we managed
  384. // to create and return failure to the caller.
  385. //
  386. DestroyWorkQueue( WorkQueue );
  387. return NULL;
  388. }
  389. VOID
  390. DestroyWorkQueue(
  391. IN OUT PWORK_QUEUE WorkQueue
  392. )
  393. /*++
  394. Routine Description:
  395. This function destroys a work queue created with the CreateWorkQueue
  396. functions. It attempts to shut down the worker threads cleanly
  397. by queueing a terminate work item to each worker thread. It then
  398. waits for all the worker threads to terminate. If the wait is
  399. not satisfied within 30 seconds, then it goes ahead and terminates
  400. all of the worker threads.
  401. Arguments:
  402. WorkQueue - Supplies a pointer to the work queue to destroy.
  403. Return Value:
  404. None.
  405. --*/
  406. {
  407. DWORD i;
  408. DWORD rc;
  409. //
  410. // If the semaphore handle field is not NULL, then there
  411. // may be threads to terminate.
  412. //
  413. if (WorkQueue->Semaphore != NULL) {
  414. //
  415. // Set the termiating flag in the work queue and
  416. // signal the counting semaphore by the number
  417. // worker threads so they will all wake up and
  418. // notice the terminating flag and exit.
  419. //
  420. EnterCriticalSection( &WorkQueue->CriticalSection );
  421. _try {
  422. WorkQueue->Terminating = TRUE;
  423. ReleaseSemaphore( WorkQueue->Semaphore,
  424. WorkQueue->NumberOfWorkerThreads,
  425. NULL
  426. );
  427. }
  428. _finally {
  429. LeaveCriticalSection( &WorkQueue->CriticalSection );
  430. }
  431. //
  432. // Wait for all worker threads to wake up and see the
  433. // terminate flag and then terminate themselves. Timeout
  434. // the wait after 30 seconds.
  435. //
  436. while (TRUE) {
  437. rc = WaitForMultipleObjectsEx( WorkQueue->NumberOfWorkerThreads,
  438. WorkQueue->WorkerThreads,
  439. TRUE,
  440. 3600000,
  441. TRUE
  442. );
  443. if (rc == WAIT_IO_COMPLETION) {
  444. //
  445. // If we came out of the wait because an I/O
  446. // completion routine was called, reissue the
  447. // wait.
  448. //
  449. continue;
  450. }
  451. else {
  452. break;
  453. }
  454. }
  455. //
  456. // Now close our thread handles so they will actually
  457. // evaporate. If the wait above was unsuccessful,
  458. // then first attempt to force the termination of
  459. // each worker thread prior to closing the handle.
  460. //
  461. for (i=0; i<WorkQueue->NumberOfWorkerThreads; i++) {
  462. if (rc != NO_ERROR) {
  463. TerminateThread( WorkQueue->WorkerThreads[ i ], rc );
  464. }
  465. CloseHandle( WorkQueue->WorkerThreads[ i ] );
  466. }
  467. //
  468. // All threads stopped, all thread handles closed. Now
  469. // delete the critical section and close the semaphore
  470. // handle.
  471. //
  472. DeleteCriticalSection( &WorkQueue->CriticalSection );
  473. CloseHandle( WorkQueue->Semaphore );
  474. }
  475. //
  476. // Everything done, now free the memory used by the work queue.
  477. //
  478. LocalFree( WorkQueue );
  479. return;
  480. }
  481. BOOL
  482. QueueWorkItem(
  483. IN OUT PWORK_QUEUE WorkQueue,
  484. IN PWORK_QUEUE_ITEM WorkItem
  485. )
  486. /*++
  487. Routine Description:
  488. This function queues a work item to the passed work queue that is
  489. processed by one of the worker threads associated with the queue.
  490. Arguments:
  491. WorkQueue - Supplies a pointer to the work queue that is to
  492. receive the work item.
  493. WorkItem - Supplies a pointer to the work item to add the the queue.
  494. The work item structure contains a doubly linked list entry, the
  495. address of a routine to call and a parameter to pass to that
  496. routine. It is the routine's responsibility to reclaim the
  497. storage occupied by the WorkItem structure.
  498. Return Value:
  499. TRUE if operation was successful. Otherwise returns FALSE and
  500. extended error information is available from GetLastError()
  501. --*/
  502. {
  503. BOOL Result;
  504. //
  505. // Acquire the work queue critical section and insert the work item
  506. // in the queue and release the semaphore if the work item is not
  507. // already in the list.
  508. //
  509. EnterCriticalSection( &WorkQueue->CriticalSection );
  510. Result = TRUE;
  511. _try {
  512. WorkItem->WorkQueue = WorkQueue;
  513. InsertTailList( &WorkQueue->Queue, &WorkItem->List );
  514. Result = ReleaseSemaphore( WorkQueue->Semaphore, 1, NULL );
  515. }
  516. _finally {
  517. LeaveCriticalSection( &WorkQueue->CriticalSection );
  518. }
  519. return Result;
  520. }
  521. DWORD
  522. WorkerThread(
  523. LPVOID lpThreadParameter
  524. )
  525. {
  526. PWORK_QUEUE WorkQueue = (PWORK_QUEUE)lpThreadParameter;
  527. DWORD rc;
  528. WORK_QUEUE_ITEM InitWorkItem;
  529. PWORK_QUEUE_ITEM WorkItem;
  530. //
  531. // Call the worker routine with an initialize work item
  532. // to give it a change to initialize some per thread
  533. // state that will passed to it for each subsequent
  534. // work item.
  535. //
  536. InitWorkItem.Reason = WORK_INITIALIZE_ITEM;
  537. (WorkQueue->WorkerRoutine)( &InitWorkItem );
  538. while( TRUE ) {
  539. _try {
  540. //
  541. // Wait until something is put in the queue (semaphore is
  542. // released), remove the item from the queue, mark it not
  543. // inserted, and execute the specified routine.
  544. //
  545. rc = WaitForSingleObjectEx( WorkQueue->Semaphore, 0xFFFFFFFF, TRUE );
  546. if (rc == WAIT_IO_COMPLETION) {
  547. continue;
  548. }
  549. EnterCriticalSection( &WorkQueue->CriticalSection );
  550. _try {
  551. if (WorkQueue->Terminating && IsListEmpty( &WorkQueue->Queue )) {
  552. break;
  553. }
  554. WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList( &WorkQueue->Queue );
  555. }
  556. _finally {
  557. LeaveCriticalSection( &WorkQueue->CriticalSection );
  558. }
  559. //
  560. // Execute the worker routine for this work item.
  561. //
  562. (WorkQueue->WorkerRoutine)( WorkItem );
  563. }
  564. _except( EXCEPTION_EXECUTE_HANDLER ) {
  565. //
  566. // Ignore any exceptions from worker routine.
  567. //
  568. }
  569. }
  570. InitWorkItem.Reason = WORK_TERMINATE_ITEM;
  571. (WorkQueue->WorkerRoutine)( &InitWorkItem );
  572. ExitThread( 0 );
  573. return 0; // This will exit this thread
  574. }
  575. BOOL
  576. CreateVirtualBuffer(
  577. OUT PVIRTUAL_BUFFER Buffer,
  578. IN SIZE_T CommitSize,
  579. IN SIZE_T ReserveSize OPTIONAL
  580. )
  581. /*++
  582. Routine Description:
  583. This function is called to create a virtual buffer. A virtual
  584. buffer is a contiguous range of virtual memory, where some initial
  585. prefix portion of the memory is committed and the remainder is only
  586. reserved virtual address space. A routine is provided to extend the
  587. size of the committed region incrementally or to trim the size of
  588. the committed region back to some specified amount.
  589. Arguments:
  590. Buffer - Pointer to the virtual buffer control structure that is
  591. filled in by this function.
  592. CommitSize - Size of the initial committed portion of the buffer.
  593. May be zero.
  594. ReserveSize - Amount of virtual address space to reserve for the
  595. buffer. May be zero, in which case amount reserved is the
  596. committed size plus one, rounded up to the next 64KB boundary.
  597. Return Value:
  598. TRUE if operation was successful. Otherwise returns FALSE and
  599. extended error information is available from GetLastError()
  600. --*/
  601. {
  602. SYSTEM_INFO SystemInformation;
  603. //
  604. // Query the page size from the system for rounding
  605. // our memory allocations.
  606. //
  607. GetSystemInfo( &SystemInformation );
  608. Buffer->PageSize = SystemInformation.dwPageSize;
  609. //
  610. // If the reserve size was not specified, default it by
  611. // rounding up the initial committed size to a 64KB
  612. // boundary. This is because the Win32 Virtual Memory
  613. // API calls always allocate virtual address space on
  614. // 64KB boundaries, so we might well have it available
  615. // for commitment.
  616. //
  617. if (!ARGUMENT_PRESENT( ReserveSize )) {
  618. ReserveSize = ROUND_UP( CommitSize + 1, 0x10000 );
  619. }
  620. //
  621. // Attempt to reserve the address space.
  622. //
  623. Buffer->Base = VirtualAlloc( NULL,
  624. ReserveSize,
  625. MEM_RESERVE,
  626. PAGE_READWRITE
  627. );
  628. if (Buffer->Base == NULL) {
  629. //
  630. // Unable to reserve address space, return failure.
  631. //
  632. return FALSE;
  633. }
  634. //
  635. // Attempt to commit some initial portion of the reserved region.
  636. //
  637. //
  638. CommitSize = ROUND_UP( CommitSize, Buffer->PageSize );
  639. if (CommitSize == 0 ||
  640. VirtualAlloc( Buffer->Base,
  641. CommitSize,
  642. MEM_COMMIT,
  643. PAGE_READWRITE
  644. ) != NULL
  645. ) {
  646. //
  647. // Either the size of the committed region was zero or the
  648. // commitment succeeded. In either case calculate the
  649. // address of the first byte after the committed region
  650. // and the address of the first byte after the reserved
  651. // region and return successs.
  652. //
  653. Buffer->CommitLimit = (LPVOID)
  654. ((char *)Buffer->Base + CommitSize);
  655. Buffer->ReserveLimit = (LPVOID)
  656. ((char *)Buffer->Base + ReserveSize);
  657. return TRUE;
  658. }
  659. //
  660. // If unable to commit the memory, release the virtual address
  661. // range allocated above and return failure.
  662. //
  663. VirtualFree( Buffer->Base, 0, MEM_RELEASE );
  664. return FALSE;
  665. }
  666. BOOL
  667. ExtendVirtualBuffer(
  668. IN PVIRTUAL_BUFFER Buffer,
  669. IN LPVOID Address
  670. )
  671. /*++
  672. Routine Description:
  673. This function is called to extend the committed portion of a virtual
  674. buffer.
  675. Arguments:
  676. Buffer - Pointer to the virtual buffer control structure.
  677. Address - Byte at this address is committed, along with all memory
  678. from the beginning of the buffer to this address. If the
  679. address is already within the committed portion of the virtual
  680. buffer, then this routine does nothing. If outside the reserved
  681. portion of the virtual buffer, then this routine returns an
  682. error.
  683. Otherwise enough pages are committed so that the memory from the
  684. base of the buffer to the passed address is a contiguous region
  685. of committed memory.
  686. Return Value:
  687. TRUE if operation was successful. Otherwise returns FALSE and
  688. extended error information is available from GetLastError()
  689. --*/
  690. {
  691. SIZE_T NewCommitSize;
  692. LPVOID NewCommitLimit;
  693. //
  694. // See if address is within the buffer.
  695. //
  696. if (Address >= Buffer->Base && Address < Buffer->ReserveLimit) {
  697. //
  698. // See if the address is within the committed portion of
  699. // the buffer. If so return success immediately.
  700. //
  701. if (Address < Buffer->CommitLimit) {
  702. return TRUE;
  703. }
  704. //
  705. // Address is within the reserved portion. Determine how many
  706. // bytes are between the address and the end of the committed
  707. // portion of the buffer. Round this size to a multiple of
  708. // the page size and this is the amount we will attempt to
  709. // commit.
  710. //
  711. NewCommitSize =
  712. (ROUND_UP( (DWORD_PTR)Address + 1, Buffer->PageSize ) -
  713. (DWORD_PTR)Buffer->CommitLimit
  714. );
  715. //
  716. // Attempt to commit the memory.
  717. //
  718. NewCommitLimit = VirtualAlloc( Buffer->CommitLimit,
  719. NewCommitSize,
  720. MEM_COMMIT,
  721. PAGE_READWRITE
  722. );
  723. if (NewCommitLimit != NULL) {
  724. //
  725. // Successful, so update the upper limit of the committed
  726. // region of the buffer and return success.
  727. //
  728. Buffer->CommitLimit = (LPVOID)
  729. ((DWORD_PTR)NewCommitLimit + NewCommitSize);
  730. return TRUE;
  731. }
  732. }
  733. //
  734. // Address is outside of the buffer, return failure.
  735. //
  736. return FALSE;
  737. }
  738. BOOL
  739. TrimVirtualBuffer(
  740. IN PVIRTUAL_BUFFER Buffer
  741. )
  742. /*++
  743. Routine Description:
  744. This function is called to decommit any memory that has been
  745. committed for this virtual buffer.
  746. Arguments:
  747. Buffer - Pointer to the virtual buffer control structure.
  748. Return Value:
  749. TRUE if operation was successful. Otherwise returns FALSE and
  750. extended error information is available from GetLastError()
  751. --*/
  752. {
  753. Buffer->CommitLimit = Buffer->Base;
  754. return VirtualFree( Buffer->Base, 0, MEM_DECOMMIT );
  755. }
  756. BOOL
  757. FreeVirtualBuffer(
  758. IN PVIRTUAL_BUFFER Buffer
  759. )
  760. /*++
  761. Routine Description:
  762. This function is called to free all the memory that is associated
  763. with this virtual buffer.
  764. Arguments:
  765. Buffer - Pointer to the virtual buffer control structure.
  766. Return Value:
  767. TRUE if operation was successful. Otherwise returns FALSE and
  768. extended error information is available from GetLastError()
  769. --*/
  770. {
  771. //
  772. // Decommit and release all virtual memory associated with
  773. // this virtual buffer.
  774. //
  775. return VirtualFree( Buffer->Base, 0, MEM_RELEASE );
  776. }
  777. int
  778. VirtualBufferExceptionFilter(
  779. IN DWORD ExceptionCode,
  780. IN PEXCEPTION_POINTERS ExceptionInfo,
  781. IN OUT PVIRTUAL_BUFFER Buffer
  782. )
  783. /*++
  784. Routine Description:
  785. This function is an exception filter that handles exceptions that
  786. referenced uncommitted but reserved memory contained in the passed
  787. virtual buffer. It this filter routine is able to commit the
  788. additional pages needed to allow the memory reference to succeed,
  789. then it will re-execute the faulting instruction. If it is unable
  790. to commit the pages, it will execute the callers exception handler.
  791. If the exception is not an access violation or is an access
  792. violation but does not reference memory contained in the reserved
  793. portion of the virtual buffer, then this filter passes the exception
  794. on up the exception chain.
  795. Arguments:
  796. ExceptionCode - Reason for the exception.
  797. ExceptionInfo - Information about the exception and the context
  798. that it occurred in.
  799. Buffer - Points to a virtual buffer control structure that defines
  800. the reserved memory region that is to be committed whenever an
  801. attempt is made to access it.
  802. Return Value:
  803. Exception disposition code that tells the exception dispatcher what
  804. to do with this exception. One of three values is returned:
  805. EXCEPTION_EXECUTE_HANDLER - execute the exception handler
  806. associated with the exception clause that called this filter
  807. procedure.
  808. EXCEPTION_CONTINUE_SEARCH - Continue searching for an exception
  809. handler to handle this exception.
  810. EXCEPTION_CONTINUE_EXECUTION - Dismiss this exception and return
  811. control to the instruction that caused the exception.
  812. --*/
  813. {
  814. LPVOID FaultingAddress;
  815. //
  816. // If this is an access violation touching memory within
  817. // our reserved buffer, but outside of the committed portion
  818. // of the buffer, then we are going to take this exception.
  819. //
  820. if (ExceptionCode == STATUS_ACCESS_VIOLATION) {
  821. //
  822. // Get the virtual address that caused the access violation
  823. // from the exception record. Determine if the address
  824. // references memory within the reserved but uncommitted
  825. // portion of the virtual buffer.
  826. //
  827. FaultingAddress = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ];
  828. if (FaultingAddress >= Buffer->CommitLimit &&
  829. FaultingAddress <= Buffer->ReserveLimit
  830. ) {
  831. //
  832. // This is our exception. Try to extend the buffer
  833. // to including the faulting address.
  834. //
  835. if (ExtendVirtualBuffer( Buffer, FaultingAddress )) {
  836. //
  837. // Buffer successfully extended, so re-execute the
  838. // faulting instruction.
  839. //
  840. return EXCEPTION_CONTINUE_EXECUTION;
  841. }
  842. else {
  843. //
  844. // Unable to extend the buffer. Stop copying
  845. // for exception handlers and execute the caller's
  846. // handler.
  847. //
  848. return EXCEPTION_EXECUTE_HANDLER;
  849. }
  850. }
  851. }
  852. //
  853. // Not an exception we care about, so pass it up the chain.
  854. //
  855. return EXCEPTION_CONTINUE_SEARCH;
  856. }