Leaked source code of windows server 2003
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.

665 lines
19 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. EmulateFindHandles.cpp
  5. Abstract:
  6. If an application calls FindFirstFile on a directory, then attempts to
  7. remove that directory without first closing the FindFirstFile handle, the
  8. directory will be in use; The RemoveDirectory call will return an
  9. ERROR_SHARING_VIOLATION error. This shim will force the FindFirstFile
  10. handle closed to ensure the directory is removed.
  11. This shim also ensures the FindFirstFile handles are valid before calling
  12. FindNext or FindClose.
  13. The FindFirstFile handle will not be forced closed unless the directory
  14. is empty.
  15. History:
  16. 04/12/2000 robkenny Created
  17. 11/13/2000 robkenny Fixed PREFIX bugs, mostly by removing the W routines.
  18. 11/20/2000 maonis Added FindNextFile and renamed from RemoveDirectoryInUse.
  19. 02/27/2001 robkenny Converted to use CString
  20. 04/26/2001 robkenny FindFileInfo now normalizes the names for comparisons
  21. Moved all AutoLockFFIV outside of the exception handlers
  22. to ensure they are deconstructed correctly.
  23. 02/14/2002 mnikkel Changed GetHandleVector to correct Prefix errors.
  24. --*/
  25. #include "precomp.h"
  26. #include "CharVector.h"
  27. #include "parseDDE.h"
  28. IMPLEMENT_SHIM_BEGIN(EmulateFindHandles)
  29. #include "ShimHookMacro.h"
  30. APIHOOK_ENUM_BEGIN
  31. APIHOOK_ENUM_ENTRY(FindFirstFileA)
  32. APIHOOK_ENUM_ENTRY(FindFirstFileExA)
  33. APIHOOK_ENUM_ENTRY(FindNextFileA)
  34. APIHOOK_ENUM_ENTRY(FindClose)
  35. APIHOOK_ENUM_ENTRY(RemoveDirectoryA)
  36. APIHOOK_ENUM_ENTRY(DdeClientTransaction)
  37. APIHOOK_ENUM_END
  38. BOOL g_bHookDDE = TRUE; // default to hooking DDE
  39. //---------------------------------------------------------------------------
  40. // A class that automatically locks/unlocks the FFIV
  41. class AutoLockFFIV
  42. {
  43. public:
  44. AutoLockFFIV();
  45. ~AutoLockFFIV();
  46. };
  47. //---------------------------------------------------------------------------
  48. /*++
  49. HANDLE Vector type class.
  50. --*/
  51. class FindFileInfo
  52. {
  53. public:
  54. HANDLE m_hFindHandle;
  55. CString m_csFindName;
  56. FindFileInfo(HANDLE findHandle, LPCSTR lpFileName)
  57. {
  58. Init(findHandle, lpFileName);
  59. }
  60. // Convert csFileName into a fully qualified, long path to the *directory*
  61. // c:\Program Files\Some App\*.exe should get changed to c:\Program Files\Some App
  62. // c:\Progra~1\Some~1\*.exe should get changed to c:\Program Files\Some App
  63. // .\*.exe should get changed to c:\Program Files\Some App
  64. // *.exe should get changed to *.exe
  65. static void NormalizeName(CString & csFileName)
  66. {
  67. DWORD dwAttr = GetFileAttributesW(csFileName);
  68. if (dwAttr == INVALID_FILE_ATTRIBUTES)
  69. {
  70. CString csDirPart;
  71. csFileName.GetNotLastPathComponent(csDirPart);
  72. csFileName = csDirPart;
  73. }
  74. csFileName.GetFullPathName();
  75. csFileName.GetLongPathName();
  76. }
  77. // Init the values, we store the full path for safe compares
  78. void Init(HANDLE findHandle, LPCSTR lpFileName)
  79. {
  80. m_hFindHandle = findHandle;
  81. m_csFindName = lpFileName;
  82. NormalizeName(m_csFindName);
  83. }
  84. bool operator == (HANDLE findHandle) const
  85. {
  86. return findHandle == m_hFindHandle;
  87. }
  88. bool operator == (LPCSTR lpFileName) const
  89. {
  90. // We need to convert lpFileName the same way as done in Init()
  91. CString csFileName(lpFileName);
  92. NormalizeName(csFileName);
  93. return m_csFindName.CompareNoCase(csFileName) == 0;
  94. }
  95. };
  96. class FindFileInfoVector : public VectorT<FindFileInfo *>
  97. {
  98. protected:
  99. static FindFileInfoVector * g_TheHandleVector;
  100. CRITICAL_SECTION m_Lock;
  101. public:
  102. FindFileInfoVector()
  103. {
  104. }
  105. void Lock()
  106. {
  107. EnterCriticalSection(&m_Lock);
  108. }
  109. void Unlock()
  110. {
  111. LeaveCriticalSection(&m_Lock);
  112. }
  113. // Search through the list of open FindFirstFile handles for a match to hMember
  114. FindFileInfo * Find(HANDLE hMember)
  115. {
  116. if (hMember != INVALID_HANDLE_VALUE)
  117. {
  118. DPF(g_szModuleName,
  119. eDbgLevelSpew,
  120. "FindFileInfoVector::Find(0x%08x)\n",
  121. hMember);
  122. for (int i = 0; i < Size(); ++i)
  123. {
  124. FindFileInfo * ffi = Get(i);
  125. if (*ffi == hMember)
  126. {
  127. DPF(g_szModuleName,
  128. eDbgLevelSpew,
  129. "FindFileInfoVector: FOUND handle 0x%08x (%S)\n",
  130. ffi->m_hFindHandle, ffi->m_csFindName.Get());
  131. return ffi;
  132. }
  133. }
  134. }
  135. return NULL;
  136. }
  137. // Search through the list of open FindFirstFile handles for a match to lpFileName
  138. FindFileInfo * Find(LPCSTR lpFileName)
  139. {
  140. if (lpFileName != NULL)
  141. {
  142. DPF(g_szModuleName,
  143. eDbgLevelSpew,
  144. "FindFileInfoVector::Find(%s)\n",
  145. lpFileName);
  146. for (int i = 0; i < Size(); ++i)
  147. {
  148. FindFileInfo * ffi = Get(i);
  149. if (*ffi == lpFileName)
  150. {
  151. DPF(g_szModuleName,
  152. eDbgLevelSpew,
  153. "FindFileInfoVector: FOUND handle 0x%08x (%S)\n",
  154. ffi->m_hFindHandle, ffi->m_csFindName.Get());
  155. return ffi;
  156. }
  157. #if 0
  158. else
  159. {
  160. DPF(g_szModuleName,
  161. eDbgLevelSpew,
  162. "FindFileInfoVector: NOT FOUND handle 0x%08x (%S)\n",
  163. ffi.m_hFindHandle, ffi.m_csFindName.Get());
  164. }
  165. #endif
  166. }
  167. }
  168. return NULL;
  169. }
  170. // Remove the FindFileInfo,
  171. // return true if the handle was actually removed.
  172. bool Remove(FindFileInfo * ffi)
  173. {
  174. for (int i = 0; i < Size(); ++i)
  175. {
  176. if (Get(i) == ffi)
  177. {
  178. DPF(g_szModuleName,
  179. eDbgLevelSpew,
  180. "FindFileInfoVector: REMOVED handle 0x%08x (%S)\n",
  181. ffi->m_hFindHandle, ffi->m_csFindName.Get());
  182. // Remove the entry by copying the last entry over this index
  183. // Only move if this is not the last entry.
  184. if (i < Size() - 1)
  185. {
  186. CopyElement(i, Get(Size() - 1));
  187. }
  188. nVectorList -= 1;
  189. }
  190. }
  191. return false;
  192. }
  193. // Initialize the global FindFileInfoVector
  194. static BOOL InitializeHandleVector()
  195. {
  196. g_TheHandleVector = new FindFileInfoVector;
  197. if (g_TheHandleVector)
  198. {
  199. return InitializeCriticalSectionAndSpinCount(&(g_TheHandleVector->m_Lock),0x80000000);
  200. }
  201. return FALSE;
  202. }
  203. // Return a pointer to the global FindFileInfoVector
  204. static FindFileInfoVector * GetHandleVector()
  205. {
  206. return g_TheHandleVector;
  207. }
  208. };
  209. FindFileInfoVector * FindFileInfoVector::g_TheHandleVector = NULL;
  210. FindFileInfoVector * OpenFindFileHandles;
  211. AutoLockFFIV::AutoLockFFIV()
  212. {
  213. FindFileInfoVector::GetHandleVector()->Lock();
  214. }
  215. AutoLockFFIV::~AutoLockFFIV()
  216. {
  217. FindFileInfoVector::GetHandleVector()->Unlock();
  218. }
  219. //---------------------------------------------------------------------------
  220. /*++
  221. Call FindFirstFileA, if it fails because the file doesn't exist,
  222. correct the file path and try again.
  223. --*/
  224. HANDLE
  225. APIHOOK(FindFirstFileA)(
  226. LPCSTR lpFileName, // file name
  227. LPWIN32_FIND_DATAA lpFindFileData // data buffer
  228. )
  229. {
  230. HANDLE returnValue = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  231. if (returnValue != INVALID_HANDLE_VALUE)
  232. {
  233. DPF(g_szModuleName,
  234. eDbgLevelSpew,
  235. "FindFirstFileA: adding handle 0x%08x (%s)\n",
  236. returnValue, lpFileName);
  237. AutoLockFFIV lock;
  238. CSTRING_TRY
  239. {
  240. // Save the handle for later.
  241. FindFileInfo *ffi = new FindFileInfo(returnValue, lpFileName);
  242. FindFileInfoVector::GetHandleVector()->Append(ffi);
  243. }
  244. CSTRING_CATCH
  245. {
  246. // Do nothing
  247. }
  248. }
  249. return returnValue;
  250. }
  251. /*++
  252. Add the file handle to our list.
  253. --*/
  254. HANDLE
  255. APIHOOK(FindFirstFileExA)(
  256. LPCSTR lpFileName,
  257. FINDEX_INFO_LEVELS fInfoLevelId,
  258. LPVOID lpFindFileData,
  259. FINDEX_SEARCH_OPS fSearchOp,
  260. LPVOID lpSearchFilter,
  261. DWORD dwAdditionalFlags
  262. )
  263. {
  264. HANDLE returnValue = ORIGINAL_API(FindFirstFileExA)(
  265. lpFileName,
  266. fInfoLevelId,
  267. lpFindFileData,
  268. fSearchOp,
  269. lpSearchFilter,
  270. dwAdditionalFlags);
  271. if (returnValue != INVALID_HANDLE_VALUE)
  272. {
  273. DPF(g_szModuleName,
  274. eDbgLevelSpew,
  275. "FindFirstFileA: adding handle 0x%08x (%s)\n",
  276. returnValue, lpFileName);
  277. AutoLockFFIV lock;
  278. CSTRING_TRY
  279. {
  280. // Save the handle for later.
  281. FindFileInfo *ffi = new FindFileInfo(returnValue, lpFileName);
  282. FindFileInfoVector::GetHandleVector()->Append(ffi);
  283. }
  284. CSTRING_CATCH
  285. {
  286. // Do nothing
  287. }
  288. }
  289. return returnValue;
  290. }
  291. /*++
  292. Validates the FindFirstFile handle before calling FindNextFileA.
  293. --*/
  294. BOOL
  295. APIHOOK(FindNextFileA)(
  296. HANDLE hFindFile,
  297. LPWIN32_FIND_DATAA lpFindFileData
  298. )
  299. {
  300. BOOL returnValue = FALSE;
  301. AutoLockFFIV lock;
  302. CSTRING_TRY
  303. {
  304. // Only call FindNextFileA if the handle is actually open
  305. FindFileInfo * ffi = FindFileInfoVector::GetHandleVector()->Find(hFindFile);
  306. if (ffi)
  307. {
  308. returnValue = ORIGINAL_API(FindNextFileA)(hFindFile, lpFindFileData);
  309. DPF(g_szModuleName,
  310. eDbgLevelSpew,
  311. "FindNextFile: using handle 0x%08x (%ls)\n",
  312. hFindFile, ffi->m_csFindName.Get());
  313. }
  314. }
  315. CSTRING_CATCH
  316. {
  317. returnValue = ORIGINAL_API(FindNextFileA)(hFindFile, lpFindFileData);
  318. }
  319. return returnValue;
  320. }
  321. /*++
  322. Remove the file handle to our list.
  323. --*/
  324. BOOL
  325. APIHOOK(FindClose)(
  326. HANDLE hFindFile // file search handle
  327. )
  328. {
  329. BOOL returnValue = FALSE;
  330. AutoLockFFIV lock;
  331. CSTRING_TRY
  332. {
  333. // Only call FindClose if the handle is actually open
  334. FindFileInfo * ffi = FindFileInfoVector::GetHandleVector()->Find(hFindFile);
  335. if (ffi)
  336. {
  337. returnValue = ORIGINAL_API(FindClose)(hFindFile);
  338. DPF(g_szModuleName,
  339. eDbgLevelSpew,
  340. "FindClose: removing handle 0x%08x (%S)\n",
  341. hFindFile, ffi->m_csFindName.Get());
  342. // Remove this entry from the list of open FindFirstFile handles.
  343. FindFileInfoVector::GetHandleVector()->Remove(ffi);
  344. }
  345. }
  346. CSTRING_CATCH
  347. {
  348. returnValue = ORIGINAL_API(FindClose)(hFindFile);
  349. }
  350. return returnValue;
  351. }
  352. /*++
  353. Call RemoveDirectoryA, if it fails because the directory is in use,
  354. make sure all FindFirstFile handles are closed, then try again.
  355. --*/
  356. BOOL
  357. APIHOOK(RemoveDirectoryA)(
  358. LPCSTR lpFileName // directory name
  359. )
  360. {
  361. FindFileInfo * ffi;
  362. BOOL returnValue = ORIGINAL_API(RemoveDirectoryA)(lpFileName);
  363. if (!returnValue)
  364. {
  365. AutoLockFFIV lock;
  366. CSTRING_TRY
  367. {
  368. DWORD dwLastError = GetLastError();
  369. // NOTE:
  370. // ERROR_DIR_NOT_EMPTY error takes precedence over ERROR_SHARING_VIOLATION,
  371. // so we will not forcably to free the FindFirstFile handles unless the directory is empty.
  372. // If the directory is in use, check to see if the app left a FindFirstFileHandle open.
  373. if (dwLastError == ERROR_SHARING_VIOLATION)
  374. {
  375. // Close all FindFirstFile handles open to this directory.
  376. ffi = FindFileInfoVector::GetHandleVector()->Find(lpFileName);
  377. while(ffi)
  378. {
  379. DPF(g_szModuleName,
  380. eDbgLevelError,
  381. "[RemoveDirectoryA] Forcing closed FindFirstFile (%S).",
  382. ffi->m_csFindName.Get());
  383. // Calling FindClose here would not, typically, get hooked, so we call
  384. // our hook routine directly to ensure we close the handle and remove it from the list
  385. // If we don't remove it from the list we'll never get out of this loop :-)
  386. APIHOOK(FindClose)(ffi->m_hFindHandle);
  387. ffi = FindFileInfoVector::GetHandleVector()->Find(lpFileName);
  388. }
  389. // Last chance
  390. returnValue = ORIGINAL_API(RemoveDirectoryA)(lpFileName);
  391. }
  392. }
  393. CSTRING_CATCH
  394. {
  395. // Do nothing
  396. }
  397. }
  398. return returnValue;
  399. }
  400. // A list of DDE commands that we are interested in.
  401. const char * c_sDDECommands[] =
  402. {
  403. "DeleteGroup",
  404. NULL,
  405. } ;
  406. // Parse the DDE Command looking for DeleteGroup,
  407. // If the command is found, make sure that we do not have any open FindFirstFile handles
  408. // on that directory.
  409. // This needs to be aware of "User" vs. "All Users" syntax of DDE
  410. void CloseHandleIfDeleteGroup(LPBYTE pData)
  411. {
  412. if (pData)
  413. {
  414. // Now we need to parse the string, looking for a DeleteGroup command
  415. // Format "[DeleteGroup(GroupName, CommonGroupFlag)]"
  416. // CommonGroupFlag is optional
  417. char * pszBuf = StringDuplicateA((const char *)pData);
  418. if (!pszBuf)
  419. return;
  420. UINT * lpwCmd = GetDDECommands(pszBuf, c_sDDECommands, FALSE);
  421. if (lpwCmd)
  422. {
  423. // Store off lpwCmd so we can free the correect addr later
  424. UINT *lpwCmdTemp = lpwCmd;
  425. // Execute a command.
  426. while (*lpwCmd != (UINT)-1)
  427. {
  428. UINT wCmd = *lpwCmd++;
  429. // Subtract 1 to account for the terminating NULL
  430. if (wCmd < ARRAYSIZE(c_sDDECommands)-1)
  431. {
  432. // We found a command--it must be DeleteGroup--since there is only 1
  433. BOOL iCommonGroup = -1;
  434. // From DDE_DeleteGroup
  435. if (*lpwCmd < 1 || *lpwCmd > 3)
  436. {
  437. goto Leave;
  438. }
  439. if (*lpwCmd == 2)
  440. {
  441. //
  442. // Need to check for common group flag
  443. //
  444. if (pszBuf[*(lpwCmd + 2)] == TEXT('1')) {
  445. iCommonGroup = 1;
  446. } else {
  447. iCommonGroup = 0;
  448. }
  449. }
  450. const char * groupName = pszBuf + lpwCmd[1];
  451. // Build a path to the directory
  452. AutoLockFFIV lock;
  453. CSTRING_TRY
  454. {
  455. // Build a path to the directory
  456. CString csGroupName;
  457. GetGroupPath(groupName, csGroupName, 0, iCommonGroup);
  458. // Attempt to delete the directory, since we are calling our hooked
  459. // routine, it will detect if the directory is in use and do the dirty work.
  460. // Close all FindFirstFile handles open to this directory.
  461. const char * szGroupName = csGroupName.GetAnsi();
  462. for (FindFileInfo * ffi = FindFileInfoVector::GetHandleVector()->Find(szGroupName);
  463. ffi != NULL;
  464. ffi = FindFileInfoVector::GetHandleVector()->Find(szGroupName))
  465. {
  466. DPF(g_szModuleName,
  467. eDbgLevelError,
  468. "[DdeClientTransaction] %s Forcing closed FindFirstFile (%S).",
  469. pData, ffi->m_csFindName.Get());
  470. // Calling FindClose here would not, typically, get hooked, so we call
  471. // our hook routine directly to ensure we close the handle and remove it from the list
  472. // If we don't remove it from the list we'll never get out of this loop :-)
  473. APIHOOK(FindClose)(ffi->m_hFindHandle);
  474. }
  475. }
  476. CSTRING_CATCH
  477. {
  478. // Do nothing
  479. }
  480. }
  481. // Next command.
  482. lpwCmd += *lpwCmd + 1;
  483. }
  484. Leave:
  485. // Tidyup...
  486. GlobalFree(lpwCmdTemp);
  487. }
  488. free(pszBuf);
  489. }
  490. }
  491. //==============================================================================
  492. //==============================================================================
  493. HDDEDATA
  494. APIHOOK(DdeClientTransaction)( IN LPBYTE pData, IN DWORD cbData,
  495. IN HCONV hConv, IN HSZ hszItem, IN UINT wFmt, IN UINT wType,
  496. IN DWORD dwTimeout, OUT LPDWORD pdwResult)
  497. {
  498. #if 0
  499. // Allow a longer timeout for debugging purposes.
  500. dwTimeout = 0x0fffffff;
  501. #endif
  502. CloseHandleIfDeleteGroup(pData);
  503. HDDEDATA returnValue = ORIGINAL_API(DdeClientTransaction)(
  504. pData,
  505. cbData,
  506. hConv,
  507. hszItem,
  508. wFmt,
  509. wType,
  510. dwTimeout,
  511. pdwResult);
  512. return returnValue;
  513. }
  514. /*++
  515. Parse the command line, looking for the -noDDE switch
  516. --*/
  517. void ParseCommandLine(const char * commandLine)
  518. {
  519. CString csCL(commandLine);
  520. // if (-noDDE) then g_bHookDDE = FALSE;
  521. g_bHookDDE = csCL.CompareNoCase(L"-noDDE") != 0;
  522. }
  523. BOOL
  524. NOTIFY_FUNCTION(
  525. DWORD fdwReason
  526. )
  527. {
  528. if (fdwReason == DLL_PROCESS_ATTACH) {
  529. // This forces the allocation of the array:
  530. if (FindFileInfoVector::InitializeHandleVector()) {
  531. ParseCommandLine(COMMAND_LINE);
  532. }
  533. else {
  534. return FALSE;
  535. }
  536. }
  537. return TRUE;
  538. }
  539. HOOK_BEGIN
  540. CALL_NOTIFY_FUNCTION
  541. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileA)
  542. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileExA)
  543. APIHOOK_ENTRY(KERNEL32.DLL, FindNextFileA)
  544. APIHOOK_ENTRY(KERNEL32.DLL, FindClose)
  545. APIHOOK_ENTRY(KERNEL32.DLL, RemoveDirectoryA)
  546. if (g_bHookDDE)
  547. {
  548. APIHOOK_ENTRY(USER32.DLL, DdeClientTransaction)
  549. }
  550. HOOK_END
  551. IMPLEMENT_SHIM_END