|
|
1.1 root 1: /*++
2:
3: Copyright (c) 1992 Microsoft Corporation
4:
5: Module Name:
6:
7: pdc.c
8:
9: Abstract:
10:
11: This is the main source file for the Windows/NT PDC API demonstration
12: program. This program demonstrates how to use many of the advanced
13: operating system features provided by the Win32 API set on Windows/NT.
14:
15: This file and its corresponding header file, pdc.h, can be found
16: in the sample programs directory of the PDC CD-ROM.
17:
18: This program has a real purpose, although the implementation is
19: somewhat contrived in order to demonstrate the various operating
20: system features of Windows/NT.
21:
22: The features that this program demonstrate are:
23:
24: - Creating multiple threads, using critical sections
25: and semaphores for synchronization.
26:
27: - Thread termination.
28:
29: - Virtual memory, commitment vs reservation.
30:
31: - Structured exception handling, including finally
32: clauses and an exception filter procedure.
33:
34: - Enumeration of directory entries.
35:
36: - Mapped file I/O
37:
38: - Asynchronous file I/O via completion routine.
39:
40: - Synchronous file I/O
41:
42: PDC is a character mode program for searching the files in a
43: directory tree for a match against a pattern. It uses multiple
44: threads to do it's work, with each thread processing a file at a
45: time, accumulating it's matches and outputting them to standard
46: output contiguously when it is done searching a file.
47:
48: The command line syntax is:
49:
50: Usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]
51:
52: where:
53:
54: -h - prints this message.
55:
56: -v - generates verbose output.
57:
58: -y - ignores case when doing comparisons.
59:
60: -a - specifies that the program should use asynchronous file
61: I/O to read the files being searched.
62:
63: -s - specifies that the program should use synchronous file
64: I/O to read the files being searched.
65:
66: -m - specifies that the program should use mapped file I/O
67: to read the files being searched.
68:
69: -t - specifies the number of threads to use when doing the
70: search. Default is 4 * the number of processors.
71:
72: SearchString - specifies the text to search for. Enclose in
73: quotes if it contains spaces or punctuation.
74:
75: DirectoryPath - specifies the root of the tree to begin the
76: search at. Defaults to the current directory.
77:
78:
79: --*/
80:
81: #include "pdc.h"
82:
83:
84: int
85: main(
86: int argc,
87: char *argv[]
88: )
89:
90: /*++
91:
92: Routine Description:
93:
94: This is the main procedure for the PDC program, and is called by
95: the C Runtime startup code when the program starts.
96:
97: Arguments:
98:
99: argc - number of argumments in the argv array.
100:
101: argv - pointer to an array of null terminated string pointers.
102:
103: Return Value:
104:
105: Process exit status. The value returned by this function will
106: be used as the exit code parameter passed to ExitProcess.
107:
108: --*/
109:
110: {
111: SYSTEM_INFO SystemInformation;
1.1.1.2 ! root 112: PWORK_QUEUE WorkQueue;
1.1 root 113:
114: //
115: // Query the number of processors from the system and
116: // default the number of worker threads to 4 times that.
117: //
118:
119: GetSystemInfo( &SystemInformation );
120: NumberOfWorkerThreads = SystemInformation.dwNumberOfProcessors * 4;
121:
122: //
123: // Process the arguments given on the command line.
124: //
125:
126: if (!ProcessCommandLineArguments( argc, argv )) {
127: exit( 1 );
128: }
129:
130: //
131: // Allocate a thread local storage slot for use by our worker
132: // thread routine (ProcessRequest). This call reserves a
133: // 32-bit slot in the thread local storage array for every
134: // thread in this process. Remember the slot index in a global
135: // variable for use by our worker thread routine.
136: //
137:
138: TlsIndex = TlsAlloc();
139: if (TlsIndex == 0xFFFFFFFF) {
140: fprintf( stderr, "PDC: Unable to allocated thread local storage.\n" );
141: exit( 1 );
142: }
143:
144:
145: //
146: // Create a work queue, which will create the specified number of threads
147: // to process.
148: //
149:
150: WorkQueue = CreateWorkQueue( NumberOfWorkerThreads, ProcessRequest );
151: if (WorkQueue == NULL) {
152: fprintf( stderr, "PDC: Unable to create %u worker threads.\n", NumberOfWorkerThreads );
153: exit( 1 );
154: }
155:
156: //
157: // If using asynchronous I/O, create an event that will be signalled
158: // when there are no more outstanding I/O requests. The event is
159: // a manual reset event, that once signalled via SetEvent, will
160: // remain signalled until ResetEvent is called.
161: //
162:
163: if (ASyncIO) {
164: IoCompletedEvent = CreateEvent( NULL, // Not inherited
165: TRUE, // Manual reset
166: FALSE, // Initially reset
167: NULL // No name
168: );
169: }
170:
171: //
172: // Now walk the directory tree, which will call our procedure
173: // (QueueSearchFile) for each directory and file in the tree.
174: //
175:
176: EnumerateDirectoryTree( DirectoryPath,
177: QueueSearchFile,
1.1.1.2 ! root 178: WorkQueue
1.1 root 179: );
180:
181: //
182: // Done walking the tree. If using asynchronous I/O, wait for all of
183: // the outstanding I/O requests to be completed.
184: //
185:
186: if (ASyncIO) {
187: //
188: // We use an alertable wait in a loop, as I/O completion
189: // will terminate the wait, even through the event we
190: // are waiting on is not signalled.
191: //
192:
193: while (WaitForSingleObjectEx( IoCompletedEvent,
194: 0xFFFFFFFF,
195: TRUE
196: ) == WAIT_IO_COMPLETION
197: ) {
198: ;
199: }
200: }
201:
202: //
203: // All done, destroy the work queue. This will wait for the work queues
204: // to empty before terminating the worker threads and destroying the
205: // queue.
206: //
207:
208: DestroyWorkQueue( WorkQueue );
209:
210: if (Verbose && MatchedLineCount) {
211: fprintf( stderr,
212: "Found %u lines with matches in %u files, out of %u files searched.\n",
213: MatchedLineCount,
214: MatchedFileCount,
215: SearchedFileCount
216: );
217: }
218:
219: return 0;
220: }
221:
222:
223:
224: VOID
225: QueueSearchFile(
226: LPSTR Path,
227: PWIN32_FIND_DATA FindFileData,
228: PVOID EnumerateParameter
229: )
230:
231: /*++
232:
233: Routine Description:
234:
235: This is the directory enumeration function. It is called by the
236: EnumerateDirectoryTree function once for each file and directory
237: in the tree.
238:
239: This function, if it decides it wants to search the file, will
240: open the file and then, depending upon the I/O method selected via
241: the command line, will:
242:
243: - map the file PAGE_READONLY for mapped file I/O
244:
245: - read the file into an allocated buffer for synchronous
246: I/O.
247:
248: - will allocate the buffer and start a read operation
249: to read the entire file into the buffer. A completion
250: routine will be invoked when the read completes, possibly
251: in another thread context.
252:
253: Finally it will queue a search request to the work queue, with the
254: relevant information contained in the request. For asynchronous
255: I/O, the search request is allocated and initialized here but is
256: not actually queued to the work queue until the I/O completion
257: routine has been called.
258:
259: Arguments:
260:
261: Path - Supplies a pointer to a null terminated string that contains
262: the fully qualified path of the file or directory.
263:
264: FindFileData - Supplies the directory information associated
265: with the file or directory specified by the Path argument.
266:
267: EnumerateParameter - Uninterpreted 32-bit value. Not used.
268:
269: Return Value:
270:
271: None.
272:
273: --*/
274:
275: {
1.1.1.2 ! root 276: PWORK_QUEUE WorkQueue = (PWORK_QUEUE)EnumerateParameter;
1.1 root 277: PSEARCH_REQUEST SearchRequest;
278: HANDLE File;
279: HANDLE Mapping;
280: LPVOID FileData;
281: DWORD FileSize;
282:
283: //
284: // Ignore directories or zero length files, as there
285: // is nothing to search in these cases.
286: //
287:
288: if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
289: !(FileSize = FindFileData->nFileSizeLow)
290: ) {
291: return;
292: }
293:
294: //
295: // Open the file using the fully qualified path. Specify the
296: // sequential scan hint to the cache manager and if asynchronous
297: // I/O will be used, specified the overlapped flag as well.
298: //
299:
300: File = CreateFile( Path,
301: GENERIC_READ,
302: FILE_SHARE_READ,
303: NULL,
304: OPEN_EXISTING,
305: FILE_FLAG_SEQUENTIAL_SCAN |
306: (ASyncIO ? FILE_FLAG_OVERLAPPED : 0),
307: NULL
308: );
309:
310: //
311: // Since NULL might be a valid file object handle, failure
312: // is indicated by a special return value.
313: //
314:
315: if (File == INVALID_HANDLE_VALUE) {
316: fprintf( stderr, "%s(0) : error %u: Unable to open file.\n",
317: Path,
318: GetLastError()
319: );
320:
321: return;
322: }
323:
324: //
325: // File successfully opened for read access.
326: //
327: if (MappedFileIO) {
328: //
329: // If mapped file I/O, create a file mapping object, backed by
330: // the file we just opened. Make the default page protection
331: // for the mapping be readonly.
332: //
333:
334: Mapping = CreateFileMapping( File,
335: NULL,
336: PAGE_READONLY,
337: 0,
338: 0,
339: NULL
340: );
341:
342: //
343: // Okay to close the file handle now, since the mapping object
344: // has a reference to the file that will cause the open file
345: // object to remain until the reference is destroyed. Note that
346: // any sharing information is lost at this point, so somebody
347: // could come in an open it for write access.
348: //
349:
350: CloseHandle( File );
351:
352: //
353: // Here, a null value indicates an error.
354: //
355:
356: if (Mapping == NULL) {
357: fprintf( stderr, "%s(0) : error %u: Unable to create mapping object.\n",
358: Path,
359: GetLastError()
360: );
361: return;
362: }
363:
364: //
365: // Finally, map a view of the file, using the mapping object
366: // just created, into the address space of this process.
367: // The offset and size are zero, which means to map the
368: // entire file.
369: //
370:
371: FileData = MapViewOfFile( Mapping,
372: FILE_MAP_READ,
373: 0,
374: 0,
375: 0
376: );
377:
378: //
379: // Okay to close the mapping object handle now, it will remained
380: // referenced as long as the view remains mapped.
381: //
382:
383: CloseHandle( Mapping );
384:
385: //
386: // A null value indicates the map operation failed.
387: //
388:
389: if (FileData == NULL) {
390: fprintf( stderr, "%s(0) : error %u: Unable to map file.\n",
391: Path,
392: GetLastError()
393: );
394: return;
395: }
396:
397: //
398: // All done mapping the file. Set the File handle to NULL as
399: // it has been closed already. Both the file object and mapping
400: // objects created above will be freed when the map view is
401: // unmapped.
402: //
403:
404: File = NULL;
405: }
406: else {
407: //
408: // Not using mapped I/O, so allocate a buffer big enough to
409: // contain the entire file.
410: //
411:
412: FileData = VirtualAlloc( NULL,
413: FileSize,
414: MEM_COMMIT,
415: PAGE_READWRITE
416: );
417: if (FileData == NULL) {
418: fprintf( stderr, "%s(0) : error %u: Unable to allocate memory to contain file.\n",
419: Path,
420: GetLastError()
421: );
422: CloseHandle( File );
423: return;
424: }
425: }
426:
427: //
428: // We have successfully opened the file and are ready to go.
429: // Allocate space for a search request and fill it in
430: // with the information needed by the ProcessSearchFile
431: // function when it runs.
432: //
433:
434: SearchRequest = LocalAlloc( LMEM_ZEROINIT,
435: sizeof( *SearchRequest ) +
436: strlen( Path ) + 1
437: );
438: if (SearchRequest == NULL) {
439: fprintf( stderr, "PDC: Out of memory\n" );
440: exit( 1 );
441: }
442:
443: SearchRequest->WorkItem.Reason = WORK_ITEM;
444: SearchRequest->File = File;
445: SearchRequest->FileSize = FileSize;
446: SearchRequest->FileData = FileData;
447: strcpy( SearchRequest->FullPathName, Path );
448:
449: if (!ASyncIO) {
450: //
451: // If not using asynchronous I/O, then queue the search
452: // request to the work queue.
453: //
454:
455: QueueWorkItem( WorkQueue, &SearchRequest->WorkItem );
456: }
457: else {
458: //
459: // Using asynchronous I/O, so queue the read operation.
460: // The file handle must remain open while the read operation
461: // is pending.
462: //
463:
464: if (!ReadFileEx( File,
465: FileData,
466: FileSize,
467: &SearchRequest->OverlappedIO,
1.1.1.2 ! root 468: (LPOVERLAPPED_COMPLETION_ROUTINE) ProcessReadFileCompletion
1.1 root 469: )
470: ) {
471: fprintf( stderr, "%s(0) : error %u: Unable to queue read of file.\n",
472: Path,
473: GetLastError()
474: );
475:
476: VirtualFree( FileData, 0, MEM_RELEASE );
477: CloseHandle( File );
478: LocalFree( SearchRequest );
479: return;
480: }
481:
482:
483: //
484: // Successfully queued the read operation. Keep a count
485: // of outstanding read operations so we know when it is
486: // okay to terminate.
487: //
488:
489: OutstandingIOOperations += 1;
490: }
491:
492: //
493: // Return back to the EnumerateDirectoryTree function so that it
494: // can call us with the next file or directrry.
495: //
496:
497: return;
498: }
499:
500:
501:
502: VOID
503: ProcessRequest(
504: IN PWORK_QUEUE_ITEM WorkItem
505: )
506:
507: /*++
508:
509: Routine Description:
510:
511: This function is called whenever a work item is removed from
512: the work queue by one of the worker threads. Which worker
513: thread context this function is called in is arbitrary.
514:
515: This functions keeps a pointer to state information in
516: thread local storage.
517:
518: This function is called once at the beginning with a
519: special initialization call. During this call, this
520: function allocates space for state information and
521: remembers the pointer to the state information in
522: a Thread Local Storage (TLS) slot.
523:
524: This function is called once at the end with a special
525: termination call. During this call, this function
526: frees the state information allocated during the
527: initialization call.
528:
529: In between these two calls are zero or more calls to
530: handle a work item. The work item is a search request
531: which is handled by the ProcessSearchFile function.
532:
533: Arguments:
534:
535: WorkItem - Supplies a pointer to the work item just removed
536: from the work queue. It is the responsibility of this
537: routine to free the memory used to hold the work item.
538:
539: Return Value:
540:
541: None.
542:
543: --*/
544:
545: {
546: DWORD BytesWritten;
547: PSEARCH_REQUEST_STATE State;
548: PSEARCH_REQUEST SearchRequest;
1.1.1.2 ! root 549: CHAR MessageBuffer[ 2 * MAX_PATH ];
1.1 root 550:
551: if (WorkItem->Reason == WORK_INITIALIZE_ITEM) {
552: //
553: // First time initialization call. Allocate space for
554: // state information.
555: //
556:
557: State = LocalAlloc( LMEM_ZEROINIT,
558: sizeof( *State )
559: );
560:
561: if (State != NULL) {
562: //
563: // Now create a virtual buffer, with an initial commitment
564: // of zero and a maximum commitment of 128KB. This buffer
565: // will be used to accumulate the matched strings output
566: // during the search of a single file. This is so the
567: // output can be written to standard output with a single
568: // write call, thus insuring that it remains contiguous
569: // in the output stream, and is not intermingled with the
570: // output of the other worker threads.
571: //
572:
573: if (CreateVirtualBuffer( &State->Buffer, 0, 2 * 64 * 1024 )) {
574: //
575: // The CurrentOutput field of the state block is
576: // a pointer to where the next output goes in the
577: // buffer. It is initialized here and reset each
578: // time the buffer is flushed to standard output.
579: //
580:
581: State->CurrentOutput = State->Buffer.Base;
582: }
583: else {
584: LocalFree( State );
585: State = NULL;
586: }
587: }
588:
589: //
590: // Remember the pointer to the state informaiton
591: // thread local storage.
592: //
593:
594: TlsSetValue( TlsIndex, State );
595: return;
596: }
597:
598: //
599: // Here to handle a work item or special terminate call.
600: // Get the state pointer from thread local storage.
601: //
602:
603: State = (PSEARCH_REQUEST_STATE)TlsGetValue( TlsIndex );
604: if (State == NULL) {
605: return;
606: }
607:
608: //
609: // If this is the special terminate work item, free the virtual
610: // buffer and state block allocated above and set the thread
611: // local storage value to NULL. Return to caller.
612: //
613:
614: if (WorkItem->Reason == WORK_TERMINATE_ITEM) {
615: FreeVirtualBuffer( &State->Buffer );
616: LocalFree( State );
617: TlsSetValue( TlsIndex, NULL );
618: return;
619: }
620:
621: //
622: // If not an initialize or terminate work item, then must be a
623: // search request. Calculate the address of the search request
624: // block, based on the position of the WorkItem field in the
625: // SEARCH_REQUEST structure.
626: //
627:
628: SearchRequest = CONTAINING_RECORD( WorkItem, SEARCH_REQUEST, WorkItem );
629:
630: //
631: // Actual search operation is protected by a try ... except
632: // block so that any attempts to store into the virtual buffer
633: // will be handled correctly by extending the virtual buffer.
634: //
635:
636: try {
637: //
638: // Perform the search against this file.
639: //
640:
641: ProcessSearchFile( SearchRequest, State );
642:
643: //
644: // Done with this file. If using asynchronous I/O, decrement the
645: // count of outstanding I/O operations and it if goes to zero,
646: // then signal the IoCompletedEvent as there are no more outstanding
647: // I/O operations.
648: //
649:
650: if (ASyncIO && InterlockedDecrement( &OutstandingIOOperations ) == 0) {
651: SetEvent( IoCompletedEvent );
652: }
653:
654: //
655: // If any output was written to the virtual buffer,
656: // flush the output to standard output. Trim the
657: // virtual buffer back to zero committed pages.
658: //
659:
660: if (State->CurrentOutput > (LPSTR)State->Buffer.Base) {
661: WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
662: State->Buffer.Base,
663: State->CurrentOutput - (LPSTR)State->Buffer.Base,
664: &BytesWritten,
665: NULL
666: );
667:
668: TrimVirtualBuffer( &State->Buffer );
669: State->CurrentOutput = (LPSTR)State->Buffer.Base;
670: }
671: }
672:
673: except( VirtualBufferExceptionFilter( GetExceptionCode(),
674: GetExceptionInformation(),
675: &State->Buffer
676: )
677: ) {
678:
679: //
680: // We will get here if the exception filter was unable to
681: // commit the memory.
682: //
683:
1.1.1.2 ! root 684: WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
! 685: MessageBuffer,
! 686: sprintf( MessageBuffer,
! 687: "%s(0) : error 0: too many matches for file\n",
! 688: SearchRequest->FullPathName
! 689: ),
! 690: &BytesWritten,
! 691: NULL
! 692: );
1.1 root 693: }
694:
695: //
1.1.1.2 ! root 696: // Free the storage used by the SearchRequest
! 697: //
! 698:
! 699: LocalFree( SearchRequest );
! 700:
! 701: //
1.1 root 702: // All done with this request. Return to the worker thread that
703: // called us.
704: //
705:
706: return;
707: }
708:
709:
710:
711: VOID
712: ProcessReadFileCompletion(
713: DWORD dwErrorCode,
714: DWORD dwNumberOfBytesTransfered,
715: LPOVERLAPPED lpOverlapped
716: )
717:
718: /*++
719:
720: Routine Description:
721:
722: This function is called whenever an asynchronous I/O operation,
723: queued by the previous function, completes. This function
724: calulates the address of the search request block and then
725: queue the request to the work queue, now that the data is
726: in memory.
727:
728: Arguments:
729:
730: dwErrorCode - Supplies the error code that the I/O completed with.
731:
732: dwNumberOfBytesTransfered - Supplies the actual number of bytes
733: transferred.
734:
735: lpOverlapped - Supplies a pointer to the structure given to
736: ReadFileEx when the I/O operation was queued.
737:
738: Return Value:
739:
740: None.
741:
742: --*/
743:
744: {
745: PSEARCH_REQUEST SearchRequest;
746:
747: //
748: // Since the data we need is now in memory, queue the search
749: // request to the work queue.
750: //
751:
752: SearchRequest = CONTAINING_RECORD( lpOverlapped, SEARCH_REQUEST, OverlappedIO );
1.1.1.2 ! root 753: QueueWorkItem( SearchRequest->WorkItem.WorkQueue, &SearchRequest->WorkItem );
1.1 root 754: }
755:
756:
757:
758: VOID
759: ProcessSearchFile(
760: IN PSEARCH_REQUEST SearchRequest,
761: IN PSEARCH_REQUEST_STATE State
762: )
763:
764: /*++
765:
766: Routine Description:
767:
768: This function performs the actual search of the contents of the
769: passed file for the search string given on the command line.
770: If we are using synchronous I/O, then do the read operation
771: now.
772:
773: Search the contents of the file for any matches, and accumulate
774: the match output in the virtual buffer using sprintf, which is
775: multi-thread safe, even with the single threaded version of
776: the libraries.
777:
778: Arguments:
779:
780: SearchRequest - Supplies a pointer to the search request which
781: contains the relevant information.
782:
783: State - Supplies a pointer to state information for the current
784: thread.
785:
786: Return Value:
787:
788: None.
789:
790: --*/
791:
792: {
793: LPSTR FileData, s, s1, BegLine, EndLine, EndOfFile;
794: DWORD LineNumber;
795: DWORD MatchesFound;
796: DWORD BytesRead;
797:
798: // Get a pointer to the beginning of the file data in memory
799: // and calculate the address of the end of file point in
800: // memory.
801: //
802:
803: FileData = SearchRequest->FileData;
804: EndOfFile = FileData + SearchRequest->FileSize;
805:
806: //
807: // If using synchronous I/O, then we have not read in the
808: // file contents yet, so issue the synchronous read to get
809: // the data into memory.
810: //
811:
812: if (SyncIO) {
813: if (!ReadFile( SearchRequest->File,
814: FileData,
815: SearchRequest->FileSize,
816: &BytesRead,
817: NULL
818: ) ||
819: BytesRead != SearchRequest->FileSize
820: ) {
821: State->CurrentOutput += sprintf( State->CurrentOutput,
822: "%s(0) : error %u: Unable to read file contents.\n",
823: SearchRequest->FullPathName,
824: GetLastError()
825: );
826:
827: CloseHandle( SearchRequest->File );
828: return;
829: }
830: }
831:
832: //
833: // Close any open file handle associated with this request.
834: //
835:
836: if (SearchRequest->File != NULL) {
837: CloseHandle( SearchRequest->File );
838: }
839:
840: //
841: // Search the file contents, keeping track of line breaks
842: // so we can tell the line number of each match.
843: //
844:
845: s = FileData;
846: LineNumber = 0;
847: MatchesFound = 0;
848: while (s < EndOfFile) {
849: BegLine = s;
850: while (s < EndOfFile && *s != '\n') {
851: s++;
852: }
853:
854: if (*s == '\n') {
855: LineNumber += 1;
856: EndLine = s - 1;
857: if (EndLine > BegLine && EndLine[ -1 ] == '\r') {
858: EndLine -= 1;
859: }
860:
861: s1 = BegLine;
862: while (s1 < (EndLine - SearchStringLength)) {
863: if (!(SearchFunction)( s1, SearchString, SearchStringLength )) {
864: //
865: // We have a match for this line. Append the
866: // output to the virtual buffer and update the
867: // current output pointer.
868: //
869:
870: State->CurrentOutput += sprintf( State->CurrentOutput,
871: "%s(%u) : %.*s\n",
872: SearchRequest->FullPathName,
873: LineNumber,
874: EndLine - BegLine,
875: BegLine
876: );
877: MatchesFound += 1;
878: break;
879: }
880:
881: s1++;
882: }
883:
884: s++;
885: }
886: }
887:
888: if (MatchesFound) {
889: MatchedLineCount += MatchesFound;
890: MatchedFileCount += 1;
891: }
892: SearchedFileCount += 1;
893:
894: //
895: // All done with the file contents. Discard it either by
896: // unmapping the view of the file in the case of mapped file
897: // I/O or free the virtual memory for other types of I/O
898: //
899:
900: if (MappedFileIO) {
1.1.1.2 ! root 901: if (!UnmapViewOfFile( FileData )) {
! 902: State->CurrentOutput += sprintf( State->CurrentOutput,
! 903: "%s(%u) : UnmapViewOfFile( %08x ) failed, error == %u\n",
! 904: SearchRequest->FullPathName,
! 905: LineNumber,
! 906: FileData,
! 907: GetLastError()
! 908: );
! 909: }
1.1 root 910: }
911: else {
912: VirtualFree( FileData, 0, MEM_RELEASE );
913: }
914: }
915:
916:
917: PWORK_QUEUE
918: CreateWorkQueue(
919: IN DWORD NumberOfWorkerThreads,
920: IN PWORKER_ROUTINE WorkerRoutine
921: )
922:
923: /*++
924:
925: Routine Description:
926:
927: This function creates a work queue, with the specified number of
928: threads to service work items placed in the queue. Work items
929: are removed from the queue in the same order that they are placed
930: in the queue.
931:
932: Arguments:
933:
934: NumberOfWorkerThreads - Specifies how many threads this function
935: should create to process work items placed in the queue.
936: Must be greater than 0 and less than 128.
937:
938: WorkerRoutine - Specifies the address of a routine to call
939: for each work item as it is removed from the queue. The
940: thread context the routine is called in is undefined.
941:
942: Return Value:
943:
944: A pointer to the work queue. Returns NULL if unable to create
945: the work queue and its worker threads. Extended error information
946: is available from GetLastError()
947:
948: --*/
949:
950: {
951: PWORK_QUEUE WorkQueue;
952: HANDLE Thread;
953: DWORD ThreadId;
954: DWORD i;
955:
956: //
957: // Allocate space for the work queue, which includes an
958: // array of thread handles.
959: //
960:
961: WorkQueue = LocalAlloc( LMEM_ZEROINIT,
962: sizeof( *WorkQueue ) +
963: (NumberOfWorkerThreads * sizeof( HANDLE ))
964: );
965: if (WorkQueue == NULL) {
966: return NULL;
967: }
968:
969: //
970: // The work queue is controlled by a counting semaphore that
971: // is incremented each time a work item is placed in the queue
972: // and decremented each time a worker thread wakes up to remove
973: // an item from the queue.
974: //
975:
1.1.1.2 ! root 976: if (WorkQueue->Semaphore = CreateSemaphore( NULL, 0, 100000, NULL )) {
1.1 root 977: //
978: // Mutual exclusion between the worker threads accessing
979: // the work queue is done with a critical section.
980: //
981:
982: InitializeCriticalSection( &WorkQueue->CriticalSection );
983:
984: //
985: // The queue itself is just a doubly linked list, where
986: // items are placed in the queue at the tail of the list
987: // and removed from the queue from the head of the list.
988: //
989:
990: InitializeListHead( &WorkQueue->Queue );
991:
992: //
993: // Removed the address of the supplied worker function
994: // in the work queue structure.
995: //
996:
997: WorkQueue->WorkerRoutine = WorkerRoutine;
998:
999: //
1000: // Now create the requested number of worker threads.
1001: // The handle to each thread is remembered in an
1002: // array of thread handles in the work queue structure.
1003: //
1004:
1005: for (i=0; i<NumberOfWorkerThreads; i++) {
1006: Thread = CreateThread( NULL,
1007: 0,
1.1.1.2 ! root 1008: (LPTHREAD_START_ROUTINE) WorkerThread,
! 1009: WorkQueue,
1.1 root 1010: 0,
1011: &ThreadId
1012: );
1013: if (Thread == NULL) {
1014: break;
1015: }
1016: else {
1017: WorkQueue->NumberOfWorkerThreads++;
1018: WorkQueue->WorkerThreads[ i ] = Thread;
1.1.1.2 ! root 1019: SetThreadPriority( Thread, THREAD_PRIORITY_ABOVE_NORMAL );
1.1 root 1020: }
1021: }
1022:
1023: //
1024: // If we successfully created all of the worker threads
1025: // then return the address of the work queue structure
1026: // to indicate success.
1027: //
1028:
1029: if (i == NumberOfWorkerThreads) {
1030: return WorkQueue;
1031: }
1032: }
1033:
1034: //
1035: // Failed for some reason. Destroy whatever we managed
1036: // to create and return failure to the caller.
1037: //
1038:
1039: DestroyWorkQueue( WorkQueue );
1040: return NULL;
1041: }
1042:
1043:
1044:
1045: VOID
1046: DestroyWorkQueue(
1047: IN OUT PWORK_QUEUE WorkQueue
1048: )
1049:
1050: /*++
1051:
1052: Routine Description:
1053:
1054: This function destroys a work queue created with the CreateWorkQueue
1055: functions. It attempts to shut down the worker threads cleanly
1056: by queueing a terminate work item to each worker thread. It then
1057: waits for all the worker threads to terminate. If the wait is
1058: not satisfied within 30 seconds, then it goes ahead and terminates
1059: all of the worker threads.
1060:
1061: Arguments:
1062:
1063: WorkQueue - Supplies a pointer to the work queue to destroy.
1064:
1065: Return Value:
1066:
1067: None.
1068:
1069: --*/
1070:
1071: {
1072: DWORD i;
1073: DWORD rc;
1074:
1075: //
1076: // If the semaphore handle field is not NULL, then there
1077: // may be threads to terminate.
1078: //
1079:
1080: if (WorkQueue->Semaphore != NULL) {
1081: //
1082: // Set the termiating flag in the work queue and
1083: // signal the counting semaphore by the number
1084: // worker threads so they will all wake up and
1085: // notice the terminating flag and exit.
1086: //
1087:
1088: EnterCriticalSection( &WorkQueue->CriticalSection );
1089: try {
1090: WorkQueue->Terminating = TRUE;
1091: ReleaseSemaphore( WorkQueue->Semaphore,
1092: WorkQueue->NumberOfWorkerThreads,
1093: NULL
1094: );
1095: }
1096: finally {
1097: LeaveCriticalSection( &WorkQueue->CriticalSection );
1098: }
1099:
1100: //
1101: // Wait for all worker threads to wake up and see the
1102: // terminate flag and then terminate themselves. Timeout
1103: // the wait after 30 seconds.
1104: //
1105:
1106: while (TRUE) {
1107: rc = WaitForMultipleObjectsEx( WorkQueue->NumberOfWorkerThreads,
1108: WorkQueue->WorkerThreads,
1109: TRUE,
1110: 30000,
1111: TRUE
1112: );
1113: if (rc == WAIT_IO_COMPLETION) {
1114: //
1115: // If we came out of the wait because an I/O
1116: // completion routine was called, reissue the
1117: // wait.
1118: //
1119: continue;
1120: }
1121: else {
1122: break;
1123: }
1124: }
1125:
1126: //
1127: // Now close our thread handles so they will actually
1128: // evaporate. If the wait above was unsuccessful,
1129: // then first attempt to force the termination of
1130: // each worker thread prior to closing the handle.
1131: //
1132:
1133: for (i=0; i<WorkQueue->NumberOfWorkerThreads; i++) {
1134: if (rc != NO_ERROR) {
1135: TerminateThread( WorkQueue->WorkerThreads[ i ], rc );
1136: }
1137:
1138: CloseHandle( WorkQueue->WorkerThreads[ i ] );
1139: }
1140:
1141: //
1142: // All threads stopped, all thread handles closed. Now
1143: // delete the critical section and close the semaphore
1144: // handle.
1145: //
1146:
1147: DeleteCriticalSection( &WorkQueue->CriticalSection );
1148: CloseHandle( WorkQueue->Semaphore );
1149: }
1150:
1151: //
1152: // Everything done, now free the memory used by the work queue.
1153: //
1154:
1155: LocalFree( WorkQueue );
1156: return;
1157: }
1158:
1159:
1160:
1161: BOOL
1162: QueueWorkItem(
1163: IN OUT PWORK_QUEUE WorkQueue,
1164: IN PWORK_QUEUE_ITEM WorkItem
1165: )
1166:
1167: /*++
1168:
1169: Routine Description:
1170:
1171: This function queues a work item to the passed work queue that is
1172: processed by one of the worker threads associated with the queue.
1173:
1174: Arguments:
1175:
1176: WorkQueue - Supplies a pointer to the work queue that is to
1177: receive the work item.
1178:
1179: WorkItem - Supplies a pointer to the work item to add the the queue.
1180: The work item structure contains a doubly linked list entry, the
1181: address of a routine to call and a parameter to pass to that
1182: routine. It is the routine's responsibility to reclaim the
1183: storage occupied by the WorkItem structure.
1184:
1185: Return Value:
1186:
1187: TRUE if operation was successful. Otherwise returns FALSE and
1188: extended error information is available from GetLastError()
1189:
1190: --*/
1191:
1192: {
1193: BOOL Result;
1194:
1195: //
1196: // Acquire the work queue critical section and insert the work item
1197: // in the queue and release the semaphore if the work item is not
1198: // already in the list.
1199: //
1200:
1201: EnterCriticalSection( &WorkQueue->CriticalSection );
1202: Result = TRUE;
1203: try {
1.1.1.2 ! root 1204: WorkItem->WorkQueue = WorkQueue;
1.1 root 1205: InsertTailList( &WorkQueue->Queue, &WorkItem->List );
1206: Result = ReleaseSemaphore( WorkQueue->Semaphore, 1, NULL );
1207: }
1208: finally {
1209: LeaveCriticalSection( &WorkQueue->CriticalSection );
1210: }
1211:
1212: return Result;
1213: }
1214:
1215:
1216:
1217: DWORD
1218: WorkerThread(
1219: LPVOID lpThreadParameter
1220: )
1221: {
1.1.1.2 ! root 1222: PWORK_QUEUE WorkQueue = (PWORK_QUEUE)lpThreadParameter;
1.1 root 1223: DWORD rc;
1224: WORK_QUEUE_ITEM InitWorkItem;
1225: PWORK_QUEUE_ITEM WorkItem;
1226:
1227: //
1228: // Call the worker routine with an initialize work item
1229: // to give it a change to initialize some per thread
1230: // state that will passed to it for each subsequent
1231: // work item.
1232: //
1233:
1234: InitWorkItem.Reason = WORK_INITIALIZE_ITEM;
1235: (WorkQueue->WorkerRoutine)( &InitWorkItem );
1236: while( TRUE ) {
1.1.1.2 ! root 1237: try {
1.1 root 1238:
1.1.1.2 ! root 1239: //
! 1240: // Wait until something is put in the queue (semaphore is
! 1241: // released), remove the item from the queue, mark it not
! 1242: // inserted, and execute the specified routine.
! 1243: //
1.1 root 1244:
1.1.1.2 ! root 1245: rc = WaitForSingleObjectEx( WorkQueue->Semaphore, 0xFFFFFFFF, TRUE );
! 1246: if (rc == WAIT_IO_COMPLETION) {
! 1247: continue;
1.1 root 1248: }
1249:
1.1.1.2 ! root 1250: EnterCriticalSection( &WorkQueue->CriticalSection );
! 1251: try {
! 1252: if (WorkQueue->Terminating && IsListEmpty( &WorkQueue->Queue )) {
! 1253: break;
! 1254: }
1.1 root 1255:
1.1.1.2 ! root 1256: WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList( &WorkQueue->Queue );
! 1257: }
! 1258: finally {
! 1259: LeaveCriticalSection( &WorkQueue->CriticalSection );
! 1260: }
! 1261:
! 1262: //
! 1263: // Execute the worker routine for this work item.
! 1264: //
1.1 root 1265:
1266: (WorkQueue->WorkerRoutine)( WorkItem );
1267: }
1268: except( EXCEPTION_EXECUTE_HANDLER ) {
1.1.1.2 ! root 1269: //
! 1270: // Ignore any exceptions from worker routine.
! 1271: //
1.1 root 1272: }
1273: }
1274:
1275: InitWorkItem.Reason = WORK_TERMINATE_ITEM;
1276: (WorkQueue->WorkerRoutine)( &InitWorkItem );
1277:
1278: ExitThread( 0 );
1279: return 0; // This will exit this thread
1280: }
1281:
1282:
1283: BOOL
1284: CreateVirtualBuffer(
1285: OUT PVIRTUAL_BUFFER Buffer,
1286: IN DWORD CommitSize,
1287: IN DWORD ReserveSize OPTIONAL
1288: )
1289:
1290: /*++
1291:
1292: Routine Description:
1293:
1294: This function is called to create a virtual buffer. A virtual
1295: buffer is a contiguous range of virtual memory, where some initial
1296: prefix portion of the memory is committed and the remainder is only
1297: reserved virtual address space. A routine is provided to extend the
1298: size of the committed region incrementally or to trim the size of
1299: the committed region back to some specified amount.
1300:
1301: Arguments:
1302:
1303: Buffer - Pointer to the virtual buffer control structure that is
1304: filled in by this function.
1305:
1306: CommitSize - Size of the initial committed portion of the buffer.
1307: May be zero.
1308:
1309: ReserveSize - Amount of virtual address space to reserve for the
1310: buffer. May be zero, in which case amount reserved is the
1311: committed size plus one, rounded up to the next 64KB boundary.
1312:
1313: Return Value:
1314:
1315: TRUE if operation was successful. Otherwise returns FALSE and
1316: extended error information is available from GetLastError()
1317:
1318: --*/
1319:
1320: {
1321: SYSTEM_INFO SystemInformation;
1322:
1323: //
1324: // Query the page size from the system for rounding
1325: // our memory allocations.
1326: //
1327:
1328: GetSystemInfo( &SystemInformation );
1329: Buffer->PageSize = SystemInformation.dwPageSize;
1330:
1331: //
1332: // If the reserve size was not specified, default it by
1333: // rounding up the initial committed size to a 64KB
1334: // boundary. This is because the Win32 Virtual Memory
1335: // API calls always allocate virtual address space on
1336: // 64KB boundaries, so we might well have it available
1337: // for commitment.
1338: //
1339:
1340: if (!ARGUMENT_PRESENT( ReserveSize )) {
1341: ReserveSize = ROUND_UP( CommitSize + 1, 0x10000 );
1342: }
1343:
1344: //
1345: // Attempt to reserve the address space.
1346: //
1347:
1348: Buffer->Base = VirtualAlloc( NULL,
1349: ReserveSize,
1350: MEM_RESERVE,
1351: PAGE_READWRITE
1352: );
1353: if (Buffer->Base == NULL) {
1354: //
1355: // Unable to reserve address space, return failure.
1356: //
1357:
1358: return FALSE;
1359: }
1360:
1361: //
1362: // Attempt to commit some initial portion of the reserved region.
1363: //
1364: //
1365:
1366: CommitSize = ROUND_UP( CommitSize, Buffer->PageSize );
1367: if (CommitSize == 0 ||
1368: VirtualAlloc( Buffer->Base,
1369: CommitSize,
1370: MEM_COMMIT,
1371: PAGE_READWRITE
1372: ) != NULL
1373: ) {
1374: //
1375: // Either the size of the committed region was zero or the
1376: // commitment succeeded. In either case calculate the
1377: // address of the first byte after the committed region
1378: // and the address of the first byte after the reserved
1379: // region and return successs.
1380: //
1381:
1382: Buffer->CommitLimit = (LPVOID)
1383: ((char *)Buffer->Base + CommitSize);
1384:
1385: Buffer->ReserveLimit = (LPVOID)
1386: ((char *)Buffer->Base + ReserveSize);
1387:
1388: return TRUE;
1389: }
1390:
1391: //
1392: // If unable to commit the memory, release the virtual address
1393: // range allocated above and return failure.
1394: //
1395:
1396: VirtualFree( Buffer->Base, 0, MEM_RELEASE );
1397: return FALSE;
1398: }
1399:
1400:
1401:
1402: BOOL
1403: ExtendVirtualBuffer(
1404: IN PVIRTUAL_BUFFER Buffer,
1405: IN LPVOID Address
1406: )
1407:
1408: /*++
1409:
1410: Routine Description:
1411:
1412: This function is called to extend the committed portion of a virtual
1413: buffer.
1414:
1415: Arguments:
1416:
1417: Buffer - Pointer to the virtual buffer control structure.
1418:
1419: Address - Byte at this address is committed, along with all memory
1420: from the beginning of the buffer to this address. If the
1421: address is already within the committed portion of the virtual
1422: buffer, then this routine does nothing. If outside the reserved
1423: portion of the virtual buffer, then this routine returns an
1424: error.
1425:
1426: Otherwise enough pages are committed so that the memory from the
1427: base of the buffer to the passed address is a contiguous region
1428: of committed memory.
1429:
1430:
1431: Return Value:
1432:
1433: TRUE if operation was successful. Otherwise returns FALSE and
1434: extended error information is available from GetLastError()
1435:
1436: --*/
1437:
1438: {
1439: DWORD NewCommitSize;
1440: LPVOID NewCommitLimit;
1441:
1442: //
1443: // See if address is within the buffer.
1444: //
1445:
1446: if (Address >= Buffer->Base && Address < Buffer->ReserveLimit) {
1447: //
1448: // See if the address is within the committed portion of
1449: // the buffer. If so return success immediately.
1450: //
1451:
1452: if (Address < Buffer->CommitLimit) {
1453: return TRUE;
1454: }
1455:
1456: //
1457: // Address is within the reserved portion. Determine how many
1458: // bytes are between the address and the end of the committed
1459: // portion of the buffer. Round this size to a multiple of
1460: // the page size and this is the amount we will attempt to
1461: // commit.
1462: //
1463:
1464: NewCommitSize =
1465: ((DWORD)ROUND_UP( (DWORD)Address + 1, Buffer->PageSize ) -
1466: (DWORD)Buffer->CommitLimit
1467: );
1468:
1469: //
1470: // Attempt to commit the memory.
1471: //
1472:
1473: NewCommitLimit = VirtualAlloc( Buffer->CommitLimit,
1474: NewCommitSize,
1475: MEM_COMMIT,
1476: PAGE_READWRITE
1477: );
1478: if (NewCommitLimit != NULL) {
1479: //
1480: // Successful, so update the upper limit of the committed
1481: // region of the buffer and return success.
1482: //
1483:
1484: Buffer->CommitLimit = (LPVOID)
1485: ((DWORD)NewCommitLimit + NewCommitSize);
1486:
1487: return TRUE;
1488: }
1489: }
1490:
1491: //
1492: // Address is outside of the buffer, return failure.
1493: //
1494:
1495: return FALSE;
1496: }
1497:
1498:
1499: BOOL
1500: TrimVirtualBuffer(
1501: IN PVIRTUAL_BUFFER Buffer
1502: )
1503:
1504: /*++
1505:
1506: Routine Description:
1507:
1508: This function is called to decommit any memory that has been
1509: committed for this virtual buffer.
1510:
1511: Arguments:
1512:
1513: Buffer - Pointer to the virtual buffer control structure.
1514:
1515: Return Value:
1516:
1517: TRUE if operation was successful. Otherwise returns FALSE and
1518: extended error information is available from GetLastError()
1519:
1520: --*/
1521:
1522: {
1523: Buffer->CommitLimit = Buffer->Base;
1524: return VirtualFree( Buffer->Base, 0, MEM_DECOMMIT );
1525: }
1526:
1527:
1528:
1529: BOOL
1530: FreeVirtualBuffer(
1531: IN PVIRTUAL_BUFFER Buffer
1532: )
1533: /*++
1534:
1535: Routine Description:
1536:
1537: This function is called to free all the memory that is associated
1538: with this virtual buffer.
1539:
1540: Arguments:
1541:
1542: Buffer - Pointer to the virtual buffer control structure.
1543:
1544: Return Value:
1545:
1546: TRUE if operation was successful. Otherwise returns FALSE and
1547: extended error information is available from GetLastError()
1548:
1549: --*/
1550:
1551: {
1552: //
1553: // Decommit and release all virtual memory associated with
1554: // this virtual buffer.
1555: //
1556:
1557: return VirtualFree( Buffer->Base, 0, MEM_RELEASE );
1558: }
1559:
1560:
1561:
1562: int
1563: VirtualBufferExceptionFilter(
1564: IN DWORD ExceptionCode,
1565: IN PEXCEPTION_POINTERS ExceptionInfo,
1566: IN OUT PVIRTUAL_BUFFER Buffer
1567: )
1568:
1569: /*++
1570:
1571: Routine Description:
1572:
1573: This function is an exception filter that handles exceptions that
1574: referenced uncommitted but reserved memory contained in the passed
1575: virtual buffer. It this filter routine is able to commit the
1576: additional pages needed to allow the memory reference to succeed,
1577: then it will re-execute the faulting instruction. If it is unable
1578: to commit the pages, it will execute the callers exception handler.
1579:
1580: If the exception is not an access violation or is an access
1581: violation but does not reference memory contained in the reserved
1582: portion of the virtual buffer, then this filter passes the exception
1583: on up the exception chain.
1584:
1585: Arguments:
1586:
1587: ExceptionCode - Reason for the exception.
1588:
1589: ExceptionInfo - Information about the exception and the context
1590: that it occurred in.
1591:
1592: Buffer - Points to a virtual buffer control structure that defines
1593: the reserved memory region that is to be committed whenever an
1594: attempt is made to access it.
1595:
1596: Return Value:
1597:
1598: Exception disposition code that tells the exception dispatcher what
1599: to do with this exception. One of three values is returned:
1600:
1601: EXCEPTION_EXECUTE_HANDLER - execute the exception handler
1602: associated with the exception clause that called this filter
1603: procedure.
1604:
1605: EXCEPTION_CONTINUE_SEARCH - Continue searching for an exception
1606: handler to handle this exception.
1607:
1608: EXCEPTION_CONTINUE_EXECUTION - Dismiss this exception and return
1609: control to the instruction that caused the exception.
1610:
1611:
1612: --*/
1613:
1614: {
1615: LPVOID FaultingAddress;
1616:
1617: //
1618: // If this is an access violation touching memory within
1619: // our reserved buffer, but outside of the committed portion
1620: // of the buffer, then we are going to take this exception.
1621: //
1622:
1623: if (ExceptionCode == STATUS_ACCESS_VIOLATION) {
1624: //
1625: // Get the virtual address that caused the access violation
1626: // from the exception record. Determine if the address
1627: // references memory within the reserved but uncommitted
1628: // portion of the virtual buffer.
1629: //
1630:
1631: FaultingAddress = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ];
1632: if (FaultingAddress >= Buffer->CommitLimit &&
1633: FaultingAddress <= Buffer->ReserveLimit
1634: ) {
1635: //
1636: // This is our exception. Try to extend the buffer
1637: // to including the faulting address.
1638: //
1639:
1640: if (ExtendVirtualBuffer( Buffer, FaultingAddress )) {
1641: //
1642: // Buffer successfully extended, so re-execute the
1643: // faulting instruction.
1644: //
1645:
1.1.1.2 ! root 1646: return EXCEPTION_CONTINUE_EXECUTION;
1.1 root 1647: }
1648: else {
1649: //
1650: // Unable to extend the buffer. Stop searching
1651: // for exception handlers and execute the caller's
1652: // handler.
1653: //
1654:
1655: return EXCEPTION_EXECUTE_HANDLER;
1656: }
1657: }
1658: }
1659:
1660: //
1661: // Not an exception we care about, so pass it up the chain.
1662: //
1663:
1664: return EXCEPTION_CONTINUE_SEARCH;
1665: }
1666:
1667:
1668: BOOL
1669: EnumerateDirectoryTree(
1670: LPSTR DirectoryPath,
1671: PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
1672: PVOID EnumerateParameter
1673: )
1674:
1675: /*++
1676:
1677: Routine Description:
1678:
1679: This function walks a directory tree, depth first, calling the
1680: passed enumeration routine for each directory and file found
1681: in the tree. The enumeration routine is passed the full path
1682: of the file, the directory information associated with the file
1683: and an enumeration parameter that is uninterpreted by this
1684: function.
1685:
1686: Arguments:
1687:
1688: DirectoryPath - Absolute or relative path to the directory that
1689: will is the root of the tree to enumerate.
1690:
1691: EnumerateRoutine - Pointer to an enumeration routine to call
1692: for each file and directory found.
1693:
1694: EnumerateParameter - Uninterpreted 32-bit value that is passed
1695: to the EnumerationRoutine each time it is called.
1696:
1697: Return Value:
1698:
1699: TRUE if operation was successful. Otherwise returns FALSE and
1700: extended error information is available from GetLastError()
1701:
1702: --*/
1703:
1704: {
1705: BOOL Result;
1706: VIRTUAL_BUFFER Buffer;
1707: PENUMERATE_DIRECTORY_STATE State;
1708: PENUMERATE_DIRECTORY_STACK Stack;
1709: WIN32_FIND_DATA FindFileData;
1710:
1711: //
1712: // Create a virtual buffer with an initial committed size of
1713: // our directory state buffer, and a maximum reserved size of
1714: // the longest possible full path based on the maximum depth
1715: // we handle and the maximum length of each path component.
1716: //
1717:
1718: if (!CreateVirtualBuffer( &Buffer,
1719: sizeof( ENUMERATE_DIRECTORY_STATE ),
1720: sizeof( ENUMERATE_DIRECTORY_STATE ) +
1721: MAX_DEPTH * MAX_PATH
1722: )
1723: ) {
1724: return FALSE;
1725: }
1726:
1727: //
1728: // This buffer will be used to maintain a stack of directory
1729: // search handles, as well as accumulate the full path string
1730: // as we descend the directory tree.
1731: //
1732:
1733: State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
1734: State->Depth = 0;
1735: Stack = &State->Stack[ 0 ];
1736:
1737: //
1738: // Enter a try ... finally block so we can insure that we clean
1739: // up after ourselves on exit.
1740: //
1741:
1742: try {
1743: //
1744: // First translate the passed in DirectoryPath into a fully
1745: // qualified path. This path will be the initial value in
1746: // our path buffer. The initial allocation of the path buffer
1747: // is big enough for this initial request, so does not need
1748: // to be guarded by a try ... except clause.
1749: //
1750:
1751: if (GetFullPathName( DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd )) {
1752: //
1753: // Now enter a try ... except block that will be used to
1754: // manage the commitment of space in the path buffer as
1755: // we append subdirectory names and file names to it.
1756: // Using the virtual buffer allows us to handle full
1757: // path names up to 16KB in length, with an initial
1758: // allocation of 4KB.
1759: //
1760:
1761: try {
1762: //
1763: // Walk the directory tree. The outer loop is executed
1764: // once for each directory in the tree.
1765: //
1766:
1767: while (TRUE) {
1768: startDirectorySearch:
1769: //
1770: // Find the end of the current path, and make sure
1771: // there is a trailing path separator.
1772: //
1773:
1774: Stack->PathEnd = strchr( State->Path, '\0' );
1775: if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
1776: *(Stack->PathEnd)++ = '\\';
1777: }
1778:
1779: //
1780: // Now append the wild card specification that will
1781: // let us enumerate all the entries in this directory.
1782: // Call FindFirstFile to find the first entry in the
1783: // directory.
1784: //
1785:
1786: strcpy( Stack->PathEnd, "*.*" );
1787: Stack->FindHandle = FindFirstFile( State->Path,
1788: &FindFileData
1789: );
1790: if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
1791: //
1792: // Entry found. Now loop through the entire
1793: // directory processing each entry found,
1794: // including the first one.
1795: //
1796: do {
1797: //
1798: // Ignore bogus pseudo-directories that are
1799: // returned by some file systems (e.g. FAT).
1800: //
1801:
1802: if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
1803: (!strcmp( FindFileData.cFileName, "." ) ||
1804: !strcmp( FindFileData.cFileName, ".." )
1805: )
1806: ) {
1807: continue;
1808: }
1809:
1810: //
1811: // Copy the file name portion from the current
1812: // directory entry to the last component in the
1813: // path buffer.
1814: //
1815:
1816: strcpy( Stack->PathEnd, FindFileData.cFileName );
1817:
1818: //
1819: // Call the supplied enumeration routine with the
1820: // full path we have built up in the path buffer,
1821: // the directory information for this directory
1822: // entry and the supplied enumeration parameter.
1823: //
1824:
1825: (*EnumerateRoutine)( State->Path, &FindFileData, EnumerateParameter );
1826:
1827: //
1828: // If this is entry is a subdirectory, then it is
1829: // time to recurse. Do this by incrementing the
1830: // stack pointer and depth and jumping to the top
1831: // of the outer loop to process current contents
1832: // of the path buffer as a fully qualified name of
1833: // a directory.
1834: //
1835:
1836: if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1837: Stack++;
1838: State->Depth++;
1839: goto startDirectorySearch;
1840: restartDirectorySearch: ;
1841: }
1842:
1843: //
1844: // Here to find the next entry in the current directory.
1845: //
1846: }
1847:
1848: while ( FindNextFile( Stack->FindHandle, &FindFileData ) );
1849:
1850: //
1851: // No more entries in the current directory, so close
1852: // the search handle and fall into the code that will
1853: // pop our stack of directory seacrh handles.
1854:
1855: FindClose( Stack->FindHandle );
1856: }
1857:
1858: //
1859: // Here when done with a directory. See if we are pushed
1860: // inside another directory. If not, then we are done
1861: // enumerating the whole tree, so break out of the loop.
1862: //
1863:
1864: if (!State->Depth) {
1865: Result = TRUE;
1866: break;
1867: }
1868:
1869: //
1870: // We were pushed within another directory search,
1871: // so pop the stack to restore its search handle
1872: // and path buffer position and resume the search
1873: // within that directory.
1874:
1875: State->Depth--;
1876: --Stack;
1877: goto restartDirectorySearch;
1878: }
1879: }
1880:
1881: //
1882: // Any of the code that appends to the path buffer within
1883: // the above try ... except clause can cause an access
1884: // violation if the path buffer becomes longer than its
1885: // current committed size. This exception filter
1886: // will dynamically commit additional pages as needed
1887: // and resume execution.
1888: //
1889:
1890: except( VirtualBufferExceptionFilter( GetExceptionCode(),
1891: GetExceptionInformation(),
1892: &Buffer
1893: )
1894: ) {
1895: //
1896: // We will get here if the exception filter was unable to
1897: // commit the memory.
1898: //
1899:
1900: Result = FALSE;
1901: }
1902: }
1903: else {
1904: //
1905: // Initial GetFullPathName failed, so return a failure.
1906: //
1907:
1908: Result = FALSE;
1909: }
1910: }
1911: finally {
1912: //
1913: // Here on our way out of the outer try ... finally block.
1914: // Make sure all our search handles have been closed and then
1915: // free the virtual buffer. The only way this code is not
1916: // executed is if code within the try ... finally block
1917: // called ExitThread or ExitProcess, or an external thread
1918: // or process terminated this thread or process.
1919: //
1920: // In the case of process death, this is not a problem, because
1921: // process terminate closes all open handles attached to the process
1922: // and frees all private virtual memory that is part of the address
1923: // space of the process.
1924: //
1925: // In the case ot thread death, the code below is not executed if
1926: // the thread terminates via ExitThread in the context of the
1927: // try .. finally or if an external thread, either in this process
1928: // or another process called TerminateThread on this thread.
1929: //
1930:
1931: while (State->Depth--) {
1932: --Stack;
1933: FindClose( Stack->FindHandle );
1934: }
1935:
1936: FreeVirtualBuffer( &Buffer );
1937: }
1938:
1939: return Result;
1940: }
1941:
1942:
1943: BOOL
1944: ProcessCommandLineArguments(
1945: int argc,
1946: char *argv[]
1947: )
1948: {
1949: BOOL Result;
1950: LPSTR s;
1951:
1952: Result = FALSE;
1953: try {
1954: if (argc < 1) {
1955: return Result;
1956: }
1957:
1958: while (--argc) {
1959: s = *++argv;
1960: if (*s == '-' || *s == '/') {
1961: while (*++s) {
1962: switch( tolower( *s ) ) {
1963: case 'm':
1964: MappedFileIO = TRUE;
1965: break;
1966:
1967: case 'a':
1968: ASyncIO = TRUE;
1969: break;
1970:
1971: case 's':
1972: SyncIO = TRUE;
1973: break;
1974:
1975: case 'v':
1976: Verbose = TRUE;
1977: break;
1978:
1979: case 'y':
1980: IgnoreCase = TRUE;
1981: break;
1982:
1983: case 't':
1984: if (--argc) {
1985: NumberOfWorkerThreads = atoi( *++argv );
1986: if (NumberOfWorkerThreads > 0 && NumberOfWorkerThreads < 128) {
1987: break;
1988: }
1989: }
1990:
1991: // fall through if -t argument missing.
1992:
1993: case '?':
1994: case 'h':
1995: default:
1996: return Result;
1997: }
1998: }
1999: }
2000: else
2001: if (SearchString == NULL) {
2002: SearchString = s;
2003: }
2004: else
2005: if (DirectoryPath == NULL) {
2006: DirectoryPath = s;
2007: }
2008: else {
2009: return Result;
2010: }
2011: }
2012:
2013: if (SearchString == NULL) {
2014: return Result;
2015: }
2016:
2017: SearchStringLength = strlen( SearchString );
2018: if (SearchStringLength == 0) {
2019: return Result;
2020: }
2021:
2022: if (IgnoreCase) {
2023: SearchFunction = _strnicmp;
2024: }
2025: else {
2026: SearchFunction = strncmp;
2027: }
2028:
2029: if (DirectoryPath == NULL) {
2030: DirectoryPath = ".";
2031: }
2032:
2033: if (!(MappedFileIO || ASyncIO || SyncIO)) {
2034: MappedFileIO = TRUE;
2035: }
2036:
2037: if (Verbose) {
2038: fprintf( stderr, "Directory Tree: %s\n", DirectoryPath );
2039: fprintf( stderr, "Search String: '%s'\n", SearchString );
2040: fprintf( stderr, "Case %ssensitive\n", IgnoreCase ? "in" : "" );
2041: fprintf( stderr, "Number of Worker Threads: %u\n", NumberOfWorkerThreads );
2042: if (MappedFileIO) {
2043: fprintf( stderr, "Using Mapped File I/O\n" );
2044: }
2045: else
2046: if (ASyncIO) {
2047: fprintf( stderr, "Using ASynchronous File I/O\n" );
2048: }
2049: else
2050: if (MappedFileIO) {
2051: fprintf( stderr, "Using Synchronous File I/O\n" );
2052: }
2053: }
2054:
2055: Result = TRUE;
2056: return Result;
2057: }
2058: finally {
2059: if (!Result) {
2060: fprintf( stderr, "usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]\n" );
2061: fprintf( stderr, "Where...\n" );
2062: fprintf( stderr, " -h - prints this message\n" );
2063: fprintf( stderr, " -v - generates verbose output\n" );
2064: fprintf( stderr, " -y - ignores case when doing comparision\n" );
2065: fprintf( stderr, " -t - specifies the number of threads to use (defaults to 4 * number of processors)\n" );
2066: fprintf( stderr, " -a - uses asynchronous file I/O\n" );
2067: fprintf( stderr, " -s - uses synchronous file I/O\n" );
2068: fprintf( stderr, " -m - uses mapped file I/O (default)\n" );
2069: fprintf( stderr, " SearchString - specifies the text to search for\n" );
2070: fprintf( stderr, " DirectoryPath - specifies the directory to start from (defaults to .)\n" );
2071: }
2072: }
2073: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.