#include "precomp.h" #pragma hdrstop #if SCAN_DEBUG BOOL scan_dprinton = FALSE; #define dprintf(_x_) if (scan_dprinton) printf _x_ #else #define dprintf(_x_) #endif #define FlagOn(_mask,_flag) (((_mask) & (_flag)) != 0) #define FlagOff(_mask,_flag) (((_mask) & (_flag)) == 0) #define SetFlag(_mask,_flag) ((_mask) |= (_flag)) #define ClearFlag(_mask,_flag) ((_mask) &= ~(_flag)) typedef struct _CONTAINER_ENTRY { LIST_ENTRY SiblingListEntry; LIST_ENTRY ContainerList; LIST_ENTRY ObjectList; struct _CONTAINER_ENTRY *Parent; PVOID UserData; } CONTAINER_ENTRY, *PCONTAINER_ENTRY; typedef struct _OBJECT_ENTRY { LIST_ENTRY SiblingListEntry; PVOID UserData; } OBJECT_ENTRY, *POBJECT_ENTRY; #define InitializeContainer(_container,_parent) { \ InitializeListHead(&(_container)->ContainerList); \ InitializeListHead(&(_container)->ObjectList); \ (_container)->Parent = (PCONTAINER_ENTRY)(_parent); \ } #define InitializeObject(_object) #define InsertContainer(_container,_subcontainer) \ InsertTailList(&(_container)->ContainerList,&(_subcontainer)->SiblingListEntry) #define InsertObject(_container,_object) \ InsertTailList(&(_container)->ObjectList,&(_object)->SiblingListEntry) #define RemoveObject(_object) RemoveEntryList(&(_object)->SiblingListEntry) #define RemoveContainer(_container) RemoveEntryList(&(_container)->SiblingListEntry) #define GetFirstObject(_container) \ ((_container)->ObjectList.Flink != &(_container)->ObjectList ? \ CONTAINING_RECORD( (_container)->ObjectList.Flink, \ OBJECT_ENTRY, \ SiblingListEntry ) : NULL) #define GetNextObject(_container,_object) \ ((_object)->SiblingListEntry.Flink != &(_container)->ObjectList ? \ CONTAINING_RECORD( (_object)->SiblingListEntry.Flink, \ OBJECT_ENTRY, \ SiblingListEntry ) : NULL) #define GetFirstContainer(_container) \ ((_container)->ContainerList.Flink != &(_container)->ContainerList ? \ CONTAINING_RECORD( (_container)->ContainerList.Flink, \ CONTAINER_ENTRY, \ SiblingListEntry ) : NULL) #define GetNextContainer(_container) \ ((_container)->SiblingListEntry.Flink != &(_container)->Parent->ContainerList ? \ CONTAINING_RECORD( (_container)->SiblingListEntry.Flink, \ CONTAINER_ENTRY, \ SiblingListEntry ) : NULL) #define GetParent(_container) ((_container)->Parent) #define GetParentUserData(_container) &(GetParent(_container))->UserData typedef struct _DIRECTORY_ENTRY { CONTAINER_ENTRY ; SMALL_WIN32_FIND_DATAW FindData; } DIRECTORY_ENTRY, *PDIRECTORY_ENTRY; typedef struct _FILE_ENTRY { OBJECT_ENTRY ; SMALL_WIN32_FIND_DATAW FindData; } FILE_ENTRY, *PFILE_ENTRY; typedef struct _SCAN_PARAMETERS { PSCAN_FREE_USER_DATA_CALLBACK FreeUserDataCallback; BOOL Recurse; BOOL SkipRoot; DIRECTORY_ENTRY RootDirectoryEntry; } SCAN_PARAMETERS, *PSCAN_PARAMETERS; VOID ScanFreeChildren ( IN PSCAN_PARAMETERS Parameters, IN PCONTAINER_ENTRY Container ); DWORD ScanInitialize ( OUT PVOID *ScanHandle, IN BOOL Recurse, IN BOOL SkipRoot, IN PSCAN_FREE_USER_DATA_CALLBACK FreeUserDataCallback OPTIONAL ) { PSCAN_PARAMETERS params; DWORD size; DWORD error; params = malloc( sizeof(SCAN_PARAMETERS) ); if ( params == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } *ScanHandle = params; params->Recurse = Recurse; params->SkipRoot = SkipRoot; params->FreeUserDataCallback = FreeUserDataCallback; InitializeContainer( ¶ms->RootDirectoryEntry, NULL ); params->RootDirectoryEntry.UserData = NULL; params->RootDirectoryEntry.FindData.cFileName[0] = 0; return NO_ERROR; } DWORD ScanDirectory ( IN PVOID ScanHandle, IN PWCH ScanPath, IN PVOID Context OPTIONAL, IN PSCAN_NEW_DIRECTORY_CALLBACK NewDirectoryCallback OPTIONAL, IN PSCAN_CHECK_DIRECTORY_CALLBACK CheckDirectoryCallback OPTIONAL, IN PSCAN_RECURSE_DIRECTORY_CALLBACK RecurseDirectoryCallback OPTIONAL, IN PSCAN_NEW_FILE_CALLBACK NewFileCallback OPTIONAL, IN PSCAN_CHECK_FILE_CALLBACK CheckFileCallback OPTIONAL ) { BOOL ok; DWORD error; PSCAN_PARAMETERS params; PDIRECTORY_ENTRY rootDirectory; PDIRECTORY_ENTRY currentDirectory; PDIRECTORY_ENTRY newDirectory; PFILE_ENTRY newFile; WIN32_FIND_DATA fileData; HANDLE findHandle; PVOID userData; WCHAR currentDirectoryName[MAX_PATH + 1]; params = ScanHandle; rootDirectory = ¶ms->RootDirectoryEntry; currentDirectory = rootDirectory; wcscpy( currentDirectoryName, ScanPath ); do { wcscat( currentDirectoryName, L"\\*" ); dprintf(( "FindFirst for %ws\n", currentDirectoryName )); findHandle = FindFirstFile( currentDirectoryName, &fileData ); currentDirectoryName[wcslen(currentDirectoryName) - 2] = 0; if ( findHandle != INVALID_HANDLE_VALUE ) { do { if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && (!params->SkipRoot || (currentDirectory != rootDirectory)) ) { dprintf(( " found file %ws\\%ws\n", currentDirectoryName, fileData.cFileName )); newFile = (PFILE_ENTRY)GetFirstObject( currentDirectory ); while ( newFile != NULL ) { if ( _wcsicmp( newFile->FindData.cFileName, fileData.cFileName ) == 0 ) { break; } newFile = (PFILE_ENTRY)GetNextObject( currentDirectory, newFile ); } userData = NULL; if ( newFile != NULL ) { userData = newFile->UserData; } if ( ARGUMENT_PRESENT(NewFileCallback) ) { error = NewFileCallback( Context, currentDirectoryName, (newFile == NULL) ? NULL : &newFile->FindData, &fileData, &userData, ¤tDirectory->UserData ); if ( error != NO_ERROR ) { FindClose( findHandle ); return error; } } if ( newFile != NULL ) { if ( userData != NULL ) { newFile->UserData = userData; } else { RemoveObject( newFile ); free( newFile ); } } else if ( userData != NULL ) { newFile = malloc( sizeof(FILE_ENTRY) - sizeof(WCHAR) + ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR)) ); if ( newFile == NULL ) { if ( (userData != NULL) && (params->FreeUserDataCallback != NULL) ) { params->FreeUserDataCallback( userData ); } FindClose( findHandle ); return ERROR_NOT_ENOUGH_MEMORY; } InitializeObject( newFile ); newFile->FindData.dwFileAttributes = fileData.dwFileAttributes; newFile->FindData.ftCreationTime = fileData.ftCreationTime; newFile->FindData.ftLastAccessTime = fileData.ftLastAccessTime; newFile->FindData.ftLastWriteTime = fileData.ftLastWriteTime; newFile->FindData.nFileSizeHigh = fileData.nFileSizeHigh; newFile->FindData.nFileSizeLow = fileData.nFileSizeLow; wcscpy( newFile->FindData.cAlternateFileName, fileData.cAlternateFileName ); wcscpy( newFile->FindData.cFileName, fileData.cFileName ); newFile->UserData = userData; InsertObject( currentDirectory, newFile ); } } else if ( params->Recurse && (wcscmp(fileData.cFileName,L".") != 0) && (wcscmp(fileData.cFileName,L"..") != 0) ) { dprintf(( " found directory %ws\\%ws\n", currentDirectoryName, fileData.cFileName )); newDirectory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); while ( newDirectory != NULL ) { if ( _wcsicmp( newDirectory->FindData.cFileName, fileData.cFileName ) == 0 ) { ok = TRUE; break; } newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( newDirectory ); } userData = NULL; if ( newDirectory != NULL ) { userData = newDirectory->UserData; } if ( ARGUMENT_PRESENT(NewDirectoryCallback) ) { error = NewDirectoryCallback( Context, currentDirectoryName, (newDirectory == NULL) ? NULL : &newDirectory->FindData, &fileData, &userData, ¤tDirectory->UserData ); if ( error != NO_ERROR ) { FindClose( findHandle ); return error; } } if ( newDirectory != NULL ) { newDirectory->UserData = userData; } else if ( userData != NULL ) { newDirectory = malloc( sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) + ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR)) ); if ( newDirectory == NULL ) { if ( (userData != NULL) && (params->FreeUserDataCallback != NULL) ) { params->FreeUserDataCallback( userData ); } FindClose( findHandle ); return ERROR_NOT_ENOUGH_MEMORY; } InitializeContainer( newDirectory, currentDirectory ); newDirectory->FindData.dwFileAttributes = fileData.dwFileAttributes; newDirectory->FindData.ftCreationTime = fileData.ftCreationTime; newDirectory->FindData.ftLastAccessTime = fileData.ftLastAccessTime; newDirectory->FindData.ftLastWriteTime = fileData.ftLastWriteTime; newDirectory->FindData.nFileSizeHigh = fileData.nFileSizeHigh; newDirectory->FindData.nFileSizeLow = fileData.nFileSizeLow; wcscpy( newDirectory->FindData.cAlternateFileName, fileData.cAlternateFileName ); wcscpy( newDirectory->FindData.cFileName, fileData.cFileName ); newDirectory->UserData = userData; InsertContainer( currentDirectory, newDirectory ); } } ok = FindNextFile( findHandle, &fileData ); } while ( ok ); FindClose( findHandle ); } // findHandle != INVALID_HANDLE_VALUE if ( ARGUMENT_PRESENT(CheckFileCallback) ) { newFile = (PFILE_ENTRY)GetFirstObject( currentDirectory ); while ( newFile != NULL ) { error = CheckFileCallback( Context, currentDirectoryName, &newFile->FindData, &newFile->UserData, ¤tDirectory->UserData ); if ( error != NO_ERROR ) { return error; } newFile = (PFILE_ENTRY)GetNextObject( currentDirectory, newFile ); } } newDirectory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); while ( newDirectory != NULL ) { if ( !ARGUMENT_PRESENT(RecurseDirectoryCallback) || RecurseDirectoryCallback( Context, currentDirectoryName, &newDirectory->FindData, &newDirectory->UserData, ¤tDirectory->UserData ) ) { break; } newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( newDirectory ); } if ( newDirectory != NULL ) { currentDirectory = newDirectory; wcscat( currentDirectoryName, L"\\" ); wcscat( currentDirectoryName, currentDirectory->FindData.cFileName ); } else { while ( TRUE ) { if ( currentDirectory == rootDirectory ) { currentDirectory = NULL; break; } if ( ARGUMENT_PRESENT(CheckDirectoryCallback) ) { error = CheckDirectoryCallback( Context, currentDirectoryName, ¤tDirectory->FindData, ¤tDirectory->UserData, GetParentUserData(currentDirectory) ); if ( error != NO_ERROR ) { return error; } } *wcsrchr(currentDirectoryName, L'\\') = 0; newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory ); while ( newDirectory != NULL ) { if ( !ARGUMENT_PRESENT(RecurseDirectoryCallback) || RecurseDirectoryCallback( Context, currentDirectoryName, &newDirectory->FindData, &newDirectory->UserData, ¤tDirectory->UserData ) ) { break; } newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( newDirectory ); } if ( newDirectory != NULL ) { currentDirectory = newDirectory; wcscat( currentDirectoryName, L"\\" ); wcscat( currentDirectoryName, currentDirectory->FindData.cFileName ); break; } else { currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory ); } } } } while ( currentDirectory != NULL ); return ERROR_SUCCESS; } DWORD ScanEnumTree ( IN PVOID ScanHandle, IN PVOID Context, IN PSCAN_ENUM_DIRECTORY_CALLBACK EnumDirectoryCallback OPTIONAL, IN PSCAN_ENUM_FILE_CALLBACK EnumFileCallback OPTIONAL ) { DWORD error; PSCAN_PARAMETERS params; PDIRECTORY_ENTRY rootDirectory; PDIRECTORY_ENTRY currentDirectory; PDIRECTORY_ENTRY directory; PFILE_ENTRY file; WCHAR relativePath[MAX_PATH + 1]; params = ScanHandle; rootDirectory = (PDIRECTORY_ENTRY)¶ms->RootDirectoryEntry; currentDirectory = rootDirectory; relativePath[0] = 0; do { if ( ARGUMENT_PRESENT(EnumFileCallback) ) { file = (PFILE_ENTRY)GetFirstObject( currentDirectory ); while ( file != NULL ) { error = EnumFileCallback( Context, relativePath, &file->FindData, &file->UserData, ¤tDirectory->UserData ); if ( error != ERROR_SUCCESS ) { dprintf(( "EnumFileCallback returned %d\n", error )); return error; } file = (PFILE_ENTRY)GetNextObject( currentDirectory, file ); } } directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); if ( directory != NULL ) { currentDirectory = directory; wcscat( relativePath, L"\\" ); wcscat( relativePath, currentDirectory->FindData.cFileName ); } else { while ( TRUE ) { if ( currentDirectory == rootDirectory ) { currentDirectory = NULL; break; } *wcsrchr(relativePath, L'\\') = 0; if ( ARGUMENT_PRESENT(EnumDirectoryCallback) ) { error = EnumDirectoryCallback( Context, relativePath, ¤tDirectory->FindData, ¤tDirectory->UserData, GetParentUserData(currentDirectory) ); if ( error != ERROR_SUCCESS ) { dprintf(( "EnumDirectoryCallback returned %d\n", error )); return error; } } directory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory ); if ( directory != NULL ) { currentDirectory = directory; wcscat( relativePath, L"\\" ); wcscat( relativePath, currentDirectory->FindData.cFileName ); break; } else { currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory ); } } } } while ( currentDirectory != NULL ); return ERROR_SUCCESS; } VOID ScanTerminate ( IN PVOID ScanHandle ) { PSCAN_PARAMETERS params; params = ScanHandle; ScanFreeChildren( params, (PCONTAINER_ENTRY)¶ms->RootDirectoryEntry ); if ( (params->RootDirectoryEntry.UserData != NULL) && (params->FreeUserDataCallback != NULL) ) { params->FreeUserDataCallback( params->RootDirectoryEntry.UserData ); } free( params ); return; } VOID ScanFreeChildren ( IN PSCAN_PARAMETERS Parameters, IN PCONTAINER_ENTRY RootContainer ) { PCONTAINER_ENTRY currentContainer; PCONTAINER_ENTRY container; PCONTAINER_ENTRY parent; POBJECT_ENTRY object; #if SCAN_DEBUG WCHAR currentPath[MAX_PATH + 1]; #endif #if SCAN_DEBUG #define CONTAINER_NAME(_container) ((PDIRECTORY_ENTRY)(_container))->FindData.cFileName #define OBJECT_NAME(_object) ((PFILE_ENTRY)(_object))->FindData.cFileName currentPath[0] = 0; #endif currentContainer = RootContainer; do { object = GetFirstObject( currentContainer ); while ( object != NULL ) { #if SCAN_DEBUG dprintf(( "Deleting entry for object %ws\\%ws\n", currentPath, OBJECT_NAME(object) )); #endif RemoveObject( object ); if ( (object->UserData != NULL) && (Parameters->FreeUserDataCallback != NULL) ) { Parameters->FreeUserDataCallback( object->UserData ); } free( object ); object = GetFirstObject( currentContainer ); } container = GetFirstContainer( currentContainer ); if ( container != NULL ) { currentContainer = container; #if SCAN_DEBUG wcscat( currentPath, L"\\" ); wcscat( currentPath, CONTAINER_NAME(currentContainer) ); #endif } else { while ( TRUE ) { if ( currentContainer == RootContainer ) { currentContainer = NULL; break; } #if SCAN_DEBUG dprintf(( "Deleting entry for container %ws\n", currentPath )); *wcsrchr(currentPath, L'\\') = 0; #endif parent = GetParent( currentContainer ); RemoveContainer( currentContainer ); if ( (currentContainer->UserData != NULL) && (Parameters->FreeUserDataCallback != NULL) ) { Parameters->FreeUserDataCallback( currentContainer->UserData ); } free( currentContainer ); currentContainer = GetFirstContainer( parent ); if ( currentContainer != NULL ) { #if SCAN_DEBUG wcscat( currentPath, L"\\" ); wcscat( currentPath, CONTAINER_NAME(currentContainer) ); #endif break; } else { currentContainer = parent; } } } } while ( currentContainer != NULL ); return; }