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.

549 lines
12 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. LimitFindFile.cpp
  5. Abstract:
  6. This shim was originally intended for QuickTime's qt32inst.exe which
  7. did a breadth-first search of the directory tree and would overflow
  8. the buffer in which it was keeping a list of subdirs yet to visit.
  9. With this shim you can limit the number of files that a single FindFile
  10. search will return, you can limit the number of subdirectories (aka the
  11. branching factor) returned, you can limit the "depth" to which any
  12. FindFile search will locate files, and you can specify whether these
  13. limits should be applied to all FindFiles or only fully-qualified FindFiles.
  14. You can also request that FindFile return only short filenames.
  15. The shim's arguments are:
  16. DEPTH=#
  17. BRANCH=#
  18. FILES=#
  19. SHORTFILENAMES or LONGFILENAMES
  20. LIMITRELATIVE or ALLOWRELATIVE
  21. The default behavior is:
  22. SHORTFILENAMES
  23. DEPTH = 4
  24. ALLOWRELATIVE
  25. ... but if any command line is specified, the behavior is only that which
  26. is specified on the command line (no default behavior).
  27. An example command line:
  28. COMMAND_LINE="FILES=100 LIMITRELATIVE"
  29. Which would limit every FindFile search to returning 100 or fewer files (but
  30. still returning any and all subdirectories).
  31. Note: Depth is a bit tricky. The method used is to count backslashes, so
  32. limiting depth to zero will allow no files to be found ("C:\autorun.bat" has
  33. 1 backslash).
  34. History:
  35. 08/24/2000 t-adams Created
  36. --*/
  37. #include "precomp.h"
  38. IMPLEMENT_SHIM_BEGIN(LimitFindFile)
  39. #include "ShimHookMacro.h"
  40. APIHOOK_ENUM_BEGIN
  41. APIHOOK_ENUM_ENTRY(FindFirstFileA)
  42. APIHOOK_ENUM_ENTRY(FindNextFileA)
  43. APIHOOK_ENUM_ENTRY(FindClose)
  44. APIHOOK_ENUM_END
  45. // Linked list of FindFileHandles
  46. struct FFNode
  47. {
  48. FFNode *next;
  49. HANDLE hFF;
  50. DWORD dwBranches;
  51. DWORD dwFiles;
  52. };
  53. FFNode *g_FFList = NULL;
  54. // Default behaviors - overridden by Commandline
  55. BOOL g_bUseShortNames = TRUE;
  56. BOOL g_bLimitDepth = TRUE;
  57. DWORD g_dwDepthLimit = 4;
  58. BOOL g_bLimitRelative = FALSE;
  59. BOOL g_bLimitBranch = FALSE;
  60. DWORD g_dwBranchLimit = 0;
  61. BOOL g_bLimitFiles = FALSE;
  62. DWORD g_dwFileLimit = 0;
  63. /*++
  64. Abstract:
  65. ApplyLimits applys the recently found file from lpFindFileData to the
  66. current node, checks that none of the limits have been violated, and
  67. shortens the filename if requested.
  68. It returns TRUE if within limits, FALSE if limits have been exceeded.
  69. History:
  70. 08/24/2000 t-adams Created
  71. --*/
  72. BOOL ApplyLimits(FFNode *pFFNode, LPWIN32_FIND_DATAA lpFindFileData)
  73. {
  74. BOOL bRet = TRUE;
  75. // If it's a directory
  76. if ( lpFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  77. {
  78. pFFNode->dwBranches++;
  79. if ( g_bLimitBranch && pFFNode->dwBranches > g_dwBranchLimit )
  80. {
  81. bRet = FALSE;
  82. goto exit;
  83. }
  84. }
  85. else
  86. { // else it's a file
  87. pFFNode->dwFiles++;
  88. if ( g_bLimitFiles && pFFNode->dwFiles > g_dwFileLimit )
  89. {
  90. bRet = FALSE;
  91. goto exit;
  92. }
  93. }
  94. // Change to short name if requested
  95. if ( g_bUseShortNames && NULL != lpFindFileData->cAlternateFileName[0])
  96. {
  97. _tcscpy(lpFindFileData->cFileName, lpFindFileData->cAlternateFileName);
  98. lpFindFileData->cAlternateFileName[0] = NULL;
  99. }
  100. exit:
  101. return bRet;
  102. }
  103. /*++
  104. Abstract:
  105. CheckDepthLimit checks to see if the depth of the requested search
  106. is greater than is allowed. If we are limiting relative paths, then
  107. the current directory is prepended to the requested search string.
  108. It returns TRUE if within limits, FALSE if limits have been exceeded.
  109. History:
  110. 08/24/2000 t-adams Created
  111. --*/
  112. BOOL
  113. CheckDepthLimit(const CString & csFileName)
  114. {
  115. BOOL bRet = TRUE;
  116. CSTRING_TRY
  117. {
  118. // Check the depth of the requested file
  119. if ( g_bLimitDepth )
  120. {
  121. DWORD dwDepth = -1;
  122. int nIndex = 0;
  123. do
  124. {
  125. dwDepth += 1;
  126. nIndex = csFileName.Find(L'\\', nIndex);
  127. }
  128. while( nIndex >= 0 );
  129. if ( dwDepth > g_dwDepthLimit )
  130. {
  131. bRet = FALSE;
  132. }
  133. }
  134. }
  135. CSTRING_CATCH
  136. {
  137. // Do nothing
  138. }
  139. return bRet;
  140. }
  141. /*++
  142. Abstract:
  143. This function checks the depth of the requested search (see comments above).
  144. If the depth check passes it performs the search, request limit application,
  145. and finally returns a successful handle only if within all limits.
  146. History:
  147. 08/24/2000 t-adams Created
  148. --*/
  149. HANDLE
  150. APIHOOK(FindFirstFileA)(
  151. LPCSTR lpFileName,
  152. LPWIN32_FIND_DATAA lpFindFileData)
  153. {
  154. HANDLE hRet = INVALID_HANDLE_VALUE;
  155. FFNode *pFFNode = NULL;
  156. BOOL bRelPath = FALSE;
  157. CString csFileName(lpFileName);
  158. // Determine if the path is relative to the CWD:
  159. CString csDrive;
  160. csFileName.GetDrivePortion(csDrive);
  161. bRelPath = csDrive.IsEmpty();
  162. // If it is a relative path & we're not limiting such, then just do
  163. // the FindFile and get out.
  164. if ( bRelPath)
  165. {
  166. if (!g_bLimitRelative)
  167. {
  168. return ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  169. }
  170. // We need to expand the directory portion of lpFileName to its full path
  171. CString csPath;
  172. CString csFile;
  173. csFileName.GetNotLastPathComponent(csPath);
  174. csFileName.GetLastPathComponent(csFile);
  175. csPath.GetFullPathNameW();
  176. csPath.AppendPath(csFile);
  177. csFileName = csPath;
  178. // Check the depth limit
  179. if ( !CheckDepthLimit(csFileName) )
  180. {
  181. return INVALID_HANDLE_VALUE;
  182. }
  183. }
  184. hRet = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  185. if ( INVALID_HANDLE_VALUE == hRet )
  186. {
  187. return hRet;
  188. }
  189. // Make a new node for this handle
  190. pFFNode = (FFNode *) malloc(sizeof FFNode);
  191. if ( !pFFNode )
  192. {
  193. // Don't close the find, maybe it could still work for the app.
  194. goto exit;
  195. }
  196. pFFNode->hFF = hRet;
  197. pFFNode->dwBranches = 0;
  198. pFFNode->dwFiles = 0;
  199. // Apply our limits until we get a passable find
  200. while( !ApplyLimits(pFFNode, lpFindFileData) )
  201. {
  202. // If there are no more files to find, clean up & exit
  203. // else loop back & ApplyLimits again
  204. if ( !FindNextFileA(hRet, lpFindFileData) )
  205. {
  206. free(pFFNode);
  207. FindClose(hRet);
  208. hRet = INVALID_HANDLE_VALUE;
  209. goto exit;
  210. }
  211. }
  212. // We are clear to add this node to the global list
  213. pFFNode->next = g_FFList;
  214. g_FFList = pFFNode;
  215. exit:
  216. return hRet;
  217. }
  218. /*++
  219. Abstract:
  220. This function continues a limited search given the search's handle.
  221. History:
  222. 08/24/2000 t-adams Created
  223. --*/
  224. BOOL
  225. APIHOOK(FindNextFileA)(
  226. HANDLE hFindFile,
  227. LPWIN32_FIND_DATAA lpFindFileData)
  228. {
  229. FFNode *pFFNode = NULL;
  230. BOOL bRet = ORIGINAL_API(FindNextFileA)(hFindFile, lpFindFileData);
  231. if ( !bRet )
  232. {
  233. goto exit;
  234. }
  235. // Find our node in the global list
  236. pFFNode = g_FFList;
  237. while( pFFNode )
  238. {
  239. if ( pFFNode->hFF == hFindFile )
  240. {
  241. break;
  242. }
  243. pFFNode = pFFNode->next;
  244. }
  245. // We don't keep track of relative-path searches if we're not
  246. // limiting such.
  247. if ( pFFNode == NULL )
  248. {
  249. goto exit;
  250. }
  251. // Apply our limits until we get a passable find
  252. while( !ApplyLimits(pFFNode, lpFindFileData) )
  253. {
  254. // If there are no more files to find return FALSE
  255. // else loop back & ApplyLimits again
  256. if ( !FindNextFileA(hFindFile, lpFindFileData) )
  257. {
  258. bRet = FALSE;
  259. goto exit;
  260. }
  261. }
  262. exit:
  263. return bRet;
  264. }
  265. /*++
  266. Abstract:
  267. This function closes a search, cleaning up the structures used
  268. in keeping track of the limits.
  269. History:
  270. 08/24/2000 t-adams Created
  271. --*/
  272. BOOL
  273. APIHOOK(FindClose)(
  274. HANDLE hFindFile)
  275. {
  276. FFNode *pFFNode, *prev;
  277. BOOL bRet = ORIGINAL_API(FindClose)(hFindFile);
  278. // Find the node that matches the handle
  279. pFFNode = g_FFList;
  280. prev = NULL;
  281. while( pFFNode )
  282. {
  283. if ( pFFNode->hFF == hFindFile )
  284. {
  285. // Remove this node from this list
  286. if ( prev )
  287. {
  288. prev->next = pFFNode->next;
  289. }
  290. else
  291. {
  292. g_FFList = pFFNode->next;
  293. }
  294. free(pFFNode);
  295. pFFNode = NULL;
  296. break;
  297. }
  298. prev = pFFNode;
  299. pFFNode = pFFNode->next;
  300. }
  301. return bRet;
  302. }
  303. /*++
  304. Abstract:
  305. This function parses the command line.
  306. See the top of the file for valid arguments.
  307. History:
  308. 08/24/2000 t-adams Created
  309. --*/
  310. // This module has been given an official blessing to use the str routines.
  311. #include "LegalStr.h"
  312. VOID
  313. ParseCommandLine( LPCSTR lpCommandLine )
  314. {
  315. char seps[] = " ,\t;:=";
  316. BOOL *pbOption = NULL;
  317. DWORD *pdwValue = NULL;
  318. char *token = NULL;
  319. // Since strtok modifies the string, we need to copy it
  320. LPSTR szCommandLine = (LPSTR) malloc(strlen(lpCommandLine)+1);
  321. if (!szCommandLine)
  322. {
  323. goto Exit;
  324. }
  325. strcpy(szCommandLine, lpCommandLine);
  326. // If there is a command line, reset the default behavior
  327. if ( NULL != *szCommandLine )
  328. {
  329. g_bLimitDepth = FALSE;
  330. g_bLimitBranch = FALSE;
  331. g_bLimitFiles = FALSE;
  332. g_bUseShortNames = FALSE;
  333. }
  334. // Run the command line
  335. token = _strtok(szCommandLine, seps);
  336. while (token)
  337. {
  338. // If we're looking for a value
  339. if ( pdwValue )
  340. {
  341. *pdwValue = atol(token);
  342. pdwValue = NULL;
  343. }
  344. else
  345. { // We're expecting a keyword
  346. if ( strcmp("DEPTH", token) == 0 )
  347. {
  348. g_bLimitDepth = TRUE;
  349. pdwValue = &g_dwDepthLimit;
  350. }
  351. else if ( strcmp("BRANCH", token) == 0 )
  352. {
  353. g_bLimitBranch = TRUE;
  354. pdwValue = &g_dwBranchLimit;
  355. }
  356. else if ( strcmp("FILES", token) == 0 )
  357. {
  358. g_bLimitFiles = TRUE;
  359. pdwValue = &g_dwFileLimit;
  360. }
  361. else if ( strcmp("SHORTFILENAMES", token) == 0)
  362. {
  363. g_bUseShortNames = TRUE;
  364. // Don't need a value here
  365. }
  366. else if ( strcmp("LONGFILENAMES", token) == 0)
  367. {
  368. g_bUseShortNames = FALSE;
  369. // Don't need a value here
  370. }
  371. else if ( strcmp("LIMITRELATIVE", token) == 0)
  372. {
  373. g_bLimitRelative = TRUE;
  374. // Don't need a value here
  375. }
  376. else if ( strcmp("ALLOWRELATIVE", token) == 0)
  377. {
  378. g_bLimitRelative = FALSE;
  379. // Don't need a value here
  380. }
  381. }
  382. // Get the next token
  383. token = _strtok(NULL, seps);
  384. }
  385. Exit:
  386. if (szCommandLine)
  387. {
  388. free(szCommandLine);
  389. }
  390. //
  391. // Dump results of command line parse
  392. //
  393. DPFN( eDbgLevelInfo, "===================================\n");
  394. DPFN( eDbgLevelInfo, " Limit FindFile \n");
  395. DPFN( eDbgLevelInfo, "===================================\n");
  396. if ( g_bLimitDepth )
  397. {
  398. DPFN( eDbgLevelInfo, " Depth = %d\n", g_dwDepthLimit);
  399. }
  400. if ( g_bLimitBranch )
  401. {
  402. DPFN( eDbgLevelInfo, " Branch = %d\n", g_dwBranchLimit);
  403. }
  404. if ( g_bLimitFiles )
  405. {
  406. DPFN( eDbgLevelInfo, " Files = %d\n", g_dwFileLimit);
  407. }
  408. if ( g_bLimitRelative )
  409. {
  410. DPFN( eDbgLevelInfo, " Limiting Relative Paths.\n");
  411. }
  412. else
  413. {
  414. DPFN( eDbgLevelInfo, " Not Limiting Relative Paths.\n");
  415. }
  416. if ( g_bUseShortNames )
  417. {
  418. DPFN( eDbgLevelInfo, " Using short file names.\n");
  419. }
  420. DPFN( eDbgLevelInfo, "-----------------------------------\n");
  421. }
  422. BOOL
  423. NOTIFY_FUNCTION(
  424. DWORD fdwReason)
  425. {
  426. if (fdwReason == DLL_PROCESS_ATTACH)
  427. {
  428. ParseCommandLine(COMMAND_LINE);
  429. }
  430. return TRUE;
  431. }
  432. /*++
  433. Register hooked functions
  434. --*/
  435. HOOK_BEGIN
  436. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileA)
  437. APIHOOK_ENTRY(KERNEL32.DLL, FindNextFileA)
  438. APIHOOK_ENTRY(KERNEL32.DLL, FindClose)
  439. CALL_NOTIFY_FUNCTION
  440. HOOK_END
  441. IMPLEMENT_SHIM_END