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.

2760 lines
76 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. fileenum.c
  5. Abstract:
  6. Implements a set of APIs to enumerate a file system using Win32 APIs.
  7. Author:
  8. 20-Oct-1999 Ovidiu Temereanca (ovidiut) - File creation.
  9. Revision History:
  10. <alias> <date> <comments>
  11. --*/
  12. #include "pch.h"
  13. //
  14. // Includes
  15. //
  16. // None
  17. #define DBG_FILEENUM "FileEnum"
  18. //
  19. // Strings
  20. //
  21. #define S_FILEENUM "FILEENUM"
  22. //
  23. // Constants
  24. //
  25. // None
  26. //
  27. // Macros
  28. //
  29. #define pFileAllocateMemory(Size) PmGetMemory (g_FileEnumPool,Size)
  30. #define pFileFreeMemory(Buffer) if (Buffer) PmReleaseMemory (g_FileEnumPool, (PVOID)Buffer)
  31. //
  32. // Types
  33. //
  34. // None
  35. //
  36. // Globals
  37. //
  38. PMHANDLE g_FileEnumPool;
  39. static INT g_FileEnumRefs;
  40. //
  41. // Macro expansion list
  42. //
  43. // None
  44. //
  45. // Private function prototypes
  46. //
  47. // None
  48. //
  49. // Macro expansion definition
  50. //
  51. // None
  52. //
  53. // Code
  54. //
  55. BOOL
  56. FileEnumInitialize (
  57. VOID
  58. )
  59. /*++
  60. Routine Description:
  61. FileEnumInitialize initializes this library.
  62. Arguments:
  63. none
  64. Return Value:
  65. TRUE if the init was successful.
  66. FALSE if not. GetLastError() returns extended error info.
  67. --*/
  68. {
  69. g_FileEnumRefs++;
  70. if (g_FileEnumRefs == 1) {
  71. g_FileEnumPool = PmCreateNamedPool (S_FILEENUM);
  72. }
  73. return g_FileEnumPool != NULL;
  74. }
  75. VOID
  76. FileEnumTerminate (
  77. VOID
  78. )
  79. /*++
  80. Routine Description:
  81. FileEnumTerminate is called to free resources used by this lib.
  82. Arguments:
  83. none
  84. Return Value:
  85. none
  86. --*/
  87. {
  88. MYASSERT (g_FileEnumRefs > 0);
  89. g_FileEnumRefs--;
  90. if (!g_FileEnumRefs) {
  91. if (g_FileEnumPool) {
  92. PmDestroyPool (g_FileEnumPool);
  93. g_FileEnumPool = NULL;
  94. }
  95. }
  96. }
  97. /*++
  98. Routine Description:
  99. EnumFirstDrive enumerates the first fixed drive root
  100. Arguments:
  101. DriveEnum - Receives info about the first fixed drive root
  102. Return Value:
  103. TRUE if a drive root was found; FALSE if not
  104. --*/
  105. BOOL
  106. EnumFirstDriveA (
  107. OUT PDRIVE_ENUMA DriveEnum,
  108. IN UINT WantedDriveTypes
  109. )
  110. {
  111. DWORD len;
  112. len = GetLogicalDriveStringsA (0, NULL);
  113. if (len) {
  114. DriveEnum->AllLogicalDrives = pFileAllocateMemory ((len + 1) * sizeof (CHAR));
  115. if (DriveEnum->AllLogicalDrives) {
  116. GetLogicalDriveStringsA (len, DriveEnum->AllLogicalDrives);
  117. DriveEnum->DriveName = NULL;
  118. DriveEnum->WantedDriveTypes = WantedDriveTypes;
  119. return EnumNextDriveA (DriveEnum);
  120. }
  121. }
  122. return FALSE;
  123. }
  124. BOOL
  125. EnumFirstDriveW (
  126. OUT PDRIVE_ENUMW DriveEnum,
  127. IN UINT WantedDriveTypes
  128. )
  129. {
  130. DWORD len;
  131. len = GetLogicalDriveStringsW (0, NULL);
  132. if (len) {
  133. DriveEnum->AllLogicalDrives = pFileAllocateMemory ((len + 1) * sizeof (WCHAR));
  134. if (DriveEnum->AllLogicalDrives) {
  135. GetLogicalDriveStringsW (len, DriveEnum->AllLogicalDrives);
  136. DriveEnum->DriveName = NULL;
  137. DriveEnum->WantedDriveTypes = WantedDriveTypes;
  138. return EnumNextDriveW (DriveEnum);
  139. }
  140. }
  141. return FALSE;
  142. }
  143. /*++
  144. Routine Description:
  145. EnumNextDrive enumerates the next fixed drive
  146. Arguments:
  147. DriveEnum - Specifies info about the previous fixed drive root; receives updated info
  148. Return Value:
  149. TRUE if a new drive root was found; FALSE if not
  150. --*/
  151. BOOL
  152. EnumNextDriveA (
  153. IN OUT PDRIVE_ENUMA DriveEnum
  154. )
  155. {
  156. do {
  157. if (!DriveEnum->DriveName) {
  158. DriveEnum->DriveName = DriveEnum->AllLogicalDrives;
  159. } else {
  160. // Since DriveEnum->DriveName is not NULL, GetEndOfStringA will
  161. // not return NULL so...
  162. DriveEnum->DriveName = GetEndOfStringA (DriveEnum->DriveName) + 1; //lint !e613
  163. }
  164. if (*DriveEnum->DriveName == 0) {
  165. AbortEnumDriveA (DriveEnum);
  166. return FALSE;
  167. }
  168. DriveEnum->DriveType = GetDriveTypeA (DriveEnum->DriveName);
  169. switch (DriveEnum->DriveType) {
  170. case DRIVE_UNKNOWN:
  171. DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
  172. break;
  173. case DRIVE_NO_ROOT_DIR:
  174. DriveEnum->DriveType = DRIVEENUM_NOROOTDIR;
  175. break;
  176. case DRIVE_REMOVABLE:
  177. DriveEnum->DriveType = DRIVEENUM_REMOVABLE;
  178. break;
  179. case DRIVE_FIXED:
  180. DriveEnum->DriveType = DRIVEENUM_FIXED;
  181. break;
  182. case DRIVE_REMOTE:
  183. DriveEnum->DriveType = DRIVEENUM_REMOTE;
  184. break;
  185. case DRIVE_CDROM:
  186. DriveEnum->DriveType = DRIVEENUM_CDROM;
  187. break;
  188. case DRIVE_RAMDISK:
  189. DriveEnum->DriveType = DRIVEENUM_RAMDISK;
  190. break;
  191. default:
  192. DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
  193. }
  194. } while (!(DriveEnum->DriveType & DriveEnum->WantedDriveTypes));
  195. return TRUE;
  196. }
  197. BOOL
  198. EnumNextDriveW (
  199. IN OUT PDRIVE_ENUMW DriveEnum
  200. )
  201. {
  202. do {
  203. if (!DriveEnum->DriveName) {
  204. DriveEnum->DriveName = DriveEnum->AllLogicalDrives;
  205. } else {
  206. DriveEnum->DriveName = GetEndOfStringW (DriveEnum->DriveName) + 1;
  207. }
  208. if (*DriveEnum->DriveName == 0) {
  209. AbortEnumDriveW (DriveEnum);
  210. return FALSE;
  211. }
  212. DriveEnum->DriveType = GetDriveTypeW (DriveEnum->DriveName);
  213. switch (DriveEnum->DriveType) {
  214. case DRIVE_UNKNOWN:
  215. DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
  216. break;
  217. case DRIVE_NO_ROOT_DIR:
  218. DriveEnum->DriveType = DRIVEENUM_NOROOTDIR;
  219. break;
  220. case DRIVE_REMOVABLE:
  221. DriveEnum->DriveType = DRIVEENUM_REMOVABLE;
  222. break;
  223. case DRIVE_FIXED:
  224. DriveEnum->DriveType = DRIVEENUM_FIXED;
  225. break;
  226. case DRIVE_REMOTE:
  227. DriveEnum->DriveType = DRIVEENUM_REMOTE;
  228. break;
  229. case DRIVE_CDROM:
  230. DriveEnum->DriveType = DRIVEENUM_CDROM;
  231. break;
  232. case DRIVE_RAMDISK:
  233. DriveEnum->DriveType = DRIVEENUM_RAMDISK;
  234. break;
  235. default:
  236. DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
  237. }
  238. } while (!(DriveEnum->DriveType & DriveEnum->WantedDriveTypes));
  239. return TRUE;
  240. }
  241. /*++
  242. Routine Description:
  243. AbortEnumDrive aborts enumeration of fixed drives
  244. Arguments:
  245. DriveEnum - Specifies info about the previous fixed drive;
  246. receives a "clean" context
  247. Return Value:
  248. none
  249. --*/
  250. VOID
  251. AbortEnumDriveA (
  252. IN OUT PDRIVE_ENUMA DriveEnum
  253. )
  254. {
  255. if (DriveEnum->AllLogicalDrives) {
  256. pFileFreeMemory (DriveEnum->AllLogicalDrives);
  257. DriveEnum->AllLogicalDrives = NULL;
  258. }
  259. }
  260. VOID
  261. AbortEnumDriveW (
  262. IN OUT PDRIVE_ENUMW DriveEnum
  263. )
  264. {
  265. if (DriveEnum->AllLogicalDrives) {
  266. pFileFreeMemory (DriveEnum->AllLogicalDrives);
  267. DriveEnum->AllLogicalDrives = NULL;
  268. }
  269. }
  270. /*++
  271. Routine Description:
  272. pGetFileEnumInfo is a private function that validates and translates the enumeration info
  273. in an internal form that's more accessible to the enum routines
  274. Arguments:
  275. FileEnumInfo - Receives the enum info
  276. EncodedPathPattern - Specifies the encoded dir pattern (encoded as defined by the
  277. ParsedPattern functions)
  278. EnumDirs - Specifies TRUE if directories should be returned during the enumeration
  279. (if they match the pattern); a directory is returned before any of its
  280. subdirs or files
  281. ContainersFirst - Specifies TRUE if directories should be returned before any of its
  282. files or subdirs; used only if EnumDirs is TRUE
  283. FilesFirst - Specifies TRUE if a dir's files should be returned before dir's subdirs;
  284. this parameter decides the enum order between files and subdirs
  285. for each directory
  286. DepthFirst - Specifies TRUE if the current subdir of any dir should be fully enumerated
  287. before going to the next subdir; this parameter decides if the tree
  288. traversal is depth-first (TRUE) or width-first (FALSE)
  289. MaxSubLevel - Specifies the maximum sub-level of a dir that is to be enumerated, relative to
  290. the root; if -1, all sub-levels are enumerated
  291. UseExclusions - Specifies TRUE if exclusion APIs should be used to determine if certain
  292. paths/files are excluded from enumeration; this slows down the speed
  293. Return Value:
  294. TRUE if all params are valid; in this case, FileEnumInfo is filled with the corresponding
  295. info.
  296. FALSE otherwise.
  297. --*/
  298. BOOL
  299. pGetFileEnumInfoA (
  300. OUT PFILEENUMINFOA FileEnumInfo,
  301. IN PCSTR EncodedPathPattern,
  302. IN BOOL EnumDirs,
  303. IN BOOL ContainersFirst,
  304. IN BOOL FilesFirst,
  305. IN BOOL DepthFirst,
  306. IN DWORD MaxSubLevel,
  307. IN BOOL UseExclusions
  308. )
  309. {
  310. FileEnumInfo->PathPattern = ObsCreateParsedPatternA (EncodedPathPattern);
  311. if (!FileEnumInfo->PathPattern) {
  312. DEBUGMSGA ((DBG_ERROR, "pGetFileEnumInfoA: bad EncodedPathPattern: %s", EncodedPathPattern));
  313. return FALSE;
  314. }
  315. //
  316. // check for empty filename; no filename will match in this case
  317. //
  318. if (FileEnumInfo->PathPattern->Leaf && *FileEnumInfo->PathPattern->Leaf == 0) {
  319. DEBUGMSGA ((
  320. DBG_ERROR,
  321. "pGetFileEnumInfoA: empty filename pattern specified in EncodedPathPattern: %s",
  322. EncodedPathPattern
  323. ));
  324. ObsDestroyParsedPatternA (FileEnumInfo->PathPattern);
  325. FileEnumInfo->PathPattern = NULL;
  326. return FALSE;
  327. }
  328. if (FileEnumInfo->PathPattern->ExactRoot) {
  329. if (!GetNodePatternMinMaxLevelsA (
  330. FileEnumInfo->PathPattern->ExactRoot,
  331. NULL,
  332. &FileEnumInfo->RootLevel,
  333. NULL
  334. )) {
  335. return FALSE;
  336. }
  337. } else {
  338. FileEnumInfo->RootLevel = 1;
  339. }
  340. if (!FileEnumInfo->PathPattern->LeafPattern) {
  341. //
  342. // no file pattern specified; assume only directory names will be returned
  343. // overwrite caller's setting
  344. //
  345. DEBUGMSGA ((
  346. DBG_FILEENUM,
  347. "pGetFileEnumInfoA: no filename pattern specified; forcing EnumDirs to TRUE"
  348. ));
  349. EnumDirs = TRUE;
  350. }
  351. if (EnumDirs) {
  352. FileEnumInfo->Flags |= FEIF_RETURN_DIRS;
  353. }
  354. if (ContainersFirst) {
  355. FileEnumInfo->Flags |= FEIF_CONTAINERS_FIRST;
  356. }
  357. if (FilesFirst) {
  358. FileEnumInfo->Flags |= FEIF_FILES_FIRST;
  359. }
  360. if (DepthFirst) {
  361. FileEnumInfo->Flags |= FEIF_DEPTH_FIRST;
  362. }
  363. if (UseExclusions) {
  364. FileEnumInfo->Flags |= FEIF_USE_EXCLUSIONS;
  365. }
  366. FileEnumInfo->MaxSubLevel = min (MaxSubLevel, FileEnumInfo->PathPattern->MaxSubLevel);
  367. return TRUE;
  368. }
  369. BOOL
  370. pGetFileEnumInfoW (
  371. OUT PFILEENUMINFOW FileEnumInfo,
  372. IN PCWSTR EncodedPathPattern,
  373. IN BOOL EnumDirs,
  374. IN BOOL ContainersFirst,
  375. IN BOOL FilesFirst,
  376. IN BOOL DepthFirst,
  377. IN DWORD MaxSubLevel,
  378. IN BOOL UseExclusions
  379. )
  380. {
  381. FileEnumInfo->PathPattern = ObsCreateParsedPatternW (EncodedPathPattern);
  382. if (!FileEnumInfo->PathPattern) {
  383. DEBUGMSGW ((DBG_ERROR, "pGetFileEnumInfoW: bad EncodedPathPattern: %s", EncodedPathPattern));
  384. return FALSE;
  385. }
  386. //
  387. // check for empty filename; no filename will match in this case
  388. //
  389. if (FileEnumInfo->PathPattern->Leaf && *FileEnumInfo->PathPattern->Leaf == 0) {
  390. DEBUGMSGW ((
  391. DBG_ERROR,
  392. "pGetFileEnumInfoW: empty filename pattern specified in EncodedPathPattern: %s",
  393. EncodedPathPattern
  394. ));
  395. ObsDestroyParsedPatternW (FileEnumInfo->PathPattern);
  396. FileEnumInfo->PathPattern = NULL;
  397. return FALSE;
  398. }
  399. if (FileEnumInfo->PathPattern->ExactRoot) {
  400. if (!GetNodePatternMinMaxLevelsW (
  401. FileEnumInfo->PathPattern->ExactRoot,
  402. NULL,
  403. &FileEnumInfo->RootLevel,
  404. NULL
  405. )) {
  406. return FALSE;
  407. }
  408. } else {
  409. FileEnumInfo->RootLevel = 1;
  410. }
  411. if (!FileEnumInfo->PathPattern->LeafPattern) {
  412. //
  413. // no file pattern specified; assume only directory names will be returned
  414. // overwrite caller's setting
  415. //
  416. DEBUGMSGW ((
  417. DBG_FILEENUM,
  418. "pGetFileEnumInfoW: no filename pattern specified; forcing EnumDirs to TRUE"
  419. ));
  420. EnumDirs = TRUE;
  421. }
  422. if (EnumDirs) {
  423. FileEnumInfo->Flags |= FEIF_RETURN_DIRS;
  424. }
  425. if (ContainersFirst) {
  426. FileEnumInfo->Flags |= FEIF_CONTAINERS_FIRST;
  427. }
  428. if (FilesFirst) {
  429. FileEnumInfo->Flags |= FEIF_FILES_FIRST;
  430. }
  431. if (DepthFirst) {
  432. FileEnumInfo->Flags |= FEIF_DEPTH_FIRST;
  433. }
  434. if (UseExclusions) {
  435. FileEnumInfo->Flags |= FEIF_USE_EXCLUSIONS;
  436. }
  437. FileEnumInfo->MaxSubLevel = min (MaxSubLevel, FileEnumInfo->PathPattern->MaxSubLevel);
  438. return TRUE;
  439. }
  440. /*++
  441. Routine Description:
  442. pGetCurrentDirNode returns the current dir node to be enumerated, based on DepthFirst flag
  443. Arguments:
  444. FileEnum - Specifies the context
  445. LastCreated - Specifies TRUE if the last created node is to be retrieved, regardless of
  446. DepthFirst flag
  447. Return Value:
  448. The current node if any or NULL if none remaining.
  449. --*/
  450. PDIRNODEA
  451. pGetCurrentDirNodeA (
  452. IN PFILETREE_ENUMA FileEnum,
  453. IN BOOL LastCreated
  454. )
  455. {
  456. PGROWBUFFER gb = &FileEnum->FileNodes;
  457. if (!gb->Buf || gb->End - gb->UserIndex < DWSIZEOF (DIRNODEA)) {
  458. return NULL;
  459. }
  460. if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
  461. return (PDIRNODEA)(gb->Buf + gb->End) - 1;
  462. } else {
  463. return (PDIRNODEA)(gb->Buf + gb->UserIndex);
  464. }
  465. }
  466. PDIRNODEW
  467. pGetCurrentDirNodeW (
  468. IN PFILETREE_ENUMW FileEnum,
  469. IN BOOL LastCreated
  470. )
  471. {
  472. PGROWBUFFER gb = &FileEnum->FileNodes;
  473. if (gb->End - gb->UserIndex < DWSIZEOF (DIRNODEW)) {
  474. return NULL;
  475. }
  476. if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
  477. return (PDIRNODEW)(gb->Buf + gb->End) - 1;
  478. } else {
  479. return (PDIRNODEW)(gb->Buf + gb->UserIndex);
  480. }
  481. }
  482. /*++
  483. Routine Description:
  484. pDeleteDirNode frees the resources associated with the current dir node and destroys it
  485. Arguments:
  486. FileEnum - Specifies the context
  487. LastCreated - Specifies TRUE if the last created node is to be deleted, regardless of
  488. DepthFirst flag
  489. Return Value:
  490. TRUE if there was a node to delete, FALSE if no more nodes
  491. --*/
  492. BOOL
  493. pDeleteDirNodeA (
  494. IN OUT PFILETREE_ENUMA FileEnum,
  495. IN BOOL LastCreated
  496. )
  497. {
  498. PDIRNODEA dirNode;
  499. PGROWBUFFER gb = &FileEnum->FileNodes;
  500. dirNode = pGetCurrentDirNodeA (FileEnum, LastCreated);
  501. if (!dirNode) {
  502. return FALSE;
  503. }
  504. if (dirNode->DirName) {
  505. FreeTextExA (g_FileEnumPool, dirNode->DirName);
  506. }
  507. if (dirNode->FindHandle) {
  508. FindClose (dirNode->FindHandle);
  509. dirNode->FindHandle = NULL;
  510. }
  511. if (FileEnum->LastNode == dirNode) {
  512. FileEnum->LastNode = NULL;
  513. }
  514. //
  515. // delete node
  516. //
  517. if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
  518. gb->End -= DWSIZEOF (DIRNODEA);
  519. } else {
  520. gb->UserIndex += DWSIZEOF (DIRNODEA);
  521. //
  522. // shift list
  523. //
  524. if (gb->Size - gb->End < DWSIZEOF (DIRNODEA)) {
  525. MoveMemory (gb->Buf, gb->Buf + gb->UserIndex, gb->End - gb->UserIndex);
  526. gb->End -= gb->UserIndex;
  527. gb->UserIndex = 0;
  528. }
  529. }
  530. return TRUE;
  531. }
  532. BOOL
  533. pDeleteDirNodeW (
  534. IN OUT PFILETREE_ENUMW FileEnum,
  535. IN BOOL LastCreated
  536. )
  537. {
  538. PDIRNODEW dirNode;
  539. PGROWBUFFER gb = &FileEnum->FileNodes;
  540. dirNode = pGetCurrentDirNodeW (FileEnum, LastCreated);
  541. if (!dirNode) {
  542. return FALSE;
  543. }
  544. if (dirNode->DirName) {
  545. FreeTextExW (g_FileEnumPool, dirNode->DirName);
  546. }
  547. if (dirNode->FindHandle) {
  548. FindClose (dirNode->FindHandle);
  549. dirNode->FindHandle = NULL;
  550. }
  551. if (FileEnum->LastNode == dirNode) {
  552. FileEnum->LastNode = NULL;
  553. }
  554. //
  555. // delete node
  556. //
  557. if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
  558. gb->End -= DWSIZEOF (DIRNODEW);
  559. } else {
  560. gb->UserIndex += DWSIZEOF (DIRNODEW);
  561. //
  562. // shift list
  563. //
  564. if (gb->Size - gb->End < DWSIZEOF (DIRNODEW)) {
  565. MoveMemory (gb->Buf, gb->Buf + gb->UserIndex, gb->End - gb->UserIndex);
  566. gb->End -= gb->UserIndex;
  567. gb->UserIndex = 0;
  568. }
  569. }
  570. return TRUE;
  571. }
  572. /*++
  573. Routine Description:
  574. pCreateDirNode creates a new node given a context, a dir name or a parent node
  575. Arguments:
  576. FileEnum - Specifies the context
  577. DirName - Specifies the dir name of the new node; may be NULL only if ParentNode is not NULL
  578. ParentNode - Specifies a pointer to the parent node of the new node; a pointer to the node
  579. is required because the parent node location in memory may change as a result
  580. of the growbuffer changing buffer location when it grows;
  581. may be NULL only if DirName is not;
  582. Ignore - Receives a meaningful value only if NULL is returned (no node created);
  583. if TRUE upon return, the failure of node creation should be ignored
  584. Return Value:
  585. A pointer to the new node or NULL if no node was created
  586. --*/
  587. PDIRNODEA
  588. pCreateDirNodeA (
  589. IN OUT PFILETREE_ENUMA FileEnum,
  590. IN PCSTR DirName, OPTIONAL
  591. IN PDIRNODEA* ParentNode, OPTIONAL
  592. OUT PBOOL Ignore OPTIONAL
  593. )
  594. {
  595. PDIRNODEA newNode;
  596. PSTR newDirName;
  597. PSEGMENTA FirstSegment;
  598. LONG offset = 0;
  599. if (DirName) {
  600. newDirName = DuplicateTextExA (g_FileEnumPool, DirName, 0, NULL);
  601. RemoveWackAtEndA (newDirName);
  602. } else {
  603. MYASSERT (ParentNode);
  604. newDirName = JoinPathsInPoolExA ((
  605. g_FileEnumPool,
  606. (*ParentNode)->DirName,
  607. (*ParentNode)->FindData.cFileName,
  608. NULL
  609. ));
  610. //
  611. // check if this starting path may match the pattern before continuing
  612. //
  613. if (FileEnum->FileEnumInfo.PathPattern->NodePattern) {
  614. FirstSegment = FileEnum->FileEnumInfo.PathPattern->NodePattern->Pattern->Segment;
  615. } else {
  616. FreeTextExA (g_FileEnumPool, newDirName);
  617. if (Ignore) {
  618. *Ignore = TRUE;
  619. }
  620. return NULL;
  621. }
  622. if ((FirstSegment->Type == SEGMENTTYPE_EXACTMATCH) &&
  623. (!StringIMatchByteCountA (
  624. FirstSegment->Exact.LowerCasePhrase,
  625. newDirName,
  626. FirstSegment->Exact.PhraseBytes
  627. ))
  628. ) {
  629. DEBUGMSGA ((
  630. DBG_FILEENUM,
  631. "Skipping tree %s\\* because it cannot match the pattern",
  632. newDirName
  633. ));
  634. FreeTextExA (g_FileEnumPool, newDirName);
  635. if (Ignore) {
  636. *Ignore = TRUE;
  637. }
  638. return NULL;
  639. }
  640. }
  641. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  642. //
  643. // look if this dir and the whole subtree are excluded; if so, soft block creation of node
  644. //
  645. if (ElIsTreeExcluded2A (ELT_FILE, newDirName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
  646. DEBUGMSGA ((
  647. DBG_FILEENUM,
  648. "Skipping tree %s\\%s because it's excluded",
  649. newDirName,
  650. FileEnum->FileEnumInfo.PathPattern->Leaf
  651. ));
  652. FreeTextExA (g_FileEnumPool, newDirName);
  653. if (Ignore) {
  654. *Ignore = TRUE;
  655. }
  656. return NULL;
  657. }
  658. }
  659. if (ParentNode) {
  660. //
  661. // remember current offset
  662. //
  663. offset = (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf);
  664. }
  665. //
  666. // allocate space for the new node in the growbuffer
  667. //
  668. newNode = (PDIRNODEA) GbGrow (&FileEnum->FileNodes, DWSIZEOF (DIRNODEA));
  669. if (!newNode) {
  670. FreeTextExA (g_FileEnumPool, newDirName);
  671. goto fail;
  672. }
  673. if (ParentNode) {
  674. //
  675. // check if the buffer moved
  676. //
  677. if (offset != (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf)) {
  678. //
  679. // adjust the parent position
  680. //
  681. *ParentNode = (PDIRNODEA)(FileEnum->FileNodes.Buf + offset);
  682. }
  683. }
  684. //
  685. // initialize the newly created node
  686. //
  687. ZeroMemory (newNode, DWSIZEOF (DIRNODEA));
  688. newNode->DirName = newDirName;
  689. if (DirName) {
  690. newNode->DirAttributes = GetFileAttributesA (DirName);
  691. //
  692. // roots are not returned from enumeration because DNF_RETURN_DIRNAME is not set here
  693. //
  694. if ((FileEnum->FileEnumInfo.PathPattern->Leaf == NULL) &&
  695. (FileEnum->FileEnumInfo.PathPattern->ExactRoot) &&
  696. (!WildCharsPatternA (FileEnum->FileEnumInfo.PathPattern->NodePattern))
  697. ) {
  698. newNode->Flags |= DNF_RETURN_DIRNAME;
  699. }
  700. } else {
  701. MYASSERT (ParentNode);
  702. //ParentNode is not NULL (see the assert above) so...
  703. newNode->DirAttributes = (*ParentNode)->FindData.dwFileAttributes; //lint !e613
  704. newNode->Flags |= DNF_RETURN_DIRNAME;
  705. }
  706. newNode->EnumState = DNS_ENUM_INIT;
  707. if ((FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTNODE | OBSPF_NODEISROOTPLUSSTAR)) ||
  708. TestParsedPatternA (FileEnum->FileEnumInfo.PathPattern->NodePattern, newDirName)
  709. ) {
  710. newNode->Flags |= DNF_DIRNAME_MATCHES;
  711. }
  712. if (ParentNode) {
  713. newNode->SubLevel = (*ParentNode)->SubLevel + 1;
  714. } else {
  715. newNode->SubLevel = 0;
  716. }
  717. return newNode;
  718. fail:
  719. if (Ignore) {
  720. if (FileEnum->FileEnumInfo.CallbackOnError) {
  721. *Ignore = (*FileEnum->FileEnumInfo.CallbackOnError)(newNode);
  722. } else {
  723. *Ignore = FALSE;
  724. }
  725. }
  726. return NULL;
  727. }
  728. PDIRNODEW
  729. pCreateDirNodeW (
  730. IN OUT PFILETREE_ENUMW FileEnum,
  731. IN PCWSTR DirName, OPTIONAL
  732. IN PDIRNODEW* ParentNode, OPTIONAL
  733. OUT PBOOL Ignore OPTIONAL
  734. )
  735. {
  736. PDIRNODEW newNode;
  737. PWSTR newDirName;
  738. PSEGMENTW FirstSegment;
  739. LONG offset = 0;
  740. if (DirName) {
  741. newDirName = DuplicateTextExW (g_FileEnumPool, DirName, 0, NULL);
  742. RemoveWackAtEndW (newDirName);
  743. } else {
  744. MYASSERT (ParentNode);
  745. newDirName = JoinPathsInPoolExW ((
  746. g_FileEnumPool,
  747. (*ParentNode)->DirName,
  748. (*ParentNode)->FindData.cFileName,
  749. NULL
  750. ));
  751. //
  752. // check if this starting path may match the pattern before continuing
  753. //
  754. if (FileEnum->FileEnumInfo.PathPattern->NodePattern) {
  755. FirstSegment = FileEnum->FileEnumInfo.PathPattern->NodePattern->Pattern->Segment;
  756. } else {
  757. FreeTextExW (g_FileEnumPool, newDirName);
  758. if (Ignore) {
  759. *Ignore = TRUE;
  760. }
  761. return NULL;
  762. }
  763. if ((FirstSegment->Type == SEGMENTTYPE_EXACTMATCH) &&
  764. (!StringIMatchByteCountW (
  765. FirstSegment->Exact.LowerCasePhrase,
  766. newDirName,
  767. FirstSegment->Exact.PhraseBytes
  768. ))
  769. ) { //lint !e64
  770. DEBUGMSGW ((
  771. DBG_FILEENUM,
  772. "Skipping tree %s\\* because it cannot match the pattern",
  773. newDirName
  774. ));
  775. FreeTextExW (g_FileEnumPool, newDirName);
  776. if (Ignore) {
  777. *Ignore = TRUE;
  778. }
  779. return NULL;
  780. }
  781. }
  782. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  783. //
  784. // look if this dir and the whole subtree are excluded; if so, soft block creation of node
  785. //
  786. if (ElIsTreeExcluded2W (ELT_FILE, newDirName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
  787. DEBUGMSGW ((
  788. DBG_FILEENUM,
  789. "Skipping tree %s\\%s because it's excluded",
  790. newDirName,
  791. FileEnum->FileEnumInfo.PathPattern->Leaf
  792. ));
  793. FreeTextExW (g_FileEnumPool, newDirName);
  794. if (Ignore) {
  795. *Ignore = TRUE;
  796. }
  797. return NULL;
  798. }
  799. }
  800. if (ParentNode) {
  801. //
  802. // remember current offset
  803. //
  804. offset = (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf);
  805. }
  806. //
  807. // allocate space for the new node in the growbuffer
  808. //
  809. newNode = (PDIRNODEW) GbGrow (&FileEnum->FileNodes, DWSIZEOF (DIRNODEW));
  810. if (!newNode) {
  811. FreeTextExW (g_FileEnumPool, newDirName);
  812. goto fail;
  813. }
  814. if (ParentNode) {
  815. //
  816. // check if the buffer moved
  817. //
  818. if (offset != (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf)) {
  819. //
  820. // adjust the parent position
  821. //
  822. *ParentNode = (PDIRNODEW)(FileEnum->FileNodes.Buf + offset);
  823. }
  824. }
  825. //
  826. // initialize the newly created node
  827. //
  828. ZeroMemory (newNode, DWSIZEOF (DIRNODEW));
  829. newNode->DirName = newDirName;
  830. if (DirName) {
  831. newNode->DirAttributes = GetFileAttributesW (DirName);
  832. //
  833. // roots are not returned from enumeration because DNF_RETURN_DIRNAME is not set here
  834. //
  835. if ((FileEnum->FileEnumInfo.PathPattern->Leaf == NULL) &&
  836. (FileEnum->FileEnumInfo.PathPattern->ExactRoot) &&
  837. (!WildCharsPatternW (FileEnum->FileEnumInfo.PathPattern->NodePattern))
  838. ) {
  839. newNode->Flags |= DNF_RETURN_DIRNAME;
  840. }
  841. } else {
  842. MYASSERT (ParentNode);
  843. //ParentNode is not NULL (see the assert above) so...
  844. newNode->DirAttributes = (*ParentNode)->FindData.dwFileAttributes; //lint !e613
  845. newNode->Flags |= DNF_RETURN_DIRNAME;
  846. }
  847. newNode->EnumState = DNS_ENUM_INIT;
  848. if ((FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTNODE | OBSPF_NODEISROOTPLUSSTAR)) ||
  849. TestParsedPatternW (FileEnum->FileEnumInfo.PathPattern->NodePattern, newDirName)
  850. ) {
  851. newNode->Flags |= DNF_DIRNAME_MATCHES;
  852. }
  853. if (ParentNode) {
  854. newNode->SubLevel = (*ParentNode)->SubLevel + 1;
  855. } else {
  856. newNode->SubLevel = 0;
  857. }
  858. return newNode;
  859. fail:
  860. if (Ignore) {
  861. if (FileEnum->FileEnumInfo.CallbackOnError) {
  862. *Ignore = (*FileEnum->FileEnumInfo.CallbackOnError)(newNode);
  863. } else {
  864. *Ignore = FALSE;
  865. }
  866. }
  867. return NULL;
  868. }
  869. /*++
  870. Routine Description:
  871. pEnumNextFile enumerates the next file that matches caller's conditions
  872. Arguments:
  873. DirNode - Specifies the node and the current context; receives updated info
  874. Return Value:
  875. TRUE if a new file was found; FALSE if not
  876. --*/
  877. BOOL
  878. pEnumNextFileA (
  879. IN OUT PDIRNODEA DirNode
  880. )
  881. {
  882. do {
  883. if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
  884. FindClose (DirNode->FindHandle);
  885. DirNode->FindHandle = NULL;
  886. return FALSE;
  887. }
  888. //
  889. // ignore dirs
  890. //
  891. if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  892. break;
  893. }
  894. } while (TRUE); //lint !e506
  895. return TRUE;
  896. }
  897. BOOL
  898. pEnumNextFileW (
  899. IN OUT PDIRNODEW DirNode
  900. )
  901. {
  902. do {
  903. if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
  904. FindClose (DirNode->FindHandle);
  905. DirNode->FindHandle = NULL;
  906. return FALSE;
  907. }
  908. //
  909. // ignore dirs
  910. //
  911. if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  912. break;
  913. }
  914. } while (TRUE); //lint !e506
  915. return TRUE;
  916. }
  917. /*++
  918. Routine Description:
  919. pEnumFirstFile enumerates the first file that matches caller's conditions
  920. Arguments:
  921. DirNode - Specifies the node and the current context; receives updated info
  922. Return Value:
  923. TRUE if a first file was found; FALSE if not
  924. --*/
  925. BOOL
  926. pEnumFirstFileA (
  927. OUT PDIRNODEA DirNode,
  928. IN PFILETREE_ENUMA FileEnum
  929. )
  930. {
  931. CHAR pattern[MAX_MBCHAR_PATH];
  932. PSEGMENTA FirstSegment;
  933. PCSTR p;
  934. if (FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_EXACTLEAF) {
  935. FirstSegment = FileEnum->FileEnumInfo.PathPattern->LeafPattern->Pattern->Segment;
  936. p = FirstSegment->Exact.LowerCasePhrase;
  937. MYASSERT (p && *p);
  938. } else {
  939. p = "*";
  940. }
  941. StringCopyA (pattern, DirNode->DirName);
  942. StringCopyA (AppendWackA (pattern), p);
  943. DirNode->FindHandle = FindFirstFileA (pattern, &DirNode->FindData);
  944. if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
  945. return FALSE;
  946. }
  947. do {
  948. //
  949. // ignore dirs
  950. //
  951. if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  952. break;
  953. }
  954. if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
  955. FindClose (DirNode->FindHandle);
  956. DirNode->FindHandle = NULL;
  957. return FALSE;
  958. }
  959. } while (TRUE); //lint !e506
  960. return TRUE;
  961. }
  962. BOOL
  963. pEnumFirstFileW (
  964. OUT PDIRNODEW DirNode,
  965. IN PFILETREE_ENUMW FileEnum
  966. )
  967. {
  968. WCHAR pattern[MAX_WCHAR_PATH];
  969. PSEGMENTW FirstSegment;
  970. PCWSTR p;
  971. if (FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_EXACTLEAF) {
  972. FirstSegment = FileEnum->FileEnumInfo.PathPattern->LeafPattern->Pattern->Segment;
  973. p = FirstSegment->Exact.LowerCasePhrase;
  974. MYASSERT (p && *p);
  975. } else {
  976. p = L"*";
  977. }
  978. StringCopyW (pattern, DirNode->DirName);
  979. StringCopyW (AppendWackW (pattern), p);
  980. DirNode->FindHandle = FindFirstFileW (pattern, &DirNode->FindData);
  981. if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
  982. return FALSE;
  983. }
  984. do {
  985. //
  986. // ignore dirs
  987. //
  988. if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  989. break;
  990. }
  991. if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
  992. FindClose (DirNode->FindHandle);
  993. DirNode->FindHandle = NULL;
  994. return FALSE;
  995. }
  996. } while (TRUE); //lint !e506
  997. return TRUE;
  998. }
  999. /*++
  1000. Routine Description:
  1001. pIsSpecialDirName checks if the specified dir name is a special name (used by the OS)
  1002. Arguments:
  1003. DirName - Specifies the name
  1004. Return Value:
  1005. TRUE if it's a special dir name
  1006. --*/
  1007. BOOL
  1008. pIsSpecialDirNameA (
  1009. IN PCSTR DirName
  1010. )
  1011. {
  1012. return DirName[0] == '.' && (DirName[1] == 0 || (DirName[1] == '.' && DirName[2] == 0));
  1013. }
  1014. BOOL
  1015. pIsSpecialDirNameW (
  1016. IN PCWSTR DirName
  1017. )
  1018. {
  1019. return DirName[0] == L'.' && (DirName[1] == 0 || (DirName[1] == L'.' && DirName[2] == 0));
  1020. }
  1021. /*++
  1022. Routine Description:
  1023. pEnumNextSubDir enumerates the next subdir that matches caller's conditions
  1024. Arguments:
  1025. DirNode - Specifies the node and the current context; receives updated info
  1026. Return Value:
  1027. TRUE if a new subdir was found; FALSE if not
  1028. --*/
  1029. BOOL
  1030. pEnumNextSubDirA (
  1031. IN OUT PDIRNODEA DirNode
  1032. )
  1033. {
  1034. do {
  1035. if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
  1036. FindClose (DirNode->FindHandle);
  1037. DirNode->FindHandle = NULL;
  1038. return FALSE;
  1039. }
  1040. //
  1041. // ignore special dirs
  1042. //
  1043. if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1044. continue;
  1045. }
  1046. if (!pIsSpecialDirNameA (DirNode->FindData.cFileName)) {
  1047. break;
  1048. }
  1049. } while (TRUE); //lint !e506
  1050. return TRUE;
  1051. }
  1052. BOOL
  1053. pEnumNextSubDirW (
  1054. IN OUT PDIRNODEW DirNode
  1055. )
  1056. {
  1057. do {
  1058. if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
  1059. FindClose (DirNode->FindHandle);
  1060. DirNode->FindHandle = NULL;
  1061. return FALSE;
  1062. }
  1063. //
  1064. // ignore special dirs
  1065. //
  1066. if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1067. continue;
  1068. }
  1069. if (!pIsSpecialDirNameW (DirNode->FindData.cFileName)) {
  1070. break;
  1071. }
  1072. } while (TRUE); //lint !e506
  1073. return TRUE;
  1074. }
  1075. /*++
  1076. Routine Description:
  1077. pEnumFirstSubDir enumerates the first subdir that matches caller's conditions
  1078. Arguments:
  1079. DirNode - Specifies the node and the current context; receives updated info
  1080. Return Value:
  1081. TRUE if a first subdir was found; FALSE if not
  1082. --*/
  1083. BOOL
  1084. pEnumFirstSubDirA (
  1085. OUT PDIRNODEA DirNode
  1086. )
  1087. {
  1088. CHAR pattern[MAX_MBCHAR_PATH];
  1089. StringCopyA (pattern, DirNode->DirName);
  1090. StringCopyA (AppendWackA (pattern), "*");
  1091. //
  1092. // NTRAID#NTBUG9-153302-2000/08/01-jimschm this should be enhanced for NT (it supports FindFirstFileExA)
  1093. //
  1094. DirNode->FindHandle = FindFirstFileA (pattern, &DirNode->FindData);
  1095. if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
  1096. return FALSE;
  1097. }
  1098. do {
  1099. //
  1100. // ignore special dirs
  1101. //
  1102. if ((DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  1103. !pIsSpecialDirNameA (DirNode->FindData.cFileName)
  1104. ) {
  1105. break;
  1106. }
  1107. if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
  1108. FindClose (DirNode->FindHandle);
  1109. DirNode->FindHandle = NULL;
  1110. return FALSE;
  1111. }
  1112. } while (TRUE); //lint !e506
  1113. return TRUE;
  1114. }
  1115. BOOL
  1116. pEnumFirstSubDirW (
  1117. OUT PDIRNODEW DirNode
  1118. )
  1119. {
  1120. WCHAR pattern[MAX_WCHAR_PATH];
  1121. StringCopyW (pattern, DirNode->DirName);
  1122. StringCopyW (AppendWackW (pattern), L"*");
  1123. //
  1124. // NTRAID#NTBUG9-153302-2000/08/01-jimschm this should be enhanced for NT (it supports FindFirstFileExW)
  1125. //
  1126. DirNode->FindHandle = FindFirstFileW (pattern, &DirNode->FindData);
  1127. if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
  1128. return FALSE;
  1129. }
  1130. do {
  1131. //
  1132. // ignore special dirs
  1133. //
  1134. if ((DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  1135. !pIsSpecialDirNameW (DirNode->FindData.cFileName)
  1136. ) {
  1137. break;
  1138. }
  1139. if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
  1140. FindClose (DirNode->FindHandle);
  1141. DirNode->FindHandle = NULL;
  1142. return FALSE;
  1143. }
  1144. } while (TRUE); //lint !e506
  1145. return TRUE;
  1146. }
  1147. /*++
  1148. Routine Description:
  1149. pEnumNextFileInTree is a private function that enumerates the next node matching
  1150. the specified criteria; it's implemented as a state machine that travels the dirs/files
  1151. as specified the the caller; it doesn't check if they actually match the patterns
  1152. Arguments:
  1153. FileEnum - Specifies the current enum context; receives updated info
  1154. CurrentDirNode - Receives the dir node that is currently processed, if success is returned
  1155. Return Value:
  1156. TRUE if a next match was found; FALSE if no more dirs/files match
  1157. --*/
  1158. BOOL
  1159. pEnumNextFileInTreeA (
  1160. IN OUT PFILETREE_ENUMA FileEnum,
  1161. OUT PDIRNODEA* CurrentDirNode
  1162. )
  1163. {
  1164. PDIRNODEA currentNode;
  1165. PDIRNODEA newNode;
  1166. BOOL ignore;
  1167. while ((currentNode = pGetCurrentDirNodeA (FileEnum, FALSE)) != NULL) {
  1168. *CurrentDirNode = currentNode;
  1169. switch (currentNode->EnumState) {
  1170. case DNS_FILE_FIRST:
  1171. if (FileEnum->ControlFlags & FECF_SKIPFILES) {
  1172. FileEnum->ControlFlags &= ~FECF_SKIPFILES;
  1173. currentNode->EnumState = DNS_FILE_DONE;
  1174. break;
  1175. }
  1176. if (pEnumFirstFileA (currentNode, FileEnum)) {
  1177. currentNode->EnumState = DNS_FILE_NEXT;
  1178. return TRUE;
  1179. }
  1180. currentNode->EnumState = DNS_FILE_DONE;
  1181. break;
  1182. case DNS_FILE_NEXT:
  1183. if (FileEnum->ControlFlags & FECF_SKIPFILES) {
  1184. FileEnum->ControlFlags &= ~FECF_SKIPFILES;
  1185. currentNode->EnumState = DNS_FILE_DONE;
  1186. break;
  1187. }
  1188. if (pEnumNextFileA (currentNode)) {
  1189. return TRUE;
  1190. }
  1191. //
  1192. // no more files for this one, go to the next
  1193. //
  1194. currentNode->EnumState = DNS_FILE_DONE;
  1195. //
  1196. // fall through
  1197. //
  1198. case DNS_FILE_DONE:
  1199. if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
  1200. //
  1201. // done with this node
  1202. //
  1203. currentNode->EnumState = DNS_ENUM_DONE;
  1204. break;
  1205. }
  1206. //
  1207. // now enum subdirs
  1208. //
  1209. currentNode->EnumState = DNS_SUBDIR_FIRST;
  1210. //
  1211. // fall through
  1212. //
  1213. case DNS_SUBDIR_FIRST:
  1214. if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
  1215. FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
  1216. currentNode->EnumState = DNS_SUBDIR_DONE;
  1217. break;
  1218. }
  1219. //
  1220. // check current dir's level; if max level reached, don't recurse into subdirs
  1221. //
  1222. if (currentNode->SubLevel >= FileEnum->FileEnumInfo.MaxSubLevel) {
  1223. currentNode->EnumState = DNS_SUBDIR_DONE;
  1224. break;
  1225. }
  1226. if (!pEnumFirstSubDirA (currentNode)) {
  1227. currentNode->EnumState = DNS_SUBDIR_DONE;
  1228. break;
  1229. }
  1230. currentNode->EnumState = DNS_SUBDIR_NEXT;
  1231. newNode = pCreateDirNodeA (FileEnum, NULL, &currentNode, &ignore);
  1232. if (newNode) {
  1233. //
  1234. // look at the new node first
  1235. //
  1236. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1237. if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
  1238. newNode->Flags &= ~DNF_RETURN_DIRNAME;
  1239. *CurrentDirNode = newNode;
  1240. return TRUE;
  1241. }
  1242. }
  1243. break;
  1244. }
  1245. if (!ignore) {
  1246. //
  1247. // abort enum
  1248. //
  1249. DEBUGMSGA ((
  1250. DBG_ERROR,
  1251. "Error encountered enumerating file system; aborting enumeration"
  1252. ));
  1253. FileEnum->RootState = FES_ROOT_DONE;
  1254. return FALSE;
  1255. }
  1256. //
  1257. // fall through
  1258. //
  1259. case DNS_SUBDIR_NEXT:
  1260. if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
  1261. FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
  1262. currentNode->EnumState = DNS_SUBDIR_DONE;
  1263. break;
  1264. }
  1265. if (pEnumNextSubDirA (currentNode)) {
  1266. newNode = pCreateDirNodeA (FileEnum, NULL, &currentNode, &ignore);
  1267. if (newNode) {
  1268. //
  1269. // look at the new node first
  1270. //
  1271. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1272. if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
  1273. newNode->Flags &= ~DNF_RETURN_DIRNAME;
  1274. *CurrentDirNode = newNode;
  1275. return TRUE;
  1276. }
  1277. }
  1278. break;
  1279. }
  1280. //
  1281. // did it fail because of a soft block?
  1282. //
  1283. if (!ignore) {
  1284. DEBUGMSGA ((
  1285. DBG_ERROR,
  1286. "Error encountered enumerating file system; aborting enumeration"
  1287. ));
  1288. FileEnum->RootState = FES_ROOT_DONE;
  1289. return FALSE;
  1290. }
  1291. //
  1292. // continue with next subdir
  1293. //
  1294. break;
  1295. }
  1296. //
  1297. // this node is done
  1298. //
  1299. currentNode->EnumState = DNS_SUBDIR_DONE;
  1300. //
  1301. // fall through
  1302. //
  1303. case DNS_SUBDIR_DONE:
  1304. if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
  1305. //
  1306. // now enum files
  1307. //
  1308. if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
  1309. currentNode->EnumState = DNS_FILE_FIRST;
  1310. break;
  1311. }
  1312. }
  1313. //
  1314. // done with this node
  1315. //
  1316. currentNode->EnumState = DNS_ENUM_DONE;
  1317. //
  1318. // fall through
  1319. //
  1320. case DNS_ENUM_DONE:
  1321. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1322. if (!(FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST)) {
  1323. if (currentNode->Flags & DNF_RETURN_DIRNAME) {
  1324. currentNode->Flags &= ~DNF_RETURN_DIRNAME;
  1325. //
  1326. // before returning, set some data
  1327. //
  1328. currentNode->FindData.cFileName[0] = 0;
  1329. return TRUE;
  1330. }
  1331. }
  1332. }
  1333. pDeleteDirNodeA (FileEnum, FALSE);
  1334. break;
  1335. case DNS_ENUM_INIT:
  1336. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1337. if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
  1338. if (currentNode->Flags & DNF_RETURN_DIRNAME) {
  1339. currentNode->Flags &= ~DNF_RETURN_DIRNAME;
  1340. return TRUE;
  1341. }
  1342. }
  1343. }
  1344. if (FileEnum->ControlFlags & FECF_SKIPDIR) {
  1345. FileEnum->ControlFlags &= ~FECF_SKIPDIR;
  1346. currentNode->EnumState = DNS_ENUM_DONE;
  1347. break;
  1348. }
  1349. if (FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST) {
  1350. //
  1351. // enum files
  1352. //
  1353. if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
  1354. currentNode->EnumState = DNS_FILE_FIRST;
  1355. break;
  1356. }
  1357. }
  1358. //
  1359. // enum subdirs
  1360. //
  1361. currentNode->EnumState = DNS_SUBDIR_FIRST;
  1362. break;
  1363. default:
  1364. MYASSERT (FALSE); //lint !e506
  1365. }
  1366. }
  1367. return FALSE;
  1368. }
  1369. BOOL
  1370. pEnumNextFileInTreeW (
  1371. IN OUT PFILETREE_ENUMW FileEnum,
  1372. OUT PDIRNODEW* CurrentDirNode
  1373. )
  1374. {
  1375. PDIRNODEW currentNode;
  1376. PDIRNODEW newNode;
  1377. BOOL ignore;
  1378. while ((currentNode = pGetCurrentDirNodeW (FileEnum, FALSE)) != NULL) {
  1379. *CurrentDirNode = currentNode;
  1380. switch (currentNode->EnumState) {
  1381. case DNS_FILE_FIRST:
  1382. if (FileEnum->ControlFlags & FECF_SKIPFILES) {
  1383. FileEnum->ControlFlags &= ~FECF_SKIPFILES;
  1384. currentNode->EnumState = DNS_FILE_DONE;
  1385. break;
  1386. }
  1387. if (pEnumFirstFileW (currentNode, FileEnum)) {
  1388. currentNode->EnumState = DNS_FILE_NEXT;
  1389. return TRUE;
  1390. }
  1391. currentNode->EnumState = DNS_FILE_DONE;
  1392. break;
  1393. case DNS_FILE_NEXT:
  1394. if (FileEnum->ControlFlags & FECF_SKIPFILES) {
  1395. FileEnum->ControlFlags &= ~FECF_SKIPFILES;
  1396. currentNode->EnumState = DNS_FILE_DONE;
  1397. break;
  1398. }
  1399. if (pEnumNextFileW (currentNode)) {
  1400. return TRUE;
  1401. }
  1402. //
  1403. // no more files for this one, go to the next
  1404. //
  1405. currentNode->EnumState = DNS_FILE_DONE;
  1406. //
  1407. // fall through
  1408. //
  1409. case DNS_FILE_DONE:
  1410. if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
  1411. //
  1412. // done with this node
  1413. //
  1414. currentNode->EnumState = DNS_ENUM_DONE;
  1415. break;
  1416. }
  1417. //
  1418. // now enum subdirs
  1419. //
  1420. currentNode->EnumState = DNS_SUBDIR_FIRST;
  1421. //
  1422. // fall through
  1423. //
  1424. case DNS_SUBDIR_FIRST:
  1425. if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
  1426. FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
  1427. currentNode->EnumState = DNS_SUBDIR_DONE;
  1428. break;
  1429. }
  1430. //
  1431. // check current dir's level; if max level reached, don't recurse into subdirs
  1432. //
  1433. if (currentNode->SubLevel >= FileEnum->FileEnumInfo.MaxSubLevel) {
  1434. currentNode->EnumState = DNS_SUBDIR_DONE;
  1435. break;
  1436. }
  1437. if (!pEnumFirstSubDirW (currentNode)) {
  1438. currentNode->EnumState = DNS_SUBDIR_DONE;
  1439. break;
  1440. }
  1441. currentNode->EnumState = DNS_SUBDIR_NEXT;
  1442. newNode = pCreateDirNodeW (FileEnum, NULL, &currentNode, &ignore);
  1443. if (newNode) {
  1444. //
  1445. // look at the new node first
  1446. //
  1447. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1448. if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
  1449. newNode->Flags &= ~DNF_RETURN_DIRNAME;
  1450. *CurrentDirNode = newNode;
  1451. return TRUE;
  1452. }
  1453. }
  1454. break;
  1455. }
  1456. //
  1457. // did it fail because of a soft block?
  1458. //
  1459. if (!ignore) {
  1460. DEBUGMSGA ((
  1461. DBG_ERROR,
  1462. "Error encountered enumerating file system; aborting enumeration"
  1463. ));
  1464. FileEnum->RootState = FES_ROOT_DONE;
  1465. return FALSE;
  1466. }
  1467. //
  1468. // fall through
  1469. //
  1470. case DNS_SUBDIR_NEXT:
  1471. if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
  1472. FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
  1473. currentNode->EnumState = DNS_SUBDIR_DONE;
  1474. break;
  1475. }
  1476. if (pEnumNextSubDirW (currentNode)) {
  1477. newNode = pCreateDirNodeW (FileEnum, NULL, &currentNode, &ignore);
  1478. if (newNode) {
  1479. //
  1480. // look at the new node first
  1481. //
  1482. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1483. if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
  1484. newNode->Flags &= ~DNF_RETURN_DIRNAME;
  1485. *CurrentDirNode = newNode;
  1486. return TRUE;
  1487. }
  1488. }
  1489. break;
  1490. }
  1491. //
  1492. // did it fail because of a soft block?
  1493. //
  1494. if (!ignore) {
  1495. DEBUGMSGA ((
  1496. DBG_ERROR,
  1497. "Error encountered enumerating file system; aborting enumeration"
  1498. ));
  1499. FileEnum->RootState = FES_ROOT_DONE;
  1500. return FALSE;
  1501. }
  1502. //
  1503. // continue with next subdir
  1504. //
  1505. break;
  1506. }
  1507. //
  1508. // this node is done
  1509. //
  1510. currentNode->EnumState = DNS_SUBDIR_DONE;
  1511. //
  1512. // fall through
  1513. //
  1514. case DNS_SUBDIR_DONE:
  1515. if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
  1516. //
  1517. // now enum files
  1518. //
  1519. if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
  1520. currentNode->EnumState = DNS_FILE_FIRST;
  1521. break;
  1522. }
  1523. }
  1524. //
  1525. // done with this node
  1526. //
  1527. currentNode->EnumState = DNS_ENUM_DONE;
  1528. //
  1529. // fall through
  1530. //
  1531. case DNS_ENUM_DONE:
  1532. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1533. if (!(FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST)) {
  1534. if (currentNode->Flags & DNF_RETURN_DIRNAME) {
  1535. currentNode->Flags &= ~DNF_RETURN_DIRNAME;
  1536. //
  1537. // before returning, set some data
  1538. //
  1539. currentNode->FindData.cFileName[0] = 0;
  1540. return TRUE;
  1541. }
  1542. }
  1543. }
  1544. pDeleteDirNodeW (FileEnum, FALSE);
  1545. break;
  1546. case DNS_ENUM_INIT:
  1547. if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
  1548. if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
  1549. if (currentNode->Flags & DNF_RETURN_DIRNAME) {
  1550. currentNode->Flags &= ~DNF_RETURN_DIRNAME;
  1551. return TRUE;
  1552. }
  1553. }
  1554. }
  1555. if (FileEnum->ControlFlags & FECF_SKIPDIR) {
  1556. FileEnum->ControlFlags &= ~FECF_SKIPDIR;
  1557. currentNode->EnumState = DNS_ENUM_DONE;
  1558. break;
  1559. }
  1560. if (FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST) {
  1561. //
  1562. // enum files
  1563. //
  1564. if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
  1565. currentNode->EnumState = DNS_FILE_FIRST;
  1566. break;
  1567. }
  1568. }
  1569. //
  1570. // enum subdirs
  1571. //
  1572. currentNode->EnumState = DNS_SUBDIR_FIRST;
  1573. break;
  1574. default:
  1575. MYASSERT (FALSE); //lint !e506
  1576. }
  1577. }
  1578. return FALSE;
  1579. }
  1580. /*++
  1581. Routine Description:
  1582. pEnumFirstFileRoot enumerates the first root that matches caller's conditions
  1583. Arguments:
  1584. FileEnum - Specifies the context; receives updated info
  1585. Return Value:
  1586. TRUE if a root node was created; FALSE if not
  1587. --*/
  1588. BOOL
  1589. pEnumFirstFileRootA (
  1590. IN OUT PFILETREE_ENUMA FileEnum
  1591. )
  1592. {
  1593. PSTR root = NULL;
  1594. BOOL ignore;
  1595. if (FileEnum->FileEnumInfo.PathPattern->ExactRoot) {
  1596. root = pFileAllocateMemory (SizeOfStringA (FileEnum->FileEnumInfo.PathPattern->ExactRoot));
  1597. ObsDecodeStringA (root, FileEnum->FileEnumInfo.PathPattern->ExactRoot);
  1598. }
  1599. if (root) {
  1600. if (!BfPathIsDirectoryA (root)) {
  1601. DEBUGMSGA ((DBG_FILEENUM, "pEnumFirstFileRootA: Invalid root spec: %s", root));
  1602. pFileFreeMemory (root);
  1603. return FALSE;
  1604. }
  1605. if (pCreateDirNodeA (FileEnum, root, NULL, NULL)) {
  1606. FileEnum->RootState = FES_ROOT_DONE;
  1607. pFileFreeMemory (root);
  1608. return TRUE;
  1609. }
  1610. } else {
  1611. FileEnum->DriveEnum = pFileAllocateMemory (DWSIZEOF (DRIVE_ENUMA));
  1612. if (!EnumFirstDriveA (FileEnum->DriveEnum, FileEnum->DriveEnumTypes)) {
  1613. return FALSE;
  1614. }
  1615. do {
  1616. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  1617. if (ElIsTreeExcluded2A (ELT_FILE, FileEnum->DriveEnum->DriveName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
  1618. DEBUGMSGA ((DBG_FILEENUM, "pEnumFirstFileRootA: Root is excluded: %s", FileEnum->DriveEnum->DriveName));
  1619. continue;
  1620. }
  1621. }
  1622. if (!pCreateDirNodeA (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
  1623. if (ignore) {
  1624. continue;
  1625. }
  1626. break;
  1627. }
  1628. FileEnum->RootState = FES_ROOT_NEXT;
  1629. return TRUE;
  1630. } while (EnumNextDriveA (FileEnum->DriveEnum));
  1631. pFileFreeMemory (FileEnum->DriveEnum);
  1632. FileEnum->DriveEnum = NULL;
  1633. }
  1634. return FALSE;
  1635. }
  1636. BOOL
  1637. pEnumFirstFileRootW (
  1638. IN OUT PFILETREE_ENUMW FileEnum
  1639. )
  1640. {
  1641. PWSTR root = NULL;
  1642. BOOL ignore;
  1643. if (FileEnum->FileEnumInfo.PathPattern->ExactRoot) {
  1644. root = pFileAllocateMemory (SizeOfStringW (FileEnum->FileEnumInfo.PathPattern->ExactRoot));
  1645. ObsDecodeStringW (root, FileEnum->FileEnumInfo.PathPattern->ExactRoot);
  1646. }
  1647. if (root) {
  1648. if (!BfPathIsDirectoryW (root)) {
  1649. DEBUGMSGW ((DBG_FILEENUM, "pEnumFirstFileRootW: Invalid root spec: %s", root));
  1650. pFileFreeMemory (root);
  1651. return FALSE;
  1652. }
  1653. if (pCreateDirNodeW (FileEnum, root, NULL, NULL)) {
  1654. FileEnum->RootState = FES_ROOT_DONE;
  1655. pFileFreeMemory (root);
  1656. return TRUE;
  1657. }
  1658. } else {
  1659. FileEnum->DriveEnum = pFileAllocateMemory (DWSIZEOF (DRIVE_ENUMA));
  1660. if (!EnumFirstDriveW (FileEnum->DriveEnum, FileEnum->DriveEnumTypes)) {
  1661. return FALSE;
  1662. }
  1663. do {
  1664. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  1665. if (ElIsTreeExcluded2W (ELT_FILE, FileEnum->DriveEnum->DriveName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
  1666. DEBUGMSGW ((DBG_FILEENUM, "pEnumFirstFileRootW: Root is excluded: %s", FileEnum->DriveEnum->DriveName));
  1667. continue;
  1668. }
  1669. }
  1670. if (!pCreateDirNodeW (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
  1671. if (ignore) {
  1672. continue;
  1673. }
  1674. break;
  1675. }
  1676. FileEnum->RootState = FES_ROOT_NEXT;
  1677. return TRUE;
  1678. } while (EnumNextDriveW (FileEnum->DriveEnum));
  1679. pFileFreeMemory (FileEnum->DriveEnum);
  1680. FileEnum->DriveEnum = NULL;
  1681. }
  1682. return FALSE;
  1683. }
  1684. BOOL
  1685. pEnumNextFileRootA (
  1686. IN OUT PFILETREE_ENUMA FileEnum
  1687. )
  1688. {
  1689. BOOL ignore;
  1690. while (EnumNextDriveA (FileEnum->DriveEnum)) {
  1691. if (pCreateDirNodeA (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
  1692. return TRUE;
  1693. }
  1694. if (!ignore) {
  1695. break;
  1696. }
  1697. }
  1698. FileEnum->RootState = FES_ROOT_DONE;
  1699. return FALSE;
  1700. }
  1701. BOOL
  1702. pEnumNextFileRootW (
  1703. IN OUT PFILETREE_ENUMW FileEnum
  1704. )
  1705. {
  1706. BOOL ignore;
  1707. while (EnumNextDriveW (FileEnum->DriveEnum)) {
  1708. if (pCreateDirNodeW (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
  1709. return TRUE;
  1710. }
  1711. if (!ignore) {
  1712. break;
  1713. }
  1714. }
  1715. FileEnum->RootState = FES_ROOT_DONE;
  1716. return FALSE;
  1717. }
  1718. /*++
  1719. Routine Description:
  1720. EnumFirstFileInTreeEx enumerates file system dirs, and optionally files, that match the
  1721. specified criteria
  1722. Arguments:
  1723. FileEnum - Receives the enum context info; this will be used in subsequent calls to
  1724. EnumNextFileInTree
  1725. EncodedPathPattern - Specifies the encoded dir pattern (encoded as defined by the
  1726. ParsedPattern functions)
  1727. EncodedFilePattern - Specifies the encoded file pattern (encoded as defined by the
  1728. ParsedPattern functions); optional; NULL means no files
  1729. should be returned (only look for dirs)
  1730. EnumDirs - Specifies TRUE if directories should be returned during the enumeration
  1731. (if they match the pattern)
  1732. ContainersFirst - Specifies TRUE if directories should be returned before any of its
  1733. files or subdirs
  1734. FilesFirst - Specifies TRUE if a dir's files should be returned before dir's subdirs;
  1735. this parameter decides the enum order between files and subdirs
  1736. for each dir
  1737. DepthFirst - Specifies TRUE if the current subdir of any dir should be fully enumerated
  1738. before going to the next subdir; this parameter decides if the tree
  1739. traversal is depth-first (TRUE) or width-first (FALSE)
  1740. MaxSubLevel - Specifies the maximum sub-level of a subdir that is to be enumerated,
  1741. relative to the root; if 0, only the root is enumerated;
  1742. if -1, all sub-levels are enumerated
  1743. UseExclusions - Specifies TRUE if exclusion APIs should be used to determine if certain
  1744. paths/files are excluded from enumeration; this slows down the speed
  1745. CallbackOnError - Specifies a pointer to a callback function that will be called during
  1746. enumeration if an error occurs; if the callback is defined and it
  1747. returns FALSE, the enumeration is aborted, otherwise it will continue
  1748. ignoring the error
  1749. Return Value:
  1750. TRUE if a first match is found.
  1751. FALSE otherwise.
  1752. --*/
  1753. BOOL
  1754. EnumFirstFileInTreeExA (
  1755. OUT PFILETREE_ENUMA FileEnum,
  1756. IN PCSTR EncodedPathPattern,
  1757. IN UINT DriveEnumTypes,
  1758. IN BOOL EnumDirs,
  1759. IN BOOL ContainersFirst,
  1760. IN BOOL FilesFirst,
  1761. IN BOOL DepthFirst,
  1762. IN DWORD MaxSubLevel,
  1763. IN BOOL UseExclusions,
  1764. IN FPE_ERROR_CALLBACKA CallbackOnError OPTIONAL
  1765. )
  1766. {
  1767. MYASSERT (FileEnum && EncodedPathPattern && *EncodedPathPattern);
  1768. MYASSERT (g_FileEnumPool);
  1769. ZeroMemory (FileEnum, DWSIZEOF (*FileEnum)); //lint !e613 !e668
  1770. FileEnum->DriveEnumTypes = DriveEnumTypes;
  1771. //
  1772. // first try to get dir enum info in internal format
  1773. //
  1774. if (!pGetFileEnumInfoA (
  1775. /*lint -e(613)*/&FileEnum->FileEnumInfo,
  1776. EncodedPathPattern,
  1777. EnumDirs,
  1778. ContainersFirst,
  1779. FilesFirst,
  1780. DepthFirst,
  1781. MaxSubLevel,
  1782. UseExclusions
  1783. )) {
  1784. AbortEnumFileInTreeA (FileEnum);
  1785. return FALSE;
  1786. }
  1787. if (UseExclusions) {
  1788. //
  1789. // next check if the starting key is in an excluded tree
  1790. //
  1791. if (ElIsObsPatternExcludedA (ELT_FILE, /*lint -e(613)*/FileEnum->FileEnumInfo.PathPattern)) {
  1792. DEBUGMSGA ((
  1793. DBG_FILEENUM,
  1794. "EnumFirstFileInTreeExA: Root is excluded: %s",
  1795. EncodedPathPattern
  1796. ));
  1797. AbortEnumFileInTreeA (FileEnum);
  1798. return FALSE;
  1799. }
  1800. }
  1801. if (!pEnumFirstFileRootA (FileEnum)) {
  1802. AbortEnumFileInTreeA (FileEnum);
  1803. return FALSE;
  1804. }
  1805. /*lint -e(613)*/FileEnum->FileEnumInfo.CallbackOnError = CallbackOnError;
  1806. return EnumNextFileInTreeA (FileEnum);
  1807. }
  1808. BOOL
  1809. EnumFirstFileInTreeExW (
  1810. OUT PFILETREE_ENUMW FileEnum,
  1811. IN PCWSTR EncodedPathPattern,
  1812. IN UINT DriveEnumTypes,
  1813. IN BOOL EnumDirs,
  1814. IN BOOL ContainersFirst,
  1815. IN BOOL FilesFirst,
  1816. IN BOOL DepthFirst,
  1817. IN DWORD MaxSubLevel,
  1818. IN BOOL UseExclusions,
  1819. IN FPE_ERROR_CALLBACKW CallbackOnError OPTIONAL
  1820. )
  1821. {
  1822. MYASSERT (FileEnum && EncodedPathPattern && *EncodedPathPattern);
  1823. MYASSERT (g_FileEnumPool);
  1824. ZeroMemory (FileEnum, DWSIZEOF (*FileEnum)); //lint !e613 !e668
  1825. FileEnum->DriveEnumTypes = DriveEnumTypes;
  1826. //
  1827. // first try to get dir enum info in internal format
  1828. //
  1829. if (!pGetFileEnumInfoW (
  1830. /*lint -e(613)*/&FileEnum->FileEnumInfo,
  1831. EncodedPathPattern,
  1832. EnumDirs,
  1833. ContainersFirst,
  1834. FilesFirst,
  1835. DepthFirst,
  1836. MaxSubLevel,
  1837. UseExclusions
  1838. )) {
  1839. AbortEnumFileInTreeW (FileEnum);
  1840. return FALSE;
  1841. }
  1842. if (UseExclusions) {
  1843. //
  1844. // next check if the starting key is in an excluded tree
  1845. //
  1846. if (ElIsObsPatternExcludedW (ELT_FILE, /*lint -e(613)*/FileEnum->FileEnumInfo.PathPattern)) {
  1847. DEBUGMSGW ((
  1848. DBG_FILEENUM,
  1849. "EnumFirstFileInTreeExW: Root is excluded: %s",
  1850. EncodedPathPattern
  1851. ));
  1852. AbortEnumFileInTreeW (FileEnum);
  1853. return FALSE;
  1854. }
  1855. }
  1856. if (!pEnumFirstFileRootW (FileEnum)) {
  1857. AbortEnumFileInTreeW (FileEnum);
  1858. return FALSE;
  1859. }
  1860. /*lint -e(613)*/FileEnum->FileEnumInfo.CallbackOnError = CallbackOnError;
  1861. return EnumNextFileInTreeW (FileEnum);
  1862. }
  1863. BOOL
  1864. pTestLeafPatternA (
  1865. IN PPARSEDPATTERNA ParsedPattern,
  1866. IN PCSTR LeafToTest
  1867. )
  1868. {
  1869. PSTR newLeaf;
  1870. BOOL result = TRUE;
  1871. if (!TestParsedPatternA (ParsedPattern, LeafToTest)) {
  1872. newLeaf = JoinTextA (LeafToTest, ".");
  1873. result = TestParsedPatternA (ParsedPattern, newLeaf);
  1874. FreeTextA (newLeaf);
  1875. }
  1876. return result;
  1877. }
  1878. BOOL
  1879. pTestLeafPatternW (
  1880. IN PPARSEDPATTERNW ParsedPattern,
  1881. IN PCWSTR LeafToTest
  1882. )
  1883. {
  1884. PWSTR newLeaf;
  1885. BOOL result = TRUE;
  1886. if (!TestParsedPatternW (ParsedPattern, LeafToTest)) {
  1887. newLeaf = JoinTextW (LeafToTest, L".");
  1888. result = TestParsedPatternW (ParsedPattern, newLeaf);
  1889. FreeTextW (newLeaf);
  1890. }
  1891. return result;
  1892. }
  1893. /*++
  1894. Routine Description:
  1895. EnumNextFileInTree enumerates the next node matching the criteria specified in
  1896. FileEnum; this is filled on the call to EnumFirstFileInTreeEx;
  1897. Arguments:
  1898. FileEnum - Specifies the current enum context; receives updated info
  1899. Return Value:
  1900. TRUE if a next match was found; FALSE if no more dirs/files match
  1901. --*/
  1902. BOOL
  1903. EnumNextFileInTreeA (
  1904. IN OUT PFILETREE_ENUMA FileEnum
  1905. )
  1906. {
  1907. PDIRNODEA currentNode;
  1908. BOOL success;
  1909. MYASSERT (FileEnum);
  1910. do {
  1911. if (FileEnum->EncodedFullName) {
  1912. ObsFreeA (FileEnum->EncodedFullName);
  1913. FileEnum->EncodedFullName = NULL;
  1914. }
  1915. while (TRUE) { //lint !e506
  1916. if (FileEnum->LastWackPtr) {
  1917. *FileEnum->LastWackPtr = '\\';
  1918. FileEnum->LastWackPtr = NULL;
  1919. }
  1920. if (!pEnumNextFileInTreeA (FileEnum, &currentNode)) {
  1921. break;
  1922. }
  1923. MYASSERT (currentNode && currentNode->DirName);
  1924. //
  1925. // check if this object matches the pattern
  1926. //
  1927. if (!(currentNode->Flags & DNF_DIRNAME_MATCHES)) { //lint !e613
  1928. continue;
  1929. }
  1930. if (/*lint -e(613)*/currentNode->FindData.cFileName[0] == 0) {
  1931. MYASSERT (/*lint -e(613)*/currentNode->DirAttributes & FILE_ATTRIBUTE_DIRECTORY);
  1932. FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
  1933. FileEnum->LastWackPtr = _mbsrchr (FileEnum->Location, '\\');
  1934. if (!FileEnum->LastWackPtr) {
  1935. FileEnum->Name = FileEnum->Location;
  1936. } else {
  1937. FileEnum->Name = _mbsinc (FileEnum->LastWackPtr);
  1938. if (!FileEnum->Name) {
  1939. FileEnum->Name = FileEnum->Location;
  1940. }
  1941. }
  1942. FileEnum->Attributes = /*lint -e(613)*/currentNode->DirAttributes;
  1943. //
  1944. // prepare full path buffer
  1945. //
  1946. StringCopyA (FileEnum->NativeFullName, FileEnum->Location);
  1947. FileEnum->LastNode = currentNode;
  1948. FileEnum->FileNameAppendPos = NULL;
  1949. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  1950. //
  1951. // check if this object is excluded
  1952. //
  1953. if (ElIsExcluded2A (ELT_FILE, FileEnum->Location, NULL)) {
  1954. DEBUGMSGA ((
  1955. DBG_FILEENUM,
  1956. "Object %s was found, but it's excluded",
  1957. FileEnum->NativeFullName
  1958. ));
  1959. continue;
  1960. }
  1961. }
  1962. FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExA (
  1963. FileEnum->Location,
  1964. NULL,
  1965. TRUE
  1966. );
  1967. } else {
  1968. FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
  1969. FileEnum->Name = /*lint -e(613)*/currentNode->FindData.cFileName;
  1970. //
  1971. // test if the filename matches
  1972. //
  1973. if (!(FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTLEAF | OBSPF_OPTIONALLEAF)) &&
  1974. !pTestLeafPatternA (
  1975. FileEnum->FileEnumInfo.PathPattern->LeafPattern,
  1976. /*lint -e(613)*/currentNode->FindData.cFileName
  1977. )
  1978. ) {
  1979. continue;
  1980. }
  1981. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  1982. if (ElIsExcluded2A (ELT_FILE, NULL, /*lint -e(613)*/currentNode->FindData.cFileName)) {
  1983. DEBUGMSGA ((
  1984. DBG_FILEENUM,
  1985. "File %s\\%s was found, but it's excluded by filename",
  1986. FileEnum->Location,
  1987. /*lint -e(613)*/currentNode->FindData.cFileName
  1988. ));
  1989. continue;
  1990. }
  1991. }
  1992. if (FileEnum->LastNode != currentNode) {
  1993. FileEnum->LastNode = currentNode;
  1994. //
  1995. // prepare full path buffer
  1996. //
  1997. FileEnum->NativeFullName[0] = 0;
  1998. FileEnum->FileNameAppendPos = StringCatA (FileEnum->NativeFullName, FileEnum->Location);
  1999. if (FileEnum->FileNameAppendPos) {
  2000. *FileEnum->FileNameAppendPos++ = '\\';
  2001. }
  2002. } else if (!FileEnum->FileNameAppendPos) {
  2003. FileEnum->FileNameAppendPos = GetEndOfStringA (FileEnum->NativeFullName);
  2004. if (FileEnum->FileNameAppendPos) {
  2005. *FileEnum->FileNameAppendPos++ = '\\';
  2006. }
  2007. }
  2008. if (FileEnum->FileNameAppendPos + SizeOfStringA (FileEnum->Name) / DWSIZEOF(CHAR)>
  2009. FileEnum->NativeFullName + DWSIZEOF (FileEnum->NativeFullName) / DWSIZEOF(CHAR)) {
  2010. DEBUGMSGA ((
  2011. DBG_ERROR,
  2012. "File %s\\%s was found, but it's path is too long",
  2013. FileEnum->Location,
  2014. FileEnum->Name
  2015. ));
  2016. continue;
  2017. }
  2018. StringCopyA (FileEnum->FileNameAppendPos, FileEnum->Name);
  2019. FileEnum->Attributes = /*lint -e(613)*/currentNode->FindData.dwFileAttributes;
  2020. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  2021. //
  2022. // check if this object is excluded
  2023. //
  2024. if (ElIsExcluded2A (ELT_FILE, FileEnum->Location, FileEnum->Name)) {
  2025. DEBUGMSGA ((
  2026. DBG_FILEENUM,
  2027. "Object %s was found, but it's excluded",
  2028. FileEnum->NativeFullName
  2029. ));
  2030. continue;
  2031. }
  2032. }
  2033. FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExA (
  2034. FileEnum->Location,
  2035. FileEnum->Name,
  2036. TRUE
  2037. );
  2038. }
  2039. if (FileEnum->LastWackPtr) {
  2040. *FileEnum->LastWackPtr = 0;
  2041. }
  2042. FileEnum->CurrentLevel = FileEnum->FileEnumInfo.RootLevel + /*lint -e(613)*/currentNode->SubLevel;
  2043. return TRUE;
  2044. }
  2045. //
  2046. // try the next root
  2047. //
  2048. if (FileEnum->RootState == FES_ROOT_DONE) {
  2049. break;
  2050. }
  2051. MYASSERT (FileEnum->RootState == FES_ROOT_NEXT);
  2052. MYASSERT (FileEnum->DriveEnum);
  2053. success = pEnumNextFileRootA (FileEnum);
  2054. } while (success);
  2055. AbortEnumFileInTreeA (FileEnum);
  2056. return FALSE;
  2057. }
  2058. BOOL
  2059. EnumNextFileInTreeW (
  2060. IN OUT PFILETREE_ENUMW FileEnum
  2061. )
  2062. {
  2063. PDIRNODEW currentNode;
  2064. BOOL success;
  2065. MYASSERT (FileEnum);
  2066. do {
  2067. if (FileEnum->EncodedFullName) {
  2068. ObsFreeW (FileEnum->EncodedFullName);
  2069. FileEnum->EncodedFullName = NULL;
  2070. }
  2071. while (TRUE) {
  2072. if (FileEnum->LastWackPtr) {
  2073. *FileEnum->LastWackPtr = L'\\';
  2074. FileEnum->LastWackPtr = NULL;
  2075. }
  2076. if (!pEnumNextFileInTreeW (FileEnum, &currentNode)) {
  2077. break;
  2078. }
  2079. MYASSERT (currentNode && currentNode->DirName);
  2080. //
  2081. // check if this object matches the pattern
  2082. //
  2083. if (!(currentNode->Flags & DNF_DIRNAME_MATCHES)) { //lint !e613
  2084. continue;
  2085. }
  2086. if (/*lint -e(613)*/currentNode->FindData.cFileName[0] == 0) {
  2087. MYASSERT (/*lint -e(613)*/currentNode->DirAttributes & FILE_ATTRIBUTE_DIRECTORY);
  2088. FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
  2089. FileEnum->LastWackPtr = wcsrchr (FileEnum->Location, L'\\');
  2090. if (!FileEnum->LastWackPtr) {
  2091. FileEnum->Name = FileEnum->Location;
  2092. } else {
  2093. FileEnum->Name = FileEnum->LastWackPtr + 1;
  2094. if (!FileEnum->Name) {
  2095. FileEnum->Name = FileEnum->Location;
  2096. }
  2097. }
  2098. FileEnum->Attributes = /*lint -e(613)*/currentNode->DirAttributes;
  2099. //
  2100. // prepare full path buffer
  2101. //
  2102. StringCopyW (FileEnum->NativeFullName, FileEnum->Location);
  2103. FileEnum->LastNode = currentNode;
  2104. FileEnum->FileNameAppendPos = NULL;
  2105. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  2106. //
  2107. // check if this object is excluded
  2108. //
  2109. if (ElIsExcluded2W (ELT_FILE, FileEnum->Location, NULL)) {
  2110. DEBUGMSGW ((
  2111. DBG_FILEENUM,
  2112. "Object %s was found, but it's excluded",
  2113. FileEnum->NativeFullName
  2114. ));
  2115. continue;
  2116. }
  2117. }
  2118. FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExW (
  2119. FileEnum->Location,
  2120. NULL,
  2121. TRUE
  2122. );
  2123. } else {
  2124. FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
  2125. FileEnum->Name = /*lint -e(613)*/currentNode->FindData.cFileName;
  2126. //
  2127. // test if the filename matches
  2128. //
  2129. if (!(FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTLEAF | OBSPF_OPTIONALLEAF)) &&
  2130. !pTestLeafPatternW (
  2131. FileEnum->FileEnumInfo.PathPattern->LeafPattern,
  2132. /*lint -e(613)*/currentNode->FindData.cFileName
  2133. )
  2134. ) {
  2135. continue;
  2136. }
  2137. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  2138. if (ElIsExcluded2W (ELT_FILE, NULL, /*lint -e(613)*/currentNode->FindData.cFileName)) {
  2139. DEBUGMSGW ((
  2140. DBG_FILEENUM,
  2141. "File %s\\%s was found, but it's excluded by filename",
  2142. FileEnum->Location,
  2143. /*lint -e(613)*/currentNode->FindData.cFileName
  2144. ));
  2145. continue;
  2146. }
  2147. }
  2148. if (FileEnum->LastNode != currentNode) {
  2149. FileEnum->LastNode = currentNode;
  2150. //
  2151. // prepare full path buffer
  2152. //
  2153. FileEnum->NativeFullName[0] = 0;
  2154. FileEnum->FileNameAppendPos = StringCatW (FileEnum->NativeFullName, FileEnum->Location);
  2155. if (FileEnum->FileNameAppendPos) {
  2156. *FileEnum->FileNameAppendPos++ = L'\\';
  2157. }
  2158. } else if (!FileEnum->FileNameAppendPos) {
  2159. FileEnum->FileNameAppendPos = GetEndOfStringW (FileEnum->NativeFullName);
  2160. if (FileEnum->FileNameAppendPos) {
  2161. *FileEnum->FileNameAppendPos++ = L'\\';
  2162. }
  2163. }
  2164. MYASSERT (FileEnum->Name && *FileEnum->Name);
  2165. if (FileEnum->FileNameAppendPos + SizeOfStringW (FileEnum->Name) / DWSIZEOF(WCHAR)>
  2166. FileEnum->NativeFullName + DWSIZEOF (FileEnum->NativeFullName) / DWSIZEOF(WCHAR)) {
  2167. DEBUGMSGW ((
  2168. DBG_ERROR,
  2169. "File %s\\%s was found, but it's path is too long",
  2170. FileEnum->Location,
  2171. FileEnum->Name
  2172. ));
  2173. continue;
  2174. }
  2175. StringCopyW (FileEnum->FileNameAppendPos, FileEnum->Name);
  2176. FileEnum->Attributes = /*lint -e(613)*/currentNode->FindData.dwFileAttributes;
  2177. if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
  2178. //
  2179. // check if this object is excluded
  2180. //
  2181. if (ElIsExcluded2W (ELT_FILE, FileEnum->Location, FileEnum->Name)) {
  2182. DEBUGMSGW ((
  2183. DBG_FILEENUM,
  2184. "Object %s was found, but it's excluded",
  2185. FileEnum->NativeFullName
  2186. ));
  2187. continue;
  2188. }
  2189. }
  2190. FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExW (
  2191. FileEnum->Location,
  2192. FileEnum->Name,
  2193. TRUE
  2194. );
  2195. }
  2196. if (FileEnum->LastWackPtr) {
  2197. *FileEnum->LastWackPtr = 0;
  2198. }
  2199. FileEnum->CurrentLevel = FileEnum->FileEnumInfo.RootLevel + /*lint -e(613)*/currentNode->SubLevel;
  2200. return TRUE;
  2201. }
  2202. //
  2203. // try the next root
  2204. //
  2205. if (FileEnum->RootState == FES_ROOT_DONE) {
  2206. break;
  2207. }
  2208. MYASSERT (FileEnum->RootState == FES_ROOT_NEXT);
  2209. MYASSERT (FileEnum->DriveEnum);
  2210. success = pEnumNextFileRootW (FileEnum);
  2211. } while (success);
  2212. AbortEnumFileInTreeW (FileEnum);
  2213. return FALSE;
  2214. }
  2215. /*++
  2216. Routine Description:
  2217. AbortEnumFileInTree aborts the enumeration, freeing all resources allocated
  2218. Arguments:
  2219. FileEnum - Specifies the current enum context; receives a "clean" context
  2220. Return Value:
  2221. none
  2222. --*/
  2223. VOID
  2224. AbortEnumFileInTreeA (
  2225. IN OUT PFILETREE_ENUMA FileEnum
  2226. )
  2227. {
  2228. while (pDeleteDirNodeA (FileEnum, TRUE)) {
  2229. }
  2230. GbFree (&FileEnum->FileNodes);
  2231. if (FileEnum->EncodedFullName) {
  2232. ObsFreeA (FileEnum->EncodedFullName);
  2233. FileEnum->EncodedFullName = NULL;
  2234. }
  2235. if (FileEnum->FileEnumInfo.PathPattern) {
  2236. ObsDestroyParsedPatternA (FileEnum->FileEnumInfo.PathPattern);
  2237. FileEnum->FileEnumInfo.PathPattern = NULL;
  2238. }
  2239. if (FileEnum->DriveEnum) {
  2240. pFileFreeMemory (FileEnum->DriveEnum);
  2241. FileEnum->DriveEnum = NULL;
  2242. }
  2243. }
  2244. VOID
  2245. AbortEnumFileInTreeW (
  2246. IN OUT PFILETREE_ENUMW FileEnum
  2247. )
  2248. {
  2249. while (pDeleteDirNodeW (FileEnum, TRUE)) {
  2250. }
  2251. GbFree (&FileEnum->FileNodes);
  2252. if (FileEnum->EncodedFullName) {
  2253. ObsFreeW (FileEnum->EncodedFullName);
  2254. FileEnum->EncodedFullName = NULL;
  2255. }
  2256. if (FileEnum->FileEnumInfo.PathPattern) {
  2257. ObsDestroyParsedPatternW (FileEnum->FileEnumInfo.PathPattern);
  2258. FileEnum->FileEnumInfo.PathPattern = NULL;
  2259. }
  2260. if (FileEnum->DriveEnum) {
  2261. pFileFreeMemory (FileEnum->DriveEnum);
  2262. FileEnum->DriveEnum = NULL;
  2263. }
  2264. }