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.
971 lines
27 KiB
971 lines
27 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fileenum.c
|
|
|
|
Abstract:
|
|
|
|
The code in this source file traverses a drive tree and calls
|
|
an external callback function for each file. An INF can be
|
|
provided to exclude files and/or directories from enumeration.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 16-Aug-1996
|
|
|
|
Revision History:
|
|
|
|
Marc R. Whitten (marcw) 11-Sep-1997 Tweaked exclusion handling code, removed
|
|
obsolete code.
|
|
|
|
Mike Condra (mikeco) 02-Jun-1996 Add fns to tap into file/path exclusion
|
|
|
|
Jim Schmidt (jimschm) 20-Dec-1996 Added callback levels and made single
|
|
source file for both A and W versions
|
|
Jim Schmidt (jimschm) 27-Nov-1996 Added level and filter to EnumTree
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "migshared.h"
|
|
|
|
typedef struct {
|
|
FILEENUMPROCA fnEnumCallback;
|
|
FILEENUMFAILPROCA fnFailCallback;
|
|
DWORD EnumID;
|
|
LPVOID pParam;
|
|
DWORD Levels;
|
|
DWORD CurrentLevel;
|
|
DWORD AttributeFilter;
|
|
} ENUMSTRUCTA, *PENUMSTRUCTA;
|
|
|
|
BOOL EnumTreeEngineA (LPCSTR CurrentPath, PENUMSTRUCTA pes);
|
|
BOOL IsPathExcludedA (DWORD EnumID, LPCSTR Path);
|
|
BOOL IsFileExcludedA (DWORD EnumID, LPCSTR File, BYTE byBitmask[]);
|
|
BOOL BuildExclusionsFromInfA (DWORD EnumID, PEXCLUDEINFA ExcludeInfStruct);
|
|
void CreateBitmaskA (DWORD EnumID, LPCSTR FindPattern, BYTE byBitmask[]);
|
|
|
|
|
|
typedef struct {
|
|
FILEENUMPROCW fnEnumCallback;
|
|
FILEENUMFAILPROCW fnFailCallback;
|
|
DWORD EnumID;
|
|
LPVOID pParam;
|
|
DWORD Levels;
|
|
DWORD CurrentLevel;
|
|
DWORD AttributeFilter;
|
|
} ENUMSTRUCTW, *PENUMSTRUCTW;
|
|
|
|
BOOL EnumTreeEngineW (LPCWSTR CurrentPath, PENUMSTRUCTW pes);
|
|
BOOL IsPathExcludedW (DWORD EnumID, LPCWSTR Path);
|
|
BOOL IsFileExcludedW (DWORD EnumID, LPCWSTR File, BYTE byBitmask[]);
|
|
BOOL BuildExclusionsFromInfW (DWORD EnumID, PEXCLUDEINFW ExcludeInfStruct);
|
|
void CreateBitmaskW (DWORD EnumID, LPCWSTR FindPattern, BYTE byBitmask[]);
|
|
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
BOOL
|
|
WINAPI
|
|
FileEnum_Entry (
|
|
IN HINSTANCE hinstDLL,
|
|
IN DWORD dwReason,
|
|
IN LPVOID lpv)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FileEnum_Entry is called after the C runtime is initialized, and its purpose
|
|
is to initialize the globals for this process. For this LIB, it
|
|
does nothing.
|
|
|
|
Arguments:
|
|
|
|
hinstDLL - (OS-supplied) Instance handle for the DLL
|
|
dwReason - (OS-supplied) Type of initialization or termination
|
|
lpv - (OS-supplied) Unused
|
|
|
|
Return Value:
|
|
|
|
TRUE because DLL always initializes properly.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
break;
|
|
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
GenerateEnumID maintains a static that is used to generate unique
|
|
enumeration handles for callers. The enumeration handle is guaranteed to
|
|
be unique for the first 2^32 calls.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
A DWORD enumeration handle that may be used to identify an exclusion
|
|
list.
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
GenerateEnumID (
|
|
void
|
|
)
|
|
{
|
|
static DWORD s_EnumID = 0;
|
|
|
|
return ++s_EnumID;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef UNICODE
|
|
|
|
#define FILEENUMPROCT FILEENUMPROCW
|
|
#define FILEENUMFAILPROCT FILEENUMFAILPROCW
|
|
#define PEXCLUDEINFT PEXCLUDEINFW
|
|
#define ENUMSTRUCTT ENUMSTRUCTW
|
|
#define PENUMSTRUCTT PENUMSTRUCTW
|
|
#define EnumerateAllDrivesT EnumerateAllDrivesW
|
|
#define EnumerateTreeT EnumerateTreeW
|
|
#define EnumTreeEngineT EnumTreeEngineW
|
|
#define IsPathExcludedT IsPathExcludedW
|
|
#define CreateBitmaskT CreateBitmaskW
|
|
#define IsFileExcludedT IsFileExcludedW
|
|
#define BuildExclusionsFromInfT BuildExclusionsFromInfW
|
|
#define ClearExclusionsT ClearExclusionsW
|
|
#define ExcludeFileT ExcludeFileW
|
|
#define ExcludePathT ExcludePathW
|
|
|
|
#else
|
|
|
|
#define FILEENUMPROCT FILEENUMPROCA
|
|
#define FILEENUMFAILPROCT FILEENUMFAILPROCA
|
|
#define PEXCLUDEINFT PEXCLUDEINFA
|
|
#define ENUMSTRUCTT ENUMSTRUCTA
|
|
#define PENUMSTRUCTT PENUMSTRUCTA
|
|
#define EnumerateAllDrivesT EnumerateAllDrivesA
|
|
#define EnumerateTreeT EnumerateTreeA
|
|
#define EnumTreeEngineT EnumTreeEngineA
|
|
#define IsPathExcludedT IsPathExcludedA
|
|
#define CreateBitmaskT CreateBitmaskA
|
|
#define IsFileExcludedT IsFileExcludedA
|
|
#define BuildExclusionsFromInfT BuildExclusionsFromInfA
|
|
#define ClearExclusionsT ClearExclusionsA
|
|
#define ExcludeFileT ExcludeFileA
|
|
#define ExcludePathT ExcludePathA
|
|
|
|
#endif
|
|
|
|
#define MAX_DRIVES 64
|
|
|
|
|
|
BOOL
|
|
EnumerateAllDrivesT (
|
|
IN FILEENUMPROCT fnEnumCallback,
|
|
IN FILEENUMFAILPROCT fnFailCallback,
|
|
IN DWORD EnumID,
|
|
IN LPVOID pParam,
|
|
IN PEXCLUDEINFT ExcludeInfStruct,
|
|
IN DWORD AttributeFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumerateAllDrives first builds an exclusion list if an exclusion INF path
|
|
is provided, and then enumerates every file on every drive that is not
|
|
excluded. The callback function is called once per file. The pParam
|
|
parameter is passed to the callback.
|
|
|
|
Arguments:
|
|
|
|
fnEnumCallback - A pointer to your callback function
|
|
EnumID - A caller-defined value used to identify the exclusion list
|
|
pParam - LPVOID passed to callback function
|
|
ExcludeInfStruct - Struct containing INF file information for excluding dirs or files
|
|
AttributeFilter - FILTER_xxx constants
|
|
|
|
Return Value:
|
|
|
|
TRUE if function succeeds. Call GetLastError for error code if return
|
|
value is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR LogicalDrives[MAX_DRIVES];
|
|
DWORD rc;
|
|
PCTSTR p;
|
|
UINT driveType;
|
|
|
|
rc = GetLogicalDriveStrings (
|
|
MAX_DRIVES,
|
|
LogicalDrives
|
|
);
|
|
|
|
if (!rc || rc > MAX_DRIVES) {
|
|
if (rc)
|
|
SetLastError (ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
for (p = LogicalDrives ; *p ; p = GetEndOfString (p) + 1) {
|
|
|
|
driveType = GetDriveType(p);
|
|
if (driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM) {
|
|
continue;
|
|
}
|
|
|
|
|
|
if (!EnumerateTreeT (p,
|
|
fnEnumCallback,
|
|
fnFailCallback,
|
|
EnumID,
|
|
pParam,
|
|
ENUM_ALL_LEVELS,
|
|
ExcludeInfStruct,
|
|
AttributeFilter
|
|
))
|
|
break;
|
|
}
|
|
|
|
return (*p == 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
EnumerateTreeT (
|
|
IN PCTSTR EnumRoot,
|
|
IN FILEENUMPROCT fnEnumCallback,
|
|
IN FILEENUMFAILPROCT fnFailCallback, OPTIONAL
|
|
IN DWORD EnumID,
|
|
IN LPVOID pParam,
|
|
IN DWORD Levels,
|
|
IN PEXCLUDEINFT ExcludeInfStruct, OPTIONAL
|
|
IN DWORD AttributeFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumerateTree is similar to EnumerateAllDrives, except it allows you to
|
|
enumerate a specific drive, or a specific subdir on a drive. Supply the
|
|
drive letter and optional subdirectory in EnumRoot. Before enumerating,
|
|
EnumerateTree will first build an exclusion list if an exclusion INF path
|
|
is provided. Then every file below EnumRoot is enumerated, and the
|
|
callback is called once per file, passing pParam unchanged.
|
|
|
|
Arguments:
|
|
|
|
EnumRoot - Drive and optional path to enumerate
|
|
fnEnumCallback - A pointer to your callback function
|
|
fnFailCallback - A pointer to optional fn that logs enumeration errors
|
|
EnumID - A caller-defined value used to identify the exclusion list
|
|
pParam - LPVOID passed to callback function
|
|
ExcludeInfStruct - Struct containing INF file information for excluding dirs or files
|
|
AttributeFilter - FILTER_xxx constants
|
|
|
|
Return Value:
|
|
|
|
TRUE if function succeeds. Call GetLastError for error code if return
|
|
value is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENUMSTRUCTT es;
|
|
BOOL b;
|
|
|
|
if (ExcludeInfStruct)
|
|
if (!BuildExclusionsFromInfT (
|
|
EnumID,
|
|
ExcludeInfStruct
|
|
)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error in exclusion file"));
|
|
return FALSE;
|
|
}
|
|
|
|
es.fnEnumCallback = fnEnumCallback;
|
|
es.fnFailCallback = fnFailCallback;
|
|
es.EnumID = EnumID;
|
|
es.pParam = pParam;
|
|
es.Levels = Levels;
|
|
es.CurrentLevel = 1;
|
|
es.AttributeFilter = AttributeFilter;
|
|
|
|
if (!IsPathLengthOk(EnumRoot))
|
|
{
|
|
if (NULL != fnFailCallback)
|
|
{
|
|
fnFailCallback(EnumRoot);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (IsPathExcludedT (EnumID, EnumRoot))
|
|
return TRUE;
|
|
|
|
b = EnumTreeEngineT (EnumRoot, &es);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumTreeEngineT (
|
|
PCTSTR CurrentPath,
|
|
PENUMSTRUCTT pes
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd; // A find struct for this subdir
|
|
HANDLE hFind; // A find handle for this subdir
|
|
PTSTR FullFilePath; // Buffer used to build file path
|
|
static TCHAR FindPattern[MAX_TCHAR_PATH * 2]; // Temp buffer used to build pattern
|
|
BYTE byBitmask[MAX_PATH]; // Bitmask is used to speed exclusion lookup
|
|
static DWORD Attrib; // Temp attribute storage for filter processing
|
|
static INT rc; // Callback return value
|
|
DWORD PrevLevelCt; // Storage for parent's max depth setting
|
|
BOOL RecurseStatus;
|
|
DWORD CurrentDirData = 0;
|
|
|
|
//
|
|
// Do nothing when CurrentPath is at the size limit.
|
|
//
|
|
if (!IsPathLengthOk(CurrentPath))
|
|
{
|
|
if (NULL != pes->fnFailCallback)
|
|
{
|
|
pes->fnFailCallback(CurrentPath);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
PrevLevelCt = pes->Levels;
|
|
|
|
|
|
StringCopy (FindPattern, CurrentPath);
|
|
|
|
//
|
|
// Create a bitmask that tells us when subdirectories match partial
|
|
// file patterns.
|
|
//
|
|
|
|
ZeroMemory (byBitmask, sizeof (byBitmask));
|
|
CreateBitmaskT (pes->EnumID, FindPattern, byBitmask);
|
|
|
|
AppendPathWack (FindPattern);
|
|
StringCat (FindPattern, TEXT("*"));
|
|
hFind = FindFirstFile (FindPattern, &fd);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
|
|
|
FullFilePath = JoinPaths (CurrentPath, fd.cFileName);
|
|
|
|
__try {
|
|
//
|
|
// Ignore this path if FullFilePath is too long
|
|
// this way fd.cFileName will surely be within limits (since it's shorter)
|
|
//
|
|
if (!IsPathLengthOk (FullFilePath)) {
|
|
if (NULL != pes->fnFailCallback) {
|
|
pes->fnFailCallback(FullFilePath);
|
|
}
|
|
__leave;
|
|
}
|
|
|
|
// Filter directories named ".", "..". Set Attrib symbol.
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
if (!StringCompare (fd.cFileName, TEXT(".")) ||
|
|
!StringCompare (fd.cFileName, TEXT("..")))
|
|
__leave;
|
|
|
|
Attrib = FILTER_DIRECTORIES;
|
|
} else {
|
|
Attrib = FILTER_FILES;
|
|
}
|
|
|
|
// Call the callback
|
|
if (Attrib & pes->AttributeFilter) {
|
|
rc = CALLBACK_CONTINUE;
|
|
|
|
switch (Attrib) {
|
|
case FILTER_DIRECTORIES:
|
|
// Ignore excluded paths
|
|
if (IsPathExcludedT (pes->EnumID, FullFilePath)) {
|
|
break;
|
|
}
|
|
|
|
// Callback for 'directory first'
|
|
if (!(pes->AttributeFilter & FILTER_DIRS_LAST)) {
|
|
rc = pes->fnEnumCallback (
|
|
FullFilePath,
|
|
NULL,
|
|
&fd,
|
|
pes->EnumID,
|
|
pes->pParam,
|
|
&CurrentDirData
|
|
);
|
|
}
|
|
|
|
if (rc >= CALLBACK_CONTINUE && pes->CurrentLevel != pes -> Levels) {
|
|
// Recurse on directory
|
|
pes->CurrentLevel++;
|
|
RecurseStatus = EnumTreeEngineT (FullFilePath, pes);
|
|
pes->CurrentLevel--;
|
|
if (!RecurseStatus) {
|
|
PushError();
|
|
FindClose(hFind);
|
|
PopError();
|
|
|
|
#pragma prefast(suppress:242, "try/finally perf not an issue here")
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Callback for 'directory last'
|
|
if (pes->AttributeFilter & FILTER_DIRS_LAST) {
|
|
rc = pes->fnEnumCallback (
|
|
FullFilePath,
|
|
NULL,
|
|
&fd,
|
|
pes->EnumID,
|
|
pes->pParam,
|
|
&CurrentDirData
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case FILTER_FILES:
|
|
if (!IsFileExcludedT (pes->EnumID, FullFilePath, byBitmask)) {
|
|
rc = pes->fnEnumCallback (FullFilePath,
|
|
NULL,
|
|
&fd,
|
|
pes->EnumID,
|
|
pes->pParam,
|
|
&CurrentDirData
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (rc == CALLBACK_FAILED) {
|
|
PushError();
|
|
FindClose (hFind);
|
|
PopError();
|
|
|
|
#pragma prefast(suppress:242, "try/finally perf not an issue here")
|
|
return FALSE;
|
|
}
|
|
else if (rc == CALLBACK_SUBDIR_DONE) {
|
|
#pragma prefast(suppress:242, "try/finally perf not an issue here")
|
|
break;
|
|
}
|
|
else if (rc > 0) {
|
|
pes->Levels = pes->CurrentLevel + rc;
|
|
}
|
|
}
|
|
else if (Attrib == FILTER_DIRECTORIES && !IsPathExcludedT (pes->EnumID, FullFilePath)) {
|
|
// Recurse on directory.
|
|
if (pes->CurrentLevel != pes -> Levels) {
|
|
|
|
pes->CurrentLevel++;
|
|
RecurseStatus = EnumTreeEngineT (FullFilePath, pes);
|
|
pes->CurrentLevel--;
|
|
if (!RecurseStatus) {
|
|
PushError();
|
|
FindClose(hFind);
|
|
PopError();
|
|
|
|
#pragma prefast(suppress:242, "try/finally perf not an issue here")
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
FreePathString (FullFilePath);
|
|
}
|
|
} while (FindNextFile (hFind, &fd));
|
|
|
|
FindClose (hFind);
|
|
|
|
//
|
|
// Test error code returned from FindNextFile
|
|
//
|
|
if (GetLastError() != ERROR_NO_MORE_FILES && GetLastError() != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Caller to handle not-ready message
|
|
//
|
|
if (GetLastError() != ERROR_NOT_READY)
|
|
{
|
|
DEBUGMSG((DBG_ERROR,
|
|
"EnumTreeEngineT: Error from FindNextFile.\n"
|
|
" FindPattern: %s\n"
|
|
" Error: %u (%x)",
|
|
FindPattern,
|
|
GetLastError(),GetLastError()));
|
|
}
|
|
return FALSE;
|
|
}
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Test return codes from FindFirstFile
|
|
//
|
|
if (GetLastError () != ERROR_NO_MORE_FILES)
|
|
{
|
|
//
|
|
// Caller to handle not-ready message
|
|
//
|
|
if (GetLastError() != ERROR_NOT_READY)
|
|
{
|
|
DEBUGMSG((DBG_WARNING,
|
|
"EnumTreeEngineT: Warning from FindFirstFile.\n"
|
|
" FindPattern: %s\n",
|
|
FindPattern));
|
|
}
|
|
// return FALSE;
|
|
}
|
|
SetLastError (ERROR_SUCCESS);
|
|
}
|
|
|
|
// If a callback returned a positive, non-zero number, the depth
|
|
// of the subdirectory search was limited for this level. Now that
|
|
// this level is done, we must restore the depth value of our parent.
|
|
pes->Levels = PrevLevelCt;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
A bitmask is used in IsFileExcluded for faster relative directory searches.
|
|
Instead of looking in the MemDb for each part of the path, IsFileExcluded
|
|
skips segments that are known not to match. We create the bitmask here
|
|
by looking up each portion of FindPattern. Bit 1 is set if the last
|
|
subdirectory exists in the file exclusion list, Bit 2 is set if the last
|
|
two subdirectories exist in the file exclusion list, and so on.
|
|
|
|
For example, assume FindPattern is set to C:\DEV\FOO\BAR. CreateBitmask
|
|
first looks in the memory database for BAR\*, and if it is found bit 1 is set.
|
|
Then CreateBitmask looks in the memory database for FOO\BAR\*, and sets bit
|
|
2. Again the function looks up DEV\FOO\BAR\* for bit 3 and finally
|
|
C:\DEV\FOO\BAR\* for bit 4.
|
|
|
|
Bit 0 is always set (empty paths always match).
|
|
|
|
Once this bitmask is set up, IsFileExcluded can test only the patterns that
|
|
are known to exist.
|
|
|
|
--*/
|
|
|
|
void
|
|
CreateBitmaskT (
|
|
DWORD EnumID,
|
|
PCTSTR FindPattern,
|
|
BYTE byBitmask[]
|
|
)
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
TCHAR ShortPath[MAX_TCHAR_PATH * 2];
|
|
PCTSTR p;
|
|
PTSTR End;
|
|
int nByte;
|
|
int nBitVal;
|
|
|
|
// Always set bit 0
|
|
byBitmask[0] |= 1;
|
|
|
|
// Build full file spec
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES
|
|
);
|
|
|
|
End = GetEndOfString (EnumPath);
|
|
StringCopy (End, FindPattern);
|
|
AppendPathWack (End);
|
|
StringCat (End, TEXT("*"));
|
|
|
|
// Start with last subdirectory, and build mask in reverse
|
|
p = _tcsrchr (EnumPath, TEXT('\\'));
|
|
nByte = 0;
|
|
nBitVal = 2;
|
|
do {
|
|
// Move back to previous backslash
|
|
for (p = _tcsdec (EnumPath, p) ;
|
|
p >= End && *p != TEXT('\\') ;
|
|
p = _tcsdec (EnumPath, p))
|
|
{
|
|
}
|
|
|
|
// Check if partial file is in the tree
|
|
wsprintf (
|
|
ShortPath,
|
|
TEXT("%s\\%X\\%s%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES,
|
|
p
|
|
);
|
|
|
|
if (MemDbGetPatternValueWithPattern (ShortPath, NULL))
|
|
byBitmask[nByte] |= nBitVal;
|
|
|
|
// Inc bit pos
|
|
nBitVal *= 2;
|
|
if (nBitVal == 256) {
|
|
nBitVal = 1;
|
|
nByte++;
|
|
}
|
|
} while (p > End);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPathExcludedT (DWORD EnumID, PCTSTR Path)
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
TCHAR ShortPath[MAX_TCHAR_PATH * 2];
|
|
PCTSTR p;
|
|
PTSTR End;
|
|
|
|
// Try full paths
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_PATHS
|
|
);
|
|
|
|
End = GetEndOfString (EnumPath);
|
|
p = _tcsappend (End, Path);
|
|
|
|
if (MemDbGetPatternValue (EnumPath, NULL)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Try partial paths
|
|
do {
|
|
// Move back to previous backslash
|
|
for (p = _tcsdec (EnumPath, p) ;
|
|
p > End && (*p != TEXT('\\')) ;
|
|
p = _tcsdec (EnumPath, p))
|
|
{
|
|
}
|
|
|
|
// Check if partial path is in the tree
|
|
if (p > End && p[1]) {
|
|
wsprintf (
|
|
ShortPath,
|
|
TEXT("%s\\%X\\%s%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_PATHS,
|
|
p
|
|
);
|
|
|
|
if (MemDbGetPatternValue (ShortPath, NULL)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
} while (p > End);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsFileExcludedT (DWORD EnumID, PCTSTR File, BYTE byBitmask[])
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
TCHAR ShortPath[MAX_TCHAR_PATH * 2];
|
|
PCTSTR p;
|
|
PTSTR End;
|
|
int nByte;
|
|
int nBit;
|
|
|
|
// Build full file spec
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES
|
|
);
|
|
|
|
End = GetEndOfString (EnumPath);
|
|
p = _tcsappend (End, File);
|
|
|
|
//
|
|
// Try partial file specs until full spec is reached
|
|
//
|
|
|
|
nByte = 0;
|
|
nBit = 1;
|
|
do {
|
|
//
|
|
// Move back to previous backslash in path
|
|
// (p starts at NULL of EnumPath, End is in the middle of EnumPath)
|
|
//
|
|
|
|
for (p = _tcsdec (EnumPath, p) ;
|
|
p >= End && (*p != TEXT('\\')) ;
|
|
p = _tcsdec (EnumPath, p))
|
|
{
|
|
}
|
|
|
|
// Bitmask is used to make sure slightly expensive query is necessary
|
|
if (byBitmask[nByte] & nBit) {
|
|
|
|
//
|
|
// Check if partial file is in the tree
|
|
//
|
|
|
|
wsprintf (
|
|
ShortPath,
|
|
TEXT("%s\\%X\\%s%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES,
|
|
p
|
|
);
|
|
|
|
if (MemDbGetPatternValue (ShortPath, NULL)) {
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
nBit *= 2;
|
|
if (nBit == 256) {
|
|
nBit = 1;
|
|
nByte++;
|
|
}
|
|
} while (p > End);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// ClearExclusions removes all enumaration exclusions. It is called
|
|
// automatically at the end of enumeration when an exclusion INF file is
|
|
// used. Use it when you need to programmatically build an exclusion list
|
|
// with ExcludeDrive, ExcludePath, and ExcludeFile.
|
|
//
|
|
// You can combine programmatic exclusions with an exclusion INF file, but
|
|
// beware that the programmatic exclusions will be cleared after
|
|
// EnumarteAllDrives or EnumerateTree completes.
|
|
//
|
|
// If you do not use an INF, the programmatic exclusions will not
|
|
// automatically be cleared.
|
|
//
|
|
// EnumID - Caller-defined value to identify enumeration exclusion list
|
|
//
|
|
|
|
VOID
|
|
ClearExclusionsT (
|
|
DWORD EnumID
|
|
)
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
|
|
wsprintf (EnumPath, TEXT("%s\\%X"), MEMDB_CATEGORY_FILEENUM, EnumID);
|
|
|
|
MemDbDeleteTree (EnumPath);
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ExcludePath adds a path name to the exclusion list. There are two
|
|
cases:
|
|
|
|
1. A full path spec is supplied, including the drive letter or
|
|
UNC double-backslash.
|
|
2. The path does not start with a drive letter and is a portion of
|
|
a full path.
|
|
|
|
The dot and double-dot directories are not supported. Any part of
|
|
the path may contain wildcard characters, but a wildcard can not
|
|
be used in place of a backslash.
|
|
|
|
Arguments:
|
|
|
|
EnumID - A caller-defined value that identifies the exclusion list
|
|
Path - The path specification as described above
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
VOID
|
|
ExcludePathT (
|
|
IN DWORD EnumID,
|
|
IN PCTSTR Path
|
|
)
|
|
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_PATHS,
|
|
Path
|
|
);
|
|
|
|
MemDbSetValue (EnumPath, 0);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ExcludeFile adds a file spec to the exclusion list. There are two
|
|
cases:
|
|
|
|
1. A full path spec is supplied, including the drive letter or
|
|
UNC double-backslash.
|
|
2. The path does not start with a drive letter and is a portion of
|
|
a full path.
|
|
|
|
The dot and double-dot directories are not supported. Any part of
|
|
the path may contain wildcard characters, but a wildcard can not
|
|
be used in place of a backslash.
|
|
|
|
Arguments:
|
|
|
|
EnumID - A caller-defined value that identifies the exclusion list
|
|
File - The file specification as described above
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
VOID
|
|
ExcludeFileT (
|
|
IN DWORD EnumID,
|
|
IN PCTSTR File
|
|
)
|
|
|
|
{
|
|
TCHAR EnumPath[MAX_TCHAR_PATH * 2];
|
|
|
|
wsprintf (
|
|
EnumPath,
|
|
TEXT("%s\\%X\\%s\\%s"),
|
|
MEMDB_CATEGORY_FILEENUM,
|
|
EnumID,
|
|
MEMDB_FIELD_FE_FILES,
|
|
File
|
|
);
|
|
|
|
MemDbSetValue (EnumPath, 0);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BuildExclusionsFromInfT (DWORD EnumID,
|
|
PEXCLUDEINFT ExcludeInfStruct)
|
|
{
|
|
HINF hInf;
|
|
INFCONTEXT ic;
|
|
TCHAR Exclude[MAX_TCHAR_PATH * 2];
|
|
|
|
// Attempt to open
|
|
hInf = SetupOpenInfFile (ExcludeInfStruct->ExclusionInfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
if (hInf == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
// Read in path exclusions
|
|
if (ExcludeInfStruct->PathSection) {
|
|
if (SetupFindFirstLine (hInf, ExcludeInfStruct->PathSection, NULL, &ic)) {
|
|
do {
|
|
if (SetupGetStringField (&ic, 1, Exclude, MAX_TCHAR_PATH, NULL)) {
|
|
ExcludePathT (EnumID, Exclude);
|
|
}
|
|
} while (SetupFindNextLine (&ic, &ic));
|
|
}
|
|
}
|
|
|
|
// Read in file exclusions
|
|
if (ExcludeInfStruct->FileSection) {
|
|
if (SetupFindFirstLine (hInf, ExcludeInfStruct->FileSection, NULL, &ic)) {
|
|
do {
|
|
if (SetupGetStringField (&ic, 1, Exclude, MAX_TCHAR_PATH, NULL)) {
|
|
ExcludeFileT (EnumID, Exclude);
|
|
}
|
|
} while (SetupFindNextLine (&ic, &ic));
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
SetupCloseInfFile (hInf);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|