windows-nt/Source/XPSP1/NT/base/fs/sis/groveler/scan.cpp
2020-09-26 16:20:57 +08:00

350 lines
12 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
scan.cpp
Abstract:
SIS Groveler volume scanning function
Authors:
Cedric Krumbein, 1998
Environment:
User Mode
Revision History:
--*/
#include "all.hxx"
/*****************************************************************************/
// scan_volume() creates the initial queue for a volume.
// It enters every qualified file in the volume into the
// queue by doing a depth-first search of the directory tree.
enum DatabaseException {
DATABASE_ERROR
};
GrovelStatus Groveler::scan_volume(
IN DWORD time_allotted,
IN BOOL start_over,
OUT DWORD *time_consumed,
OUT DWORD *findfirst_count,
OUT DWORD *findnext_count,
OUT DWORD *count_of_files_enqueued)
{
SGNativeQueueEntry queueEntry;
SGNativeStackEntry parentEntry,
subdirEntry;
TFileName parentName,
tempName;
HANDLE dirHandle = NULL;
WIN32_FIND_DATA findData;
DWORD timeConsumed = 0,
findFirstCount = 0,
findNextCount = 0,
numQueueAdditions = 0,
numActions = 0;
ULARGE_INTEGER fileSize,
createTime,
writeTime;
LONG num;
BOOL success;
ASSERT(volumeHandle != NULL);
ASSERT(sgDatabase != NULL);
ASSERT(databaseName != NULL);
#ifdef DEBUG_UNTHROTTLED
timeAllotted = INFINITE;
#else
timeAllotted = time_allotted;
#endif
startAllottedTime = GetTickCount();
// If the start_over flag is set, delete the current database, then
// prepare for the new scan by pushing this volume's root onto the stack.
try {
if (start_over) {
// Sync up with the worker thread. We don't want to delete the existing database
// (if one exists) while the worker thread is in the middle of an (suspended)
// operation.
abortGroveling = TRUE; // also set TRUE in open()
while (grovelStatus != Grovel_ok){
DWORD tmpTimeAllotted = timeAllotted;
timeAllotted = INFINITE;
ASSERT(IsReset(grovelStartEvent));
success = SetEvent(grovelStartEvent);
ASSERT_ERROR(success);
WaitForEvent(grovelStopEvent);
timeAllotted = tmpTimeAllotted;
}
if (!CreateDatabase())
return Grovel_error;
inScan = TRUE;
abortGroveling = FALSE;
}
// The main loop for the scanning process. Pop a directory ID
// from the stack, open and scan it. Continue the loop until
// the time allotted is used up or the stack is empty.
do {
num = sgDatabase->StackGetTop(&parentEntry);
if (num < 0)
throw DATABASE_ERROR;
// If there are no more to-do entries in the stack,
// discard the completed entries and exit the loop.
if (num == 0) {
inScan = FALSE;
num = sgDatabase->StackDelete(0);
if (num < 0)
throw DATABASE_ERROR;
timeConsumed = GetTickCount() - startAllottedTime;
break;
}
ASSERT(num == 1);
ASSERT(parentEntry.fileID != 0);
ASSERT(parentEntry.order > 0);
if (!GetFileName(volumeHandle, parentEntry.fileID, &parentName)) {
DPRINTF((_T("%s: can't get name for directory 0x%016I64x\n"),
driveName, parentEntry.fileID));
} else if (IsAllowedName(parentName.name)) {
// Open the directory.
ASSERT(dirHandle == NULL);
tempName.assign(driveName);
tempName.append(parentName.name);
tempName.append(_T("\\*"));
dirHandle = FindFirstFile(tempName.name, &findData);
if (dirHandle == INVALID_HANDLE_VALUE) {
DPRINTF((_T("%s: can't read directory \"%s\": %lu\n"),
driveName, parentName.name, GetLastError()));
dirHandle = NULL;
} else {
findFirstCount++;
// Scan the directory.
do {
findNextCount++;
// Push every subdirectory not already on the stack
// onto the stack. (extract_log() also adds directories
// to the stack as they are created, renamed, or moved.)
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT))
continue;
if (_tcscmp(findData.cFileName, _T(".")) == 0
|| _tcscmp(findData.cFileName, _T("..")) == 0)
continue;
tempName.assign(driveName);
tempName.append(parentName.name);
tempName.append(_T("\\"));
tempName.append(findData.cFileName);
subdirEntry.fileID = GetFileID(tempName.name);
if (subdirEntry.fileID == 0) {
DPRINTF((_T("%s: can't get ID for directory \"%s\"\n"),
driveName, tempName.name));
continue;
}
num = sgDatabase->StackGetFirstByFileID(&subdirEntry);
if (num < 0)
throw DATABASE_ERROR;
if (num > 0) {
ASSERT(num == 1);
continue;
}
if (numActions == 0) {
if (sgDatabase->BeginTransaction() < 0)
throw DATABASE_ERROR;
numActions = 1;
}
num = sgDatabase->StackPut(subdirEntry.fileID, FALSE);
if (num < 0)
throw DATABASE_ERROR;
ASSERT(num == 1);
numActions++;
}
// Add every allowed file to the queue.
else {
fileSize.HighPart = findData.nFileSizeHigh;
fileSize.LowPart = findData.nFileSizeLow;
if ((findData.dwFileAttributes & disallowedAttributes) == 0
&& fileSize.QuadPart >= minFileSize) {
tempName.assign(parentName.name);
tempName.append(_T("\\"));
tempName.append(findData.cFileName);
if (IsAllowedName(tempName.name)) {
queueEntry.fileID = 0;
queueEntry.parentID = parentEntry.fileID;
queueEntry.reason = 0;
queueEntry.fileName = findData.cFileName;
queueEntry.retryTime = 0;
createTime.HighPart = findData.ftCreationTime .dwHighDateTime;
createTime.LowPart = findData.ftCreationTime .dwLowDateTime;
writeTime .HighPart = findData.ftLastWriteTime.dwHighDateTime;
writeTime .LowPart = findData.ftLastWriteTime.dwLowDateTime;
queueEntry.readyTime = (createTime.QuadPart > writeTime.QuadPart
? createTime.QuadPart : writeTime.QuadPart)
+ minFileAge;
if (numActions == 0) {
if (sgDatabase->BeginTransaction() < 0)
throw DATABASE_ERROR;
numActions = 1;
}
num = sgDatabase->QueuePut(&queueEntry);
if (num < 0)
throw DATABASE_ERROR;
ASSERT(num == 1);
numQueueAdditions++;
numActions++;
}
}
}
if (numActions >= MAX_ACTIONS_PER_TRANSACTION) {
if (!sgDatabase->CommitTransaction())
throw DATABASE_ERROR;
TPRINTF((_T("%s: committing %lu actions to \"%s\"\n"),
driveName, numActions, databaseName));
numActions = 0;
}
} while (FindNextFile(dirHandle, &findData));
// We've finished scanning this directory. Close the directory,
// move the stack entry from the to-do list to the completed
// list, and commit the changes to the stack and queue.
success = FindClose(dirHandle);
ASSERT(success);
dirHandle = NULL;
}
}
if (numActions == 0) {
if (sgDatabase->BeginTransaction() < 0)
throw DATABASE_ERROR;
numActions = 1;
}
num = sgDatabase->StackDelete(parentEntry.order);
if (num < 0)
throw DATABASE_ERROR;
ASSERT(num == 1);
numActions++;
num = sgDatabase->StackPut(parentEntry.fileID, TRUE);
if (num < 0)
throw DATABASE_ERROR;
ASSERT(num == 1);
numActions++;
if (!sgDatabase->CommitTransaction())
throw DATABASE_ERROR;
TPRINTF((_T("%s: committing %lu actions to \"%s\"\n"),
driveName, numActions, databaseName));
numActions = 0;
// Continue scanning directories until the time
// allotted is used up or the stack is empty.
timeConsumed = GetTickCount() - startAllottedTime;
} while (timeConsumed < timeAllotted);
}
// If a database error occured, close the directory and return an error status.
catch (DatabaseException databaseException) {
ASSERT(databaseException == DATABASE_ERROR);
if (numActions > 0) {
sgDatabase->AbortTransaction();
numActions = 0;
}
if (dirHandle != NULL) {
success = FindClose(dirHandle);
ASSERT(success);
dirHandle = NULL;
}
return Grovel_error;
}
// Return the performance statistics.
if (time_consumed != NULL)
*time_consumed = timeConsumed;
if (findfirst_count != NULL)
*findfirst_count = findFirstCount;
if (findnext_count != NULL)
*findnext_count = findNextCount;
if (count_of_files_enqueued != NULL)
*count_of_files_enqueued = numQueueAdditions;
TRACE_PRINTF(TC_scan, 2,
(_T("%s: ScanTime=%lu.%03lu sec FindFirst=%lu FindNext=%lu FilesEnqueued=%lu%s\n"),
driveName, timeConsumed / 1000, timeConsumed % 1000, findFirstCount,
findNextCount, numQueueAdditions, inScan ? _T("") : _T(" DONE")));
return inScan ? Grovel_pending : Grovel_ok;
}