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.

512 lines
14 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. CorrectFilePathsUninstall.cpp
  5. Abstract:
  6. InstallSheild is a bad, bad app. It places files in the SHFolder directories,
  7. but does not remember if it used CSIDL_COMMON_DESKTOPDIRECTORY or CSIDL_DESKTOPDIRECTORY.
  8. On Win9x, this did not matter, since most machines are single user. In NT, the machine
  9. is multi-user, so the links are often installed in one directory, and the uninstall attempts
  10. to remove them from another directory. Also, if CorrectFilePaths.dll was applied to the
  11. install, the uninstallation program has no idea that the files were moved.
  12. This shim attempts to uninstall by looking for the file/directory in the possible locations.
  13. Created:
  14. 03/23/1999 robkenny
  15. Modified:
  16. --*/
  17. #include "precomp.h"
  18. #include "ClassCFP.h"
  19. IMPLEMENT_SHIM_BEGIN(CorrectFilePathsUninstall)
  20. #include "ShimHookMacro.h"
  21. APIHOOK_ENUM_BEGIN
  22. APIHOOK_ENUM_ENTRY(FindFirstFileA)
  23. APIHOOK_ENUM_ENTRY(GetFileAttributesA)
  24. APIHOOK_ENUM_ENTRY(DeleteFileA)
  25. APIHOOK_ENUM_ENTRY(RemoveDirectoryA)
  26. APIHOOK_ENUM_END
  27. //---------------------------------------------------------------------------
  28. /*++
  29. This will force all paths to the User directory
  30. --*/
  31. class CorrectPathChangesForceUser : public CorrectPathChangesUser
  32. {
  33. protected:
  34. virtual void InitializePathFixes();
  35. };
  36. void CorrectPathChangesForceUser::InitializePathFixes()
  37. {
  38. CorrectPathChangesUser::InitializePathFixes();
  39. AddPathChangeW( L"%AllStartMenu%", L"%UserStartMenu%" );
  40. AddPathChangeW( L"%AllDesktop%", L"%UserDesktop%" );
  41. }
  42. /*++
  43. We have three different version of Correct File Paths. Our intent,
  44. is to search for the file/directory that is being deleted:
  45. 1. Original location
  46. 2. Where CorrectFilePaths.dll would have placed the file
  47. 3. <username>/Start Menu or <username>/Desktop
  48. 4. All Users/Start Menu or All Users/Desktop
  49. --*/
  50. CorrectPathChangesAllUser * CorrectFilePaths = NULL;
  51. CorrectPathChangesUser * CorrectToUsername = NULL;
  52. CorrectPathChangesForceUser * CorrectToAll = NULL;
  53. // Call during DLL_PROCESS_ATTACH
  54. // Construct and intialize the path correction class,
  55. // if this routine fails, do not load the shim.
  56. BOOL InitPathcorrectorClass()
  57. {
  58. BOOL bSuccess = FALSE;
  59. CorrectToUsername = new CorrectPathChangesUser;
  60. CorrectFilePaths = new CorrectPathChangesAllUser;
  61. CorrectToAll = new CorrectPathChangesForceUser;
  62. if (CorrectToUsername && CorrectFilePaths && CorrectToAll)
  63. {
  64. bSuccess = CorrectToUsername->ClassInit() &&
  65. CorrectFilePaths->ClassInit() &&
  66. CorrectToAll->ClassInit();
  67. }
  68. // If we failed for any reason
  69. if (!bSuccess)
  70. {
  71. delete CorrectToUsername;
  72. delete CorrectFilePaths;
  73. delete CorrectToAll;
  74. CorrectToUsername = NULL;
  75. CorrectFilePaths = NULL;
  76. CorrectToAll = NULL;
  77. }
  78. return bSuccess;
  79. }
  80. // Call during SHIM_STATIC_DLLS_INITIALIZED
  81. void InitializePathCorrections()
  82. {
  83. CorrectToAll->AddCommandLineA( COMMAND_LINE );
  84. CorrectFilePaths->AddCommandLineA( COMMAND_LINE );
  85. CorrectToUsername->AddCommandLineA( COMMAND_LINE );
  86. }
  87. //---------------------------------------------------------------------------
  88. // Sometimes you get a FILE_NOT_FOUND error, sometimes you get PATH_NOT_FOUND
  89. #define FileOrPathNotFound(err) (err == ERROR_PATH_NOT_FOUND || err == ERROR_FILE_NOT_FOUND)
  90. //---------------------------------------------------------------------------
  91. /*++
  92. This class will contain a list of file locations. No duplicates are allowed
  93. --*/
  94. class FileLocations : public CharVector
  95. {
  96. public:
  97. ~FileLocations();
  98. BOOL InitClass();
  99. void InitPathChanges();
  100. void Append(const char * str);
  101. void Set(const char * lpFileName);
  102. };
  103. /*++
  104. Free up our private copies of the strings
  105. --*/
  106. FileLocations::~FileLocations()
  107. {
  108. for (int i = 0; i < Size(); ++i)
  109. {
  110. char * freeMe = Get(i);
  111. free(freeMe);
  112. }
  113. }
  114. /*++
  115. Override the Append function to ensure strings are unique.
  116. --*/
  117. void FileLocations::Append(const char * str)
  118. {
  119. if (str != NULL)
  120. {
  121. for (int i = 0; i < Size(); ++i)
  122. {
  123. if (_stricmp(Get(i), str) == 0)
  124. return; // It is a duplicate
  125. }
  126. CharVector::Append(StringDuplicateA(str));
  127. }
  128. }
  129. /*++
  130. Add all our alternative path locations to the FileLocations list
  131. --*/
  132. void FileLocations::Set(const char * lpFileName)
  133. {
  134. Erase();
  135. // Add the original filename first, so we will work even if the allocation fails
  136. Append(lpFileName);
  137. char * lpCorrectPath = CorrectFilePaths->CorrectPathAllocA(lpFileName);
  138. char * lpUserPath = CorrectToUsername->CorrectPathAllocA(lpFileName);
  139. char * lpAllPath = CorrectToAll->CorrectPathAllocA(lpFileName);
  140. Append(lpCorrectPath);
  141. Append(lpUserPath);
  142. Append(lpAllPath);
  143. free(lpCorrectPath);
  144. free(lpUserPath);
  145. free(lpAllPath);
  146. }
  147. //---------------------------------------------------------------------------
  148. /*++
  149. Call FindFirstFileA
  150. return FALSE only if it fails because NOT_FOUND
  151. --*/
  152. BOOL bFindFirstFileA(
  153. LPCSTR lpFileName,
  154. LPWIN32_FIND_DATAA lpFindFileData,
  155. HANDLE & returnValue
  156. )
  157. {
  158. returnValue = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  159. // If it failed because the file is not found, try again with <username> corrected name
  160. if (returnValue == INVALID_HANDLE_VALUE)
  161. {
  162. DWORD dwLastError = GetLastError();
  163. if (FileOrPathNotFound(dwLastError))
  164. {
  165. return FALSE;
  166. }
  167. }
  168. return TRUE;
  169. }
  170. /*++
  171. Call FindFirstFileA, if it fails because the file doesn't exist,
  172. correct the file path and try again.
  173. --*/
  174. HANDLE APIHOOK(FindFirstFileA)(
  175. LPCSTR lpFileName, // file name
  176. LPWIN32_FIND_DATAA lpFindFileData // data buffer
  177. )
  178. {
  179. HANDLE returnValue = INVALID_HANDLE_VALUE;
  180. // Create our list of alternative locations
  181. FileLocations fileLocations;
  182. fileLocations.Set(lpFileName);
  183. for (int i = 0; i < fileLocations.Size(); ++i)
  184. {
  185. BOOL fileFound = bFindFirstFileA(fileLocations[i], lpFindFileData, returnValue);
  186. if (fileFound)
  187. {
  188. // Announce the fact that we changed the path
  189. if (i != 0)
  190. {
  191. DPFN( eDbgLevelInfo, "FindFirstFileA corrected path\n %s\n %s", lpFileName, fileLocations[i]);
  192. }
  193. else
  194. {
  195. DPFN( eDbgLevelSpew, "FindFirstFileA(%s)", fileLocations[i]);
  196. }
  197. break;
  198. }
  199. }
  200. // If we totally failed to find the file, call the API with
  201. // the original values to make sure we have the correct return values
  202. if (returnValue == INVALID_HANDLE_VALUE)
  203. {
  204. returnValue = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  205. }
  206. return returnValue;
  207. }
  208. /*++
  209. Call GetFileAttributesA
  210. return FALSE only if it fails because NOT_FOUND
  211. --*/
  212. BOOL bGetFileAttributesA(
  213. LPCSTR lpFileName,
  214. DWORD & returnValue
  215. )
  216. {
  217. returnValue = ORIGINAL_API(GetFileAttributesA)(lpFileName);
  218. // If it failed because the file is not found, try again with <username> corrected name
  219. if (returnValue == -1)
  220. {
  221. DWORD dwLastError = GetLastError();
  222. if (FileOrPathNotFound(dwLastError))
  223. {
  224. return FALSE;
  225. }
  226. }
  227. return TRUE;
  228. }
  229. /*++
  230. Call GetFileAttributesA, if it fails because the file doesn't exist,
  231. correct the file path and try again.
  232. --*/
  233. DWORD APIHOOK(GetFileAttributesA)(
  234. LPCSTR lpFileName // name of file or directory
  235. )
  236. {
  237. DWORD returnValue = 0;
  238. // Create our list of alternative locations
  239. FileLocations fileLocations;
  240. fileLocations.Set(lpFileName);
  241. for (int i = 0; i < fileLocations.Size(); ++i)
  242. {
  243. BOOL fileFound = bGetFileAttributesA(fileLocations[i], returnValue);
  244. if (fileFound)
  245. {
  246. // Announce the fact that we changed the path
  247. if (i != 0)
  248. {
  249. DPFN( eDbgLevelInfo, "GetFileAttributesA corrected path\n %s\n %s", lpFileName, fileLocations[i]);
  250. }
  251. else
  252. {
  253. DPFN( eDbgLevelSpew, "GetFileAttributesA(%s)", fileLocations[i]);
  254. }
  255. break;
  256. }
  257. }
  258. return returnValue;
  259. }
  260. /*++
  261. Call DeleteFileA
  262. return FALSE only if it fails because NOT_FOUND
  263. --*/
  264. BOOL bDeleteFileA(
  265. LPCSTR lpFileName,
  266. BOOL & returnValue
  267. )
  268. {
  269. returnValue = ORIGINAL_API(DeleteFileA)(lpFileName);
  270. // If it failed because the file is not found, try again with <username> corrected name
  271. if (!returnValue)
  272. {
  273. DWORD dwLastError = GetLastError();
  274. if (FileOrPathNotFound(dwLastError))
  275. {
  276. return FALSE;
  277. }
  278. }
  279. return TRUE;
  280. }
  281. /*++
  282. Call DeleteFileA, if it fails because the file doesn't exist,
  283. correct the file path and try again.
  284. --*/
  285. BOOL APIHOOK(DeleteFileA)(
  286. LPCSTR lpFileName // file name
  287. )
  288. {
  289. BOOL returnValue = 0;
  290. // Create our list of alternative locations
  291. FileLocations fileLocations;
  292. fileLocations.Set(lpFileName);
  293. for (int i = 0; i < fileLocations.Size(); ++i)
  294. {
  295. BOOL fileFound = bDeleteFileA(fileLocations[i], returnValue);
  296. if (fileFound)
  297. {
  298. // Announce the fact that we changed the path
  299. if (i != 0)
  300. {
  301. DPFN( eDbgLevelInfo, "DeleteFileA corrected path\n %s\n %s", lpFileName, fileLocations[i]);
  302. }
  303. else
  304. {
  305. DPFN( eDbgLevelSpew, "DeleteFileA(%s)", fileLocations[i]);
  306. }
  307. break;
  308. }
  309. }
  310. return returnValue;
  311. }
  312. /*++
  313. Call RemoveDirectoryA
  314. return FALSE only if it fails because NOT_FOUND
  315. --*/
  316. BOOL bRemoveDirectoryA(LPCSTR lpFileName, BOOL & returnValue)
  317. {
  318. returnValue = ORIGINAL_API(RemoveDirectoryA)(lpFileName);
  319. DWORD dwLastError = GetLastError();
  320. if (!returnValue)
  321. {
  322. if (FileOrPathNotFound(dwLastError))
  323. {
  324. return FALSE;
  325. }
  326. }
  327. // There is a bug(?) in NTFS. A directory that has been sucessfully removed
  328. // will still appear in FindFirstFile/FindNextFile. It seems that the directory
  329. // list update is delayed for some unknown time.
  330. else
  331. {
  332. // Call FindFirstFile on the directory we just deleted, until it dissappears.
  333. // Limit to 500 attempts, worst case delay of 1/2 sec.
  334. for (int nAttempts = 500; nAttempts > 0; nAttempts -= 1)
  335. {
  336. // Call the non-hooked version of FindFirstFileA, we do not want to Correct the filename.
  337. WIN32_FIND_DATAA ffd;
  338. HANDLE fff = ORIGINAL_API(FindFirstFileA)(lpFileName, & ffd);
  339. if (fff == INVALID_HANDLE_VALUE)
  340. {
  341. // FindFile nolonger reports the deleted directory, our job is done
  342. break;
  343. }
  344. else
  345. {
  346. // Spew debug info letting user know that we are waiting for directory to clear FindFirstFile info.
  347. if (nAttempts == 500)
  348. {
  349. DPFN( eDbgLevelInfo, "RemoveDirectoryA waiting for FindFirstFile(%s) to clear", lpFileName);
  350. }
  351. else
  352. {
  353. DPFN( eDbgLevelSpew, " Dir (%s) attr(0x%08x) Attempt(%3d)", ffd.cFileName, ffd.dwFileAttributes, nAttempts);
  354. }
  355. FindClose(fff);
  356. }
  357. Sleep(1);
  358. }
  359. }
  360. return TRUE;
  361. }
  362. /*++
  363. Call RemoveDirectoryA, if it fails because the file doesn't exist,
  364. correct the file path and try again.
  365. --*/
  366. BOOL
  367. APIHOOK(RemoveDirectoryA)(
  368. LPCSTR lpFileName // directory name
  369. )
  370. {
  371. BOOL returnValue = 0;
  372. // Create our list of alternative locations
  373. FileLocations fileLocations;
  374. fileLocations.Set(lpFileName);
  375. for (int i = 0; i < fileLocations.Size(); ++i)
  376. {
  377. BOOL fileFound = bRemoveDirectoryA(fileLocations[i], returnValue);
  378. if (fileFound)
  379. {
  380. // Announce the fact that we changed the path
  381. if (i != 0)
  382. {
  383. DPFN( eDbgLevelInfo, "RemoveDirectoryA corrected path\n %s\n %s", lpFileName, fileLocations[i]);
  384. }
  385. else
  386. {
  387. DPFN( eDbgLevelSpew, "RemoveDirectoryA(%s)", fileLocations[i]);
  388. }
  389. break;
  390. }
  391. }
  392. return returnValue;
  393. }
  394. BOOL
  395. NOTIFY_FUNCTION(
  396. DWORD fdwReason
  397. )
  398. {
  399. BOOL bSuccess = TRUE;
  400. if (fdwReason == DLL_PROCESS_ATTACH)
  401. {
  402. return InitPathcorrectorClass();
  403. }
  404. else if (fdwReason == SHIM_STATIC_DLLS_INITIALIZED)
  405. {
  406. InitializePathCorrections();
  407. }
  408. return bSuccess;
  409. }
  410. /*++
  411. Register hooked functions
  412. --*/
  413. HOOK_BEGIN
  414. CALL_NOTIFY_FUNCTION
  415. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileA)
  416. APIHOOK_ENTRY(KERNEL32.DLL, GetFileAttributesA)
  417. APIHOOK_ENTRY(KERNEL32.DLL, DeleteFileA)
  418. APIHOOK_ENTRY(KERNEL32.DLL, RemoveDirectoryA)
  419. HOOK_END
  420. IMPLEMENT_SHIM_END