350 lines
12 KiB
C++
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;
|
|
}
|