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.

777 lines
21 KiB

  1. #define MAX_DRIVES 64
  2. BOOL
  3. EnumerateAllDrivesT (
  4. IN FILEENUMPROCT fnEnumCallback,
  5. IN FILEENUMFAILPROCT fnFailCallback,
  6. IN DWORD EnumID,
  7. IN LPVOID pParam,
  8. IN PEXCLUDEINFT ExcludeInfStruct,
  9. IN DWORD AttributeFilter
  10. )
  11. /*++
  12. Routine Description:
  13. EnumerateAllDrives first builds an exclusion list if an exclusion INF path
  14. is provided, and then enumerates every file on every drive that is not
  15. excluded. The callback function is called once per file. The pParam
  16. parameter is passed to the callback.
  17. Arguments:
  18. fnEnumCallback - A pointer to your callback function
  19. EnumID - A caller-defined value used to identify the exclusion list
  20. pParam - LPVOID passed to callback function
  21. ExcludeInfStruct - Struct containing INF file information for excluding dirs or files
  22. AttributeFilter - FILTER_xxx constants
  23. Return Value:
  24. TRUE if function succeeds. Call GetLastError for error code if return
  25. value is FALSE.
  26. --*/
  27. {
  28. TCHAR LogicalDrives[MAX_DRIVES];
  29. DWORD rc;
  30. PCTSTR p;
  31. UINT driveType;
  32. rc = GetLogicalDriveStrings (
  33. MAX_DRIVES,
  34. LogicalDrives
  35. );
  36. if (!rc || rc > MAX_DRIVES) {
  37. if (rc)
  38. SetLastError (ERROR_OUTOFMEMORY);
  39. return FALSE;
  40. }
  41. for (p = LogicalDrives ; *p ; p = GetEndOfString (p) + 1) {
  42. driveType = GetDriveType(p);
  43. if (driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM) {
  44. continue;
  45. }
  46. if (!EnumerateTreeT (p,
  47. fnEnumCallback,
  48. fnFailCallback,
  49. EnumID,
  50. pParam,
  51. ENUM_ALL_LEVELS,
  52. ExcludeInfStruct,
  53. AttributeFilter
  54. ))
  55. break;
  56. }
  57. return (*p == 0);
  58. }
  59. BOOL
  60. EnumerateTreeT (
  61. IN PCTSTR EnumRoot,
  62. IN FILEENUMPROCT fnEnumCallback,
  63. IN FILEENUMFAILPROCT fnFailCallback, OPTIONAL
  64. IN DWORD EnumID,
  65. IN LPVOID pParam,
  66. IN DWORD Levels,
  67. IN PEXCLUDEINFT ExcludeInfStruct, OPTIONAL
  68. IN DWORD AttributeFilter
  69. )
  70. /*++
  71. Routine Description:
  72. EnumerateTree is similar to EnumerateAllDrives, except it allows you to
  73. enumerate a specific drive, or a specific subdir on a drive. Supply the
  74. drive letter and optional subdirectory in EnumRoot. Before enumerating,
  75. EnumerateTree will first build an exclusion list if an exclusion INF path
  76. is provided. Then every file below EnumRoot is enumerated, and the
  77. callback is called once per file, passing pParam unchanged.
  78. Arguments:
  79. EnumRoot - Drive and optional path to enumerate
  80. fnEnumCallback - A pointer to your callback function
  81. fnFailCallback - A pointer to optional fn that logs enumeration errors
  82. EnumID - A caller-defined value used to identify the exclusion list
  83. pParam - LPVOID passed to callback function
  84. ExcludeInfStruct - Struct containing INF file information for excluding dirs or files
  85. AttributeFilter - FILTER_xxx constants
  86. Return Value:
  87. TRUE if function succeeds. Call GetLastError for error code if return
  88. value is FALSE.
  89. --*/
  90. {
  91. ENUMSTRUCTT es;
  92. BOOL b;
  93. if (ExcludeInfStruct)
  94. if (!BuildExclusionsFromInfT (
  95. EnumID,
  96. ExcludeInfStruct
  97. )) {
  98. DEBUGMSG ((DBG_ERROR, "Error in exclusion file"));
  99. return FALSE;
  100. }
  101. es.fnEnumCallback = fnEnumCallback;
  102. es.fnFailCallback = fnFailCallback;
  103. es.EnumID = EnumID;
  104. es.pParam = pParam;
  105. es.Levels = Levels;
  106. es.CurrentLevel = 1;
  107. es.AttributeFilter = AttributeFilter;
  108. if (!IsPathLengthOk(EnumRoot))
  109. {
  110. if (NULL != fnFailCallback)
  111. {
  112. fnFailCallback(EnumRoot);
  113. return TRUE;
  114. }
  115. }
  116. if (IsPathExcludedT (EnumID, EnumRoot))
  117. return TRUE;
  118. b = EnumTreeEngineT (EnumRoot, &es);
  119. return b;
  120. }
  121. BOOL
  122. EnumTreeEngineT (
  123. PCTSTR CurrentPath,
  124. PENUMSTRUCTT pes
  125. )
  126. {
  127. WIN32_FIND_DATA fd; // A find struct for this subdir
  128. HANDLE hFind; // A find handle for this subdir
  129. PTSTR FullFilePath; // Buffer used to build file path
  130. static TCHAR FindPattern[MAX_TCHAR_PATH * 2]; // Temp buffer used to build pattern
  131. BYTE byBitmask[MAX_PATH]; // Bitmask is used to speed exclusion lookup
  132. static DWORD Attrib; // Temp attribute storage for filter processing
  133. static INT rc; // Callback return value
  134. DWORD PrevLevelCt; // Storage for parent's max depth setting
  135. BOOL RecurseStatus;
  136. DWORD CurrentDirData = 0;
  137. //
  138. // Do nothing when CurrentPath is at the size limit.
  139. //
  140. if (!IsPathLengthOk(CurrentPath))
  141. {
  142. if (NULL != pes->fnFailCallback)
  143. {
  144. pes->fnFailCallback(CurrentPath);
  145. }
  146. return TRUE;
  147. }
  148. PrevLevelCt = pes->Levels;
  149. StringCopy (FindPattern, CurrentPath);
  150. //
  151. // Create a bitmask that tells us when subdirectories match partial
  152. // file patterns.
  153. //
  154. ZeroMemory (byBitmask, sizeof (byBitmask));
  155. CreateBitmaskT (pes->EnumID, FindPattern, byBitmask);
  156. AppendPathWack (FindPattern);
  157. StringCat (FindPattern, TEXT("*"));
  158. hFind = FindFirstFile (FindPattern, &fd);
  159. if (hFind != INVALID_HANDLE_VALUE) {
  160. do {
  161. FullFilePath = JoinPaths (CurrentPath, fd.cFileName);
  162. __try {
  163. //
  164. // Ignore this path if FullFilePath is too long
  165. // this way fd.cFileName will surely be within limits (since it's shorter)
  166. //
  167. if (!IsPathLengthOk (FullFilePath)) {
  168. if (NULL != pes->fnFailCallback) {
  169. pes->fnFailCallback(FullFilePath);
  170. }
  171. continue;
  172. }
  173. // Filter directories named ".", "..". Set Attrib symbol.
  174. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  175. if (!StringCompare (fd.cFileName, TEXT(".")) ||
  176. !StringCompare (fd.cFileName, TEXT("..")))
  177. continue;
  178. Attrib = FILTER_DIRECTORIES;
  179. } else {
  180. Attrib = FILTER_FILES;
  181. }
  182. // Call the callback
  183. if (Attrib & pes->AttributeFilter) {
  184. rc = CALLBACK_CONTINUE;
  185. switch (Attrib) {
  186. case FILTER_DIRECTORIES:
  187. // Ignore excluded paths
  188. if (IsPathExcludedT (pes->EnumID, FullFilePath)) {
  189. break;
  190. }
  191. // Callback for 'directory first'
  192. if (!(pes->AttributeFilter & FILTER_DIRS_LAST)) {
  193. rc = pes->fnEnumCallback (
  194. FullFilePath,
  195. NULL,
  196. &fd,
  197. pes->EnumID,
  198. pes->pParam,
  199. &CurrentDirData
  200. );
  201. }
  202. if (rc >= CALLBACK_CONTINUE && pes->CurrentLevel != pes -> Levels) {
  203. // Recurse on directory
  204. pes->CurrentLevel++;
  205. RecurseStatus = EnumTreeEngineT (FullFilePath, pes);
  206. pes->CurrentLevel--;
  207. if (!RecurseStatus) {
  208. PushError();
  209. FindClose(hFind);
  210. PopError();
  211. return FALSE;
  212. }
  213. }
  214. // Callback for 'directory last'
  215. if (pes->AttributeFilter & FILTER_DIRS_LAST) {
  216. rc = pes->fnEnumCallback (
  217. FullFilePath,
  218. NULL,
  219. &fd,
  220. pes->EnumID,
  221. pes->pParam,
  222. &CurrentDirData
  223. );
  224. }
  225. break;
  226. case FILTER_FILES:
  227. if (!IsFileExcludedT (pes->EnumID, FullFilePath, byBitmask)) {
  228. rc = pes->fnEnumCallback (FullFilePath,
  229. NULL,
  230. &fd,
  231. pes->EnumID,
  232. pes->pParam,
  233. &CurrentDirData
  234. );
  235. }
  236. break;
  237. }
  238. if (rc == CALLBACK_FAILED) {
  239. PushError();
  240. FindClose (hFind);
  241. PopError();
  242. return FALSE;
  243. }
  244. else if (rc == CALLBACK_SUBDIR_DONE) {
  245. break;
  246. }
  247. else if (rc > 0) {
  248. pes->Levels = pes->CurrentLevel + rc;
  249. }
  250. }
  251. else if (Attrib == FILTER_DIRECTORIES && !IsPathExcludedT (pes->EnumID, FullFilePath)) {
  252. // Recurse on directory.
  253. if (pes->CurrentLevel != pes -> Levels) {
  254. pes->CurrentLevel++;
  255. RecurseStatus = EnumTreeEngineT (FullFilePath, pes);
  256. pes->CurrentLevel--;
  257. if (!RecurseStatus) {
  258. PushError();
  259. FindClose(hFind);
  260. PopError();
  261. return FALSE;
  262. }
  263. }
  264. }
  265. }
  266. __finally {
  267. FreePathString (FullFilePath);
  268. }
  269. } while (FindNextFile (hFind, &fd));
  270. FindClose (hFind);
  271. //
  272. // Test error code returned from FindNextFile
  273. //
  274. if (GetLastError() != ERROR_NO_MORE_FILES && GetLastError() != ERROR_SUCCESS)
  275. {
  276. //
  277. // Caller to handle not-ready message
  278. //
  279. if (GetLastError() != ERROR_NOT_READY)
  280. {
  281. DEBUGMSG((DBG_ERROR,
  282. "EnumTreeEngineT: Error from FindNextFile.\n"
  283. " FindPattern: %s\n"
  284. " Error: %u (%x)",
  285. FindPattern,
  286. GetLastError(),GetLastError()));
  287. }
  288. return FALSE;
  289. }
  290. SetLastError(ERROR_SUCCESS);
  291. }
  292. else {
  293. //
  294. // Test return codes from FindFirstFile
  295. //
  296. if (GetLastError () != ERROR_NO_MORE_FILES)
  297. {
  298. //
  299. // Caller to handle not-ready message
  300. //
  301. if (GetLastError() != ERROR_NOT_READY)
  302. {
  303. DEBUGMSG((DBG_WARNING,
  304. "EnumTreeEngineT: Warning from FindFirstFile.\n"
  305. " FindPattern: %s\n",
  306. FindPattern));
  307. }
  308. // return FALSE;
  309. }
  310. SetLastError (ERROR_SUCCESS);
  311. }
  312. // If a callback returned a positive, non-zero number, the depth
  313. // of the subdirectory search was limited for this level. Now that
  314. // this level is done, we must restore the depth value of our parent.
  315. pes->Levels = PrevLevelCt;
  316. return TRUE;
  317. }
  318. /*++
  319. A bitmask is used in IsFileExcluded for faster relative directory searches.
  320. Instead of looking in the MemDb for each part of the path, IsFileExcluded
  321. skips segments that are known not to match. We create the bitmask here
  322. by looking up each portion of FindPattern. Bit 1 is set if the last
  323. subdirectory exists in the file exclusion list, Bit 2 is set if the last
  324. two subdirectories exist in the file exclusion list, and so on.
  325. For example, assume FindPattern is set to C:\DEV\FOO\BAR. CreateBitmask
  326. first looks in the memory database for BAR\*, and if it is found bit 1 is set.
  327. Then CreateBitmask looks in the memory database for FOO\BAR\*, and sets bit
  328. 2. Again the function looks up DEV\FOO\BAR\* for bit 3 and finally
  329. C:\DEV\FOO\BAR\* for bit 4.
  330. Bit 0 is always set (empty paths always match).
  331. Once this bitmask is set up, IsFileExcluded can test only the patterns that
  332. are known to exist.
  333. --*/
  334. void
  335. CreateBitmaskT (
  336. DWORD EnumID,
  337. PCTSTR FindPattern,
  338. BYTE byBitmask[]
  339. )
  340. {
  341. TCHAR EnumPath[MAX_TCHAR_PATH * 2];
  342. TCHAR ShortPath[MAX_TCHAR_PATH * 2];
  343. PCTSTR p;
  344. PTSTR End;
  345. int nByte;
  346. int nBitVal;
  347. // Always set bit 0
  348. byBitmask[0] |= 1;
  349. // Build full file spec
  350. wsprintf (
  351. EnumPath,
  352. TEXT("%s\\%X\\%s\\"),
  353. MEMDB_CATEGORY_FILEENUM,
  354. EnumID,
  355. MEMDB_FIELD_FE_FILES
  356. );
  357. End = GetEndOfString (EnumPath);
  358. StringCopy (End, FindPattern);
  359. AppendPathWack (End);
  360. StringCat (End, TEXT("*"));
  361. // Start with last subdirectory, and build mask in reverse
  362. p = _tcsrchr (EnumPath, TEXT('\\'));
  363. nByte = 0;
  364. nBitVal = 2;
  365. do {
  366. // Move back to previous backslash
  367. for (p = _tcsdec (EnumPath, p) ;
  368. p >= End && *p != TEXT('\\') ;
  369. p = _tcsdec (EnumPath, p))
  370. {
  371. }
  372. // Check if partial file is in the tree
  373. wsprintf (
  374. ShortPath,
  375. TEXT("%s\\%X\\%s%s"),
  376. MEMDB_CATEGORY_FILEENUM,
  377. EnumID,
  378. MEMDB_FIELD_FE_FILES,
  379. p
  380. );
  381. if (MemDbGetPatternValueWithPattern (ShortPath, NULL))
  382. byBitmask[nByte] |= nBitVal;
  383. // Inc bit pos
  384. nBitVal *= 2;
  385. if (nBitVal == 256) {
  386. nBitVal = 1;
  387. nByte++;
  388. }
  389. } while (p > End);
  390. }
  391. BOOL
  392. IsPathExcludedT (DWORD EnumID, PCTSTR Path)
  393. {
  394. TCHAR EnumPath[MAX_TCHAR_PATH * 2];
  395. TCHAR ShortPath[MAX_TCHAR_PATH * 2];
  396. PCTSTR p;
  397. PTSTR End;
  398. // Try full paths
  399. wsprintf (
  400. EnumPath,
  401. TEXT("%s\\%X\\%s\\"),
  402. MEMDB_CATEGORY_FILEENUM,
  403. EnumID,
  404. MEMDB_FIELD_FE_PATHS
  405. );
  406. End = GetEndOfString (EnumPath);
  407. p = _tcsappend (End, Path);
  408. if (MemDbGetPatternValue (EnumPath, NULL)) {
  409. return TRUE;
  410. }
  411. // Try partial paths
  412. do {
  413. // Move back to previous backslash
  414. for (p = _tcsdec (EnumPath, p) ;
  415. p > End && (*p != TEXT('\\')) ;
  416. p = _tcsdec (EnumPath, p))
  417. {
  418. }
  419. // Check if partial path is in the tree
  420. if (p > End && p[1]) {
  421. wsprintf (
  422. ShortPath,
  423. TEXT("%s\\%X\\%s%s"),
  424. MEMDB_CATEGORY_FILEENUM,
  425. EnumID,
  426. MEMDB_FIELD_FE_PATHS,
  427. p
  428. );
  429. if (MemDbGetPatternValue (ShortPath, NULL)) {
  430. return TRUE;
  431. }
  432. }
  433. } while (p > End);
  434. return FALSE;
  435. }
  436. BOOL
  437. IsFileExcludedT (DWORD EnumID, PCTSTR File, BYTE byBitmask[])
  438. {
  439. TCHAR EnumPath[MAX_TCHAR_PATH * 2];
  440. TCHAR ShortPath[MAX_TCHAR_PATH * 2];
  441. PCTSTR p;
  442. PTSTR End;
  443. int nByte;
  444. int nBit;
  445. // Build full file spec
  446. wsprintf (
  447. EnumPath,
  448. TEXT("%s\\%X\\%s\\"),
  449. MEMDB_CATEGORY_FILEENUM,
  450. EnumID,
  451. MEMDB_FIELD_FE_FILES
  452. );
  453. End = GetEndOfString (EnumPath);
  454. p = _tcsappend (End, File);
  455. //
  456. // Try partial file specs until full spec is reached
  457. //
  458. nByte = 0;
  459. nBit = 1;
  460. do {
  461. //
  462. // Move back to previous backslash in path
  463. // (p starts at NULL of EnumPath, End is in the middle of EnumPath)
  464. //
  465. for (p = _tcsdec (EnumPath, p) ;
  466. p >= End && (*p != TEXT('\\')) ;
  467. p = _tcsdec (EnumPath, p))
  468. {
  469. }
  470. // Bitmask is used to make sure slightly expensive query is necessary
  471. if (byBitmask[nByte] & nBit) {
  472. //
  473. // Check if partial file is in the tree
  474. //
  475. wsprintf (
  476. ShortPath,
  477. TEXT("%s\\%X\\%s%s"),
  478. MEMDB_CATEGORY_FILEENUM,
  479. EnumID,
  480. MEMDB_FIELD_FE_FILES,
  481. p
  482. );
  483. if (MemDbGetPatternValue (ShortPath, NULL)) {
  484. return TRUE;
  485. }
  486. }
  487. nBit *= 2;
  488. if (nBit == 256) {
  489. nBit = 1;
  490. nByte++;
  491. }
  492. } while (p > End);
  493. return FALSE;
  494. }
  495. //
  496. // ClearExclusions removes all enumaration exclusions. It is called
  497. // automatically at the end of enumeration when an exclusion INF file is
  498. // used. Use it when you need to programmatically build an exclusion list
  499. // with ExcludeDrive, ExcludePath, and ExcludeFile.
  500. //
  501. // You can combine programmatic exclusions with an exclusion INF file, but
  502. // beware that the programmatic exclusions will be cleared after
  503. // EnumarteAllDrives or EnumerateTree completes.
  504. //
  505. // If you do not use an INF, the programmatic exclusions will not
  506. // automatically be cleared.
  507. //
  508. // EnumID - Caller-defined value to identify enumeration exclusion list
  509. //
  510. VOID
  511. ClearExclusionsT (
  512. DWORD EnumID
  513. )
  514. {
  515. TCHAR EnumPath[MAX_TCHAR_PATH * 2];
  516. wsprintf (EnumPath, TEXT("%s\\%X"), MEMDB_CATEGORY_FILEENUM, EnumID);
  517. MemDbDeleteTree (EnumPath);
  518. }
  519. /*++
  520. Routine Description:
  521. ExcludePath adds a path name to the exclusion list. There are two
  522. cases:
  523. 1. A full path spec is supplied, including the drive letter or
  524. UNC double-backslash.
  525. 2. The path does not start with a drive letter and is a portion of
  526. a full path.
  527. The dot and double-dot directories are not supported. Any part of
  528. the path may contain wildcard characters, but a wildcard can not
  529. be used in place of a backslash.
  530. Arguments:
  531. EnumID - A caller-defined value that identifies the exclusion list
  532. Path - The path specification as described above
  533. Return Value:
  534. none
  535. --*/
  536. VOID
  537. ExcludePathT (
  538. IN DWORD EnumID,
  539. IN PCTSTR Path
  540. )
  541. {
  542. TCHAR EnumPath[MAX_TCHAR_PATH * 2];
  543. wsprintf (
  544. EnumPath,
  545. TEXT("%s\\%X\\%s\\%s"),
  546. MEMDB_CATEGORY_FILEENUM,
  547. EnumID,
  548. MEMDB_FIELD_FE_PATHS,
  549. Path
  550. );
  551. MemDbSetValue (EnumPath, 0);
  552. }
  553. /*++
  554. Routine Description:
  555. ExcludeFile adds a file spec to the exclusion list. There are two
  556. cases:
  557. 1. A full path spec is supplied, including the drive letter or
  558. UNC double-backslash.
  559. 2. The path does not start with a drive letter and is a portion of
  560. a full path.
  561. The dot and double-dot directories are not supported. Any part of
  562. the path may contain wildcard characters, but a wildcard can not
  563. be used in place of a backslash.
  564. Arguments:
  565. EnumID - A caller-defined value that identifies the exclusion list
  566. File - The file specification as described above
  567. Return Value:
  568. none
  569. --*/
  570. VOID
  571. ExcludeFileT (
  572. IN DWORD EnumID,
  573. IN PCTSTR File
  574. )
  575. {
  576. TCHAR EnumPath[MAX_TCHAR_PATH * 2];
  577. wsprintf (
  578. EnumPath,
  579. TEXT("%s\\%X\\%s\\%s"),
  580. MEMDB_CATEGORY_FILEENUM,
  581. EnumID,
  582. MEMDB_FIELD_FE_FILES,
  583. File
  584. );
  585. MemDbSetValue (EnumPath, 0);
  586. }
  587. BOOL
  588. BuildExclusionsFromInfT (DWORD EnumID,
  589. PEXCLUDEINFT ExcludeInfStruct)
  590. {
  591. HINF hInf;
  592. INFCONTEXT ic;
  593. TCHAR Exclude[MAX_TCHAR_PATH * 2];
  594. // Attempt to open
  595. hInf = SetupOpenInfFile (ExcludeInfStruct->ExclusionInfPath, NULL, INF_STYLE_WIN4, NULL);
  596. if (hInf == INVALID_HANDLE_VALUE)
  597. return FALSE;
  598. // Read in path exclusions
  599. if (ExcludeInfStruct->PathSection) {
  600. if (SetupFindFirstLine (hInf, ExcludeInfStruct->PathSection, NULL, &ic)) {
  601. do {
  602. if (SetupGetStringField (&ic, 1, Exclude, MAX_TCHAR_PATH, NULL)) {
  603. ExcludePathT (EnumID, Exclude);
  604. }
  605. } while (SetupFindNextLine (&ic, &ic));
  606. }
  607. }
  608. // Read in file exclusions
  609. if (ExcludeInfStruct->FileSection) {
  610. if (SetupFindFirstLine (hInf, ExcludeInfStruct->FileSection, NULL, &ic)) {
  611. do {
  612. if (SetupGetStringField (&ic, 1, Exclude, MAX_TCHAR_PATH, NULL)) {
  613. ExcludeFileT (EnumID, Exclude);
  614. }
  615. } while (SetupFindNextLine (&ic, &ic));
  616. }
  617. }
  618. // Clean up
  619. SetupCloseInfFile (hInf);
  620. return TRUE;
  621. }