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.

292 lines
11 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. Enumdir.cpp
  5. Abstract:
  6. Public functions exposed by the directory
  7. enumeration class.
  8. Notes:
  9. Unicode only right now.
  10. History:
  11. 02/21/2001 rparsons Created
  12. --*/
  13. #include "enumdir.h"
  14. /*++
  15. Routine Description:
  16. This function walks a directory tree, depth first, calling the
  17. passed enumeration routine for each directory and file found
  18. in the tree. The enumeration routine is passed the full path
  19. of the file, the directory information associated with the file
  20. and an enumeration parameter that is uninterpreted by this
  21. function.
  22. Arguments:
  23. DirectoryPath - Absolute or relative path to the directory that
  24. will is the root of the tree to enumerate.
  25. EnumerateRoutine - Pointer to an enumeration routine to call
  26. for each file and directory found.
  27. fEnumSubDirs - Indicates if we should enumerate
  28. subdirectories.
  29. EnumerateParameter - Uninterpreted 32-bit value that is passed
  30. to the EnumerationRoutine each time it is called.
  31. Return Value:
  32. TRUE if operation was successful. Otherwise returns FALSE and
  33. extended error information is available from GetLastError()
  34. --*/
  35. BOOL
  36. CEnumDir::EnumerateDirectoryTree(
  37. IN LPCWSTR DirectoryPath,
  38. IN PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
  39. IN BOOL fEnumSubDirs,
  40. IN PVOID EnumerateParameter
  41. )
  42. {
  43. BOOL fResult;
  44. VIRTUAL_BUFFER Buffer;
  45. PENUMERATE_DIRECTORY_STATE State;
  46. PENUMERATE_DIRECTORY_STACK Stack;
  47. WIN32_FIND_DATA FindFileData;
  48. //
  49. // Create a virtual buffer with an initial committed size of
  50. // our directory state buffer, and a maximum reserved size of
  51. // the longest possible full path based on the maximum depth
  52. // we handle and the maximum length of each path component.
  53. //
  54. if (!this->CreateVirtualBuffer(&Buffer,
  55. sizeof(ENUMERATE_DIRECTORY_STATE),
  56. sizeof(ENUMERATE_DIRECTORY_STATE) +
  57. MAX_DEPTH * MAX_PATH)) {
  58. return FALSE;
  59. }
  60. //
  61. // This buffer will be used to maintain a stack of directory
  62. // search handles, as well as accumulate the full path string
  63. // as we descend the directory tree.
  64. //
  65. State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
  66. State->Depth = 0;
  67. Stack = &State->Stack[0];
  68. //
  69. // Enter a try ... finally block so we can insure that we clean
  70. // up after ourselves on exit.
  71. //
  72. __try {
  73. //
  74. // First translate the passed in DirectoryPath into a fully
  75. // qualified path. This path will be the initial value in
  76. // our path buffer. The initial allocation of the path buffer
  77. // is big enough for this initial request, so does not need
  78. // to be guarded by a try ... except clause.
  79. //
  80. if (GetFullPathName(DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd)) {
  81. //
  82. // Now enter a try ... except block that will be used to
  83. // manage the commitment of space in the path buffer as
  84. // we append subdirectory names and file names to it.
  85. // Using the virtual buffer allows us to handle full
  86. // path names up to 16KB in length, with an initial
  87. // allocation of 4KB.
  88. //
  89. __try {
  90. //
  91. // Walk the directory tree. The outer loop is executed
  92. // once for each directory in the tree.
  93. //
  94. while (TRUE) {
  95. startDirectorySearch:
  96. //
  97. // Find the end of the current path, and make sure
  98. // there is a trailing path separator.
  99. //
  100. Stack->PathEnd = wcschr( State->Path, '\0' );
  101. if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
  102. *(Stack->PathEnd)++ = '\\';
  103. }
  104. //
  105. // Now append the wild card specification that will
  106. // let us enumerate all the entries in this directory.
  107. // Call FindFirstFile to find the first entry in the
  108. // directory.
  109. //
  110. wcscpy( Stack->PathEnd, L"*.*" );
  111. Stack->FindHandle = FindFirstFile( State->Path,
  112. &FindFileData
  113. );
  114. if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
  115. //
  116. // Entry found. Now loop through the entire
  117. // directory processing each entry found,
  118. // including the first one.
  119. //
  120. do {
  121. //
  122. // Ignore bogus pseudo-directories that are
  123. // returned by some file systems (e.g. FAT).
  124. //
  125. if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
  126. (!wcscmp(FindFileData.cFileName, L".") ||
  127. !wcscmp(FindFileData.cFileName, L"..") ||
  128. !wcscmp(FindFileData.cFileName, L"System Volume Information" ) ||
  129. !wcscmp(FindFileData.cFileName, L"Recycler"))
  130. )
  131. {
  132. continue;
  133. }
  134. //
  135. // Copy the file name portion from the current
  136. // directory entry to the last component in the
  137. // path buffer.
  138. //
  139. wcscpy( Stack->PathEnd, FindFileData.cFileName);
  140. //
  141. // Call the supplied enumeration routine with the
  142. // full path we have built up in the path buffer,
  143. // the directory information for this directory
  144. // entry and the supplied enumeration parameter.
  145. //
  146. (*EnumerateRoutine)(State->Path, &FindFileData, EnumerateParameter);
  147. //
  148. // If this is entry is a subdirectory, then it is
  149. // time to recurse. Do this by incrementing the
  150. // stack pointer and depth and jumping to the top
  151. // of the outer loop to process current contents
  152. // of the path buffer as a fully qualified name of
  153. // a directory.
  154. //
  155. if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && fEnumSubDirs) {
  156. Stack++;
  157. State->Depth++;
  158. goto startDirectorySearch;
  159. restartDirectorySearch: ;
  160. }
  161. //
  162. // Here to find the next entry in the current directory.
  163. //
  164. }
  165. while (FindNextFile( Stack->FindHandle, &FindFileData));
  166. //
  167. // No more entries in the current directory, so close
  168. // the search handle and fall into the code that will
  169. // pop our stack of directory seacrh handles.
  170. //
  171. FindClose(Stack->FindHandle);
  172. }
  173. //
  174. // Here when done with a directory. See if we are pushed
  175. // inside another directory. If not, then we are done
  176. // enumerating the whole tree, so break out of the loop.
  177. //
  178. if (!State->Depth) {
  179. fResult = TRUE;
  180. break;
  181. }
  182. //
  183. // We were pushed within another directory search,
  184. // so pop the stack to restore its search handle
  185. // and path buffer position and resume the search
  186. // within that directory.
  187. //
  188. State->Depth--;
  189. --Stack;
  190. goto restartDirectorySearch;
  191. }
  192. }
  193. //
  194. // Any of the code that appends to the path buffer within
  195. // the above try ... except clause can cause an access
  196. // violation if the path buffer becomes longer than its
  197. // current committed size. This exception filter
  198. // will dynamically commit additional pages as needed
  199. // and resume execution.
  200. //
  201. _except( this->VirtualBufferExceptionFilter(GetExceptionCode(),
  202. GetExceptionInformation(),
  203. &Buffer)) {
  204. //
  205. // We will get here if the exception filter was unable to
  206. // commit the memory.
  207. //
  208. fResult = FALSE;
  209. }
  210. } else {
  211. //
  212. // Initial GetFullPathName failed, so return a failure.
  213. //
  214. fResult = FALSE;
  215. }
  216. }
  217. __finally {
  218. //
  219. // Here on our way out of the outer try ... finally block.
  220. // Make sure all our search handles have been closed and then
  221. // free the virtual buffer. The only way this code is not
  222. // executed is if code within the try ... finally block
  223. // called ExitThread or ExitProcess, or an external thread
  224. // or process terminated this thread or process.
  225. //
  226. // In the case of process death, this is not a problem, because
  227. // process terminate closes all open handles attached to the process
  228. // and frees all private virtual memory that is part of the address
  229. // space of the process.
  230. //
  231. // In the case ot thread death, the code below is not executed if
  232. // the thread terminates via ExitThread in the context of the
  233. // try .. finally or if an external thread, either in this process
  234. // or another process called TerminateThread on this thread.
  235. //
  236. while (State->Depth--) {
  237. --Stack;
  238. FindClose(Stack->FindHandle);
  239. }
  240. this->FreeVirtualBuffer(&Buffer);
  241. }
  242. return fResult;
  243. }