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.

349 lines
12 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. scan.cpp
  5. Abstract:
  6. SIS Groveler volume scanning function
  7. Authors:
  8. Cedric Krumbein, 1998
  9. Environment:
  10. User Mode
  11. Revision History:
  12. --*/
  13. #include "all.hxx"
  14. /*****************************************************************************/
  15. // scan_volume() creates the initial queue for a volume.
  16. // It enters every qualified file in the volume into the
  17. // queue by doing a depth-first search of the directory tree.
  18. enum DatabaseException {
  19. DATABASE_ERROR
  20. };
  21. GrovelStatus Groveler::scan_volume(
  22. IN DWORD time_allotted,
  23. IN BOOL start_over,
  24. OUT DWORD *time_consumed,
  25. OUT DWORD *findfirst_count,
  26. OUT DWORD *findnext_count,
  27. OUT DWORD *count_of_files_enqueued)
  28. {
  29. SGNativeQueueEntry queueEntry;
  30. SGNativeStackEntry parentEntry,
  31. subdirEntry;
  32. TFileName parentName,
  33. tempName;
  34. HANDLE dirHandle = NULL;
  35. WIN32_FIND_DATA findData;
  36. DWORD timeConsumed = 0,
  37. findFirstCount = 0,
  38. findNextCount = 0,
  39. numQueueAdditions = 0,
  40. numActions = 0;
  41. ULARGE_INTEGER fileSize,
  42. createTime,
  43. writeTime;
  44. LONG num;
  45. BOOL success;
  46. ASSERT(volumeHandle != NULL);
  47. ASSERT(sgDatabase != NULL);
  48. ASSERT(databaseName != NULL);
  49. #ifdef DEBUG_UNTHROTTLED
  50. timeAllotted = INFINITE;
  51. #else
  52. timeAllotted = time_allotted;
  53. #endif
  54. startAllottedTime = GetTickCount();
  55. // If the start_over flag is set, delete the current database, then
  56. // prepare for the new scan by pushing this volume's root onto the stack.
  57. try {
  58. if (start_over) {
  59. // Sync up with the worker thread. We don't want to delete the existing database
  60. // (if one exists) while the worker thread is in the middle of an (suspended)
  61. // operation.
  62. abortGroveling = TRUE; // also set TRUE in open()
  63. while (grovelStatus != Grovel_ok){
  64. DWORD tmpTimeAllotted = timeAllotted;
  65. timeAllotted = INFINITE;
  66. ASSERT(IsReset(grovelStartEvent));
  67. success = SetEvent(grovelStartEvent);
  68. ASSERT_ERROR(success);
  69. WaitForEvent(grovelStopEvent);
  70. timeAllotted = tmpTimeAllotted;
  71. }
  72. if (!CreateDatabase())
  73. return Grovel_error;
  74. inScan = TRUE;
  75. abortGroveling = FALSE;
  76. }
  77. // The main loop for the scanning process. Pop a directory ID
  78. // from the stack, open and scan it. Continue the loop until
  79. // the time allotted is used up or the stack is empty.
  80. do {
  81. num = sgDatabase->StackGetTop(&parentEntry);
  82. if (num < 0)
  83. throw DATABASE_ERROR;
  84. // If there are no more to-do entries in the stack,
  85. // discard the completed entries and exit the loop.
  86. if (num == 0) {
  87. inScan = FALSE;
  88. num = sgDatabase->StackDelete(0);
  89. if (num < 0)
  90. throw DATABASE_ERROR;
  91. timeConsumed = GetTickCount() - startAllottedTime;
  92. break;
  93. }
  94. ASSERT(num == 1);
  95. ASSERT(parentEntry.fileID != 0);
  96. ASSERT(parentEntry.order > 0);
  97. if (!GetFileName(volumeHandle, parentEntry.fileID, &parentName)) {
  98. DPRINTF((_T("%s: can't get name for directory 0x%016I64x\n"),
  99. driveName, parentEntry.fileID));
  100. } else if (IsAllowedName(parentName.name)) {
  101. // Open the directory.
  102. ASSERT(dirHandle == NULL);
  103. tempName.assign(driveName);
  104. tempName.append(parentName.name);
  105. tempName.append(_T("\\*"));
  106. dirHandle = FindFirstFile(tempName.name, &findData);
  107. if (dirHandle == INVALID_HANDLE_VALUE) {
  108. DPRINTF((_T("%s: can't read directory \"%s\": %lu\n"),
  109. driveName, parentName.name, GetLastError()));
  110. dirHandle = NULL;
  111. } else {
  112. findFirstCount++;
  113. // Scan the directory.
  114. do {
  115. findNextCount++;
  116. // Push every subdirectory not already on the stack
  117. // onto the stack. (extract_log() also adds directories
  118. // to the stack as they are created, renamed, or moved.)
  119. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
  120. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
  121. (findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT))
  122. continue;
  123. if (_tcscmp(findData.cFileName, _T(".")) == 0
  124. || _tcscmp(findData.cFileName, _T("..")) == 0)
  125. continue;
  126. tempName.assign(driveName);
  127. tempName.append(parentName.name);
  128. tempName.append(_T("\\"));
  129. tempName.append(findData.cFileName);
  130. subdirEntry.fileID = GetFileID(tempName.name);
  131. if (subdirEntry.fileID == 0) {
  132. DPRINTF((_T("%s: can't get ID for directory \"%s\"\n"),
  133. driveName, tempName.name));
  134. continue;
  135. }
  136. num = sgDatabase->StackGetFirstByFileID(&subdirEntry);
  137. if (num < 0)
  138. throw DATABASE_ERROR;
  139. if (num > 0) {
  140. ASSERT(num == 1);
  141. continue;
  142. }
  143. if (numActions == 0) {
  144. if (sgDatabase->BeginTransaction() < 0)
  145. throw DATABASE_ERROR;
  146. numActions = 1;
  147. }
  148. num = sgDatabase->StackPut(subdirEntry.fileID, FALSE);
  149. if (num < 0)
  150. throw DATABASE_ERROR;
  151. ASSERT(num == 1);
  152. numActions++;
  153. }
  154. // Add every allowed file to the queue.
  155. else {
  156. fileSize.HighPart = findData.nFileSizeHigh;
  157. fileSize.LowPart = findData.nFileSizeLow;
  158. if ((findData.dwFileAttributes & disallowedAttributes) == 0
  159. && fileSize.QuadPart >= minFileSize) {
  160. tempName.assign(parentName.name);
  161. tempName.append(_T("\\"));
  162. tempName.append(findData.cFileName);
  163. if (IsAllowedName(tempName.name)) {
  164. queueEntry.fileID = 0;
  165. queueEntry.parentID = parentEntry.fileID;
  166. queueEntry.reason = 0;
  167. queueEntry.fileName = findData.cFileName;
  168. queueEntry.retryTime = 0;
  169. createTime.HighPart = findData.ftCreationTime .dwHighDateTime;
  170. createTime.LowPart = findData.ftCreationTime .dwLowDateTime;
  171. writeTime .HighPart = findData.ftLastWriteTime.dwHighDateTime;
  172. writeTime .LowPart = findData.ftLastWriteTime.dwLowDateTime;
  173. queueEntry.readyTime = (createTime.QuadPart > writeTime.QuadPart
  174. ? createTime.QuadPart : writeTime.QuadPart)
  175. + minFileAge;
  176. if (numActions == 0) {
  177. if (sgDatabase->BeginTransaction() < 0)
  178. throw DATABASE_ERROR;
  179. numActions = 1;
  180. }
  181. num = sgDatabase->QueuePut(&queueEntry);
  182. if (num < 0)
  183. throw DATABASE_ERROR;
  184. ASSERT(num == 1);
  185. numQueueAdditions++;
  186. numActions++;
  187. }
  188. }
  189. }
  190. if (numActions >= MAX_ACTIONS_PER_TRANSACTION) {
  191. if (!sgDatabase->CommitTransaction())
  192. throw DATABASE_ERROR;
  193. TPRINTF((_T("%s: committing %lu actions to \"%s\"\n"),
  194. driveName, numActions, databaseName));
  195. numActions = 0;
  196. }
  197. } while (FindNextFile(dirHandle, &findData));
  198. // We've finished scanning this directory. Close the directory,
  199. // move the stack entry from the to-do list to the completed
  200. // list, and commit the changes to the stack and queue.
  201. success = FindClose(dirHandle);
  202. ASSERT(success);
  203. dirHandle = NULL;
  204. }
  205. }
  206. if (numActions == 0) {
  207. if (sgDatabase->BeginTransaction() < 0)
  208. throw DATABASE_ERROR;
  209. numActions = 1;
  210. }
  211. num = sgDatabase->StackDelete(parentEntry.order);
  212. if (num < 0)
  213. throw DATABASE_ERROR;
  214. ASSERT(num == 1);
  215. numActions++;
  216. num = sgDatabase->StackPut(parentEntry.fileID, TRUE);
  217. if (num < 0)
  218. throw DATABASE_ERROR;
  219. ASSERT(num == 1);
  220. numActions++;
  221. if (!sgDatabase->CommitTransaction())
  222. throw DATABASE_ERROR;
  223. TPRINTF((_T("%s: committing %lu actions to \"%s\"\n"),
  224. driveName, numActions, databaseName));
  225. numActions = 0;
  226. // Continue scanning directories until the time
  227. // allotted is used up or the stack is empty.
  228. timeConsumed = GetTickCount() - startAllottedTime;
  229. } while (timeConsumed < timeAllotted);
  230. }
  231. // If a database error occured, close the directory and return an error status.
  232. catch (DatabaseException databaseException) {
  233. ASSERT(databaseException == DATABASE_ERROR);
  234. if (numActions > 0) {
  235. sgDatabase->AbortTransaction();
  236. numActions = 0;
  237. }
  238. if (dirHandle != NULL) {
  239. success = FindClose(dirHandle);
  240. ASSERT(success);
  241. dirHandle = NULL;
  242. }
  243. return Grovel_error;
  244. }
  245. // Return the performance statistics.
  246. if (time_consumed != NULL)
  247. *time_consumed = timeConsumed;
  248. if (findfirst_count != NULL)
  249. *findfirst_count = findFirstCount;
  250. if (findnext_count != NULL)
  251. *findnext_count = findNextCount;
  252. if (count_of_files_enqueued != NULL)
  253. *count_of_files_enqueued = numQueueAdditions;
  254. TRACE_PRINTF(TC_scan, 2,
  255. (_T("%s: ScanTime=%lu.%03lu sec FindFirst=%lu FindNext=%lu FilesEnqueued=%lu%s\n"),
  256. driveName, timeConsumed / 1000, timeConsumed % 1000, findFirstCount,
  257. findNextCount, numQueueAdditions, inScan ? _T("") : _T(" DONE")));
  258. return inScan ? Grovel_pending : Grovel_ok;
  259. }