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.

636 lines
16 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. ForceCDStop.cpp
  5. Abstract:
  6. This shim is used to fix the problem of contention with the CD Drive.
  7. Some applications try and access the CD even if they are in the middle of
  8. playing a movie or sound via MCI. Note that this shim assumes the app
  9. is running off of a single CDRom drive at a time.
  10. Notes:
  11. This is a general purpose shim.
  12. History:
  13. 04/10/2000 linstev Created
  14. 04/12/2000 a-michni Added _hread, ReadFile and _lseek capability.
  15. 04/28/2000 a-michni changed logic to check for IsACDRom before
  16. checking for a bad handle, this way CD letter
  17. is set for those routines which only have a
  18. handle and no way of finding the drive letter.
  19. 05/30/2000 a-chcoff Changed logic to do a cd stop only if error was device busy..
  20. we were checking every failed access and plane crazy was making
  21. lots of calls that would fail as file not found. as such the cd was
  22. getting stopped when it did not need to be, causing a CD not found.
  23. This shim should be changed to a faster model. maybe later..
  24. --*/
  25. #include "precomp.h"
  26. IMPLEMENT_SHIM_BEGIN(ForceCDStop)
  27. #include "ShimHookMacro.h"
  28. APIHOOK_ENUM_BEGIN
  29. APIHOOK_ENUM_ENTRY(FindFirstFileA)
  30. APIHOOK_ENUM_ENTRY(FindFirstFileW)
  31. APIHOOK_ENUM_ENTRY(FindFirstFileExA)
  32. APIHOOK_ENUM_ENTRY(FindFirstFileExW)
  33. APIHOOK_ENUM_ENTRY(CreateFileA)
  34. APIHOOK_ENUM_ENTRY(CreateFileW)
  35. APIHOOK_ENUM_ENTRY(ReadFile)
  36. APIHOOK_ENUM_ENTRY(_hread)
  37. APIHOOK_ENUM_ENTRY(_lseek)
  38. APIHOOK_ENUM_END
  39. // Include these so we can get to the IOCTLs
  40. #include <devioctl.h>
  41. #include <ntddcdrm.h>
  42. //
  43. // We have to store the first opened CD drive, so that if ReadFile fails, we
  44. // know which drive to stop. Note, we don't need to protect this variable with
  45. // a critical section, since it's basically atomic.
  46. //
  47. WCHAR g_wLastCDDrive = L'\0';
  48. /*++
  49. Initialize the global CD letter variable if required.
  50. --*/
  51. VOID
  52. InitializeCDA(
  53. LPSTR lpFileName
  54. )
  55. {
  56. CHAR cDrive;
  57. if (!g_wLastCDDrive) {
  58. if (GetDriveTypeFromFileNameA(lpFileName, &cDrive) == DRIVE_CDROM) {
  59. g_wLastCDDrive = (WCHAR)cDrive;
  60. }
  61. }
  62. }
  63. /*++
  64. Initialize the global CD letter variable if required.
  65. --*/
  66. VOID
  67. InitializeCDW(
  68. LPWSTR lpFileName
  69. )
  70. {
  71. WCHAR wDrive;
  72. if (!g_wLastCDDrive) {
  73. if (GetDriveTypeFromFileNameW(lpFileName, &wDrive) == DRIVE_CDROM) {
  74. g_wLastCDDrive = wDrive;
  75. }
  76. }
  77. }
  78. /*++
  79. Send a STOP IOCTL to the specified drive.
  80. --*/
  81. BOOL
  82. StopDrive(
  83. WCHAR wDrive
  84. )
  85. {
  86. BOOL bRet = FALSE;
  87. HANDLE hDrive;
  88. WCHAR wzCDROM[7] = L"\\\\.\\C:";
  89. wzCDROM[4] = wDrive;
  90. hDrive = CreateFileW(wzCDROM,
  91. GENERIC_READ,
  92. FILE_SHARE_READ,
  93. NULL,
  94. OPEN_EXISTING,
  95. FILE_ATTRIBUTE_NORMAL,
  96. NULL);
  97. if (hDrive != INVALID_HANDLE_VALUE) {
  98. DWORD dwBytesRead;
  99. // Attempt to stop the audio
  100. bRet = DeviceIoControl(hDrive,
  101. IOCTL_CDROM_STOP_AUDIO,
  102. NULL,
  103. 0,
  104. NULL,
  105. 0,
  106. &dwBytesRead,
  107. NULL);
  108. CloseHandle(hDrive);
  109. if (bRet) {
  110. DPFN( eDbgLevelInfo,
  111. "[StopDrive] Successfully stopped drive.\n");
  112. } else {
  113. DPFN( eDbgLevelError,
  114. "[StopDrive] Failed to stop drive. Error %d.\n", GetLastError());
  115. }
  116. } else {
  117. DPFN( eDbgLevelError,
  118. "[StopDrive] Unable to create cd device handle. %S Error %d.\n",
  119. wzCDROM, GetLastError());
  120. }
  121. return bRet;
  122. }
  123. /*++
  124. Attempts to stop the CD if filename is a file on a CDROM drive.
  125. Returns true on a successful stop.
  126. --*/
  127. BOOL
  128. StopCDA(
  129. LPCSTR lpFileName
  130. )
  131. {
  132. CHAR c;
  133. if (GetDriveTypeFromFileNameA(lpFileName, &c) == DRIVE_CDROM) {
  134. return StopDrive((WCHAR)c);
  135. } else {
  136. return FALSE;
  137. }
  138. }
  139. /*++
  140. Attempts to stop the CD if filename is a file on a CDROM drive.
  141. Returns true on a successful stop.
  142. --*/
  143. BOOL
  144. StopCDW(
  145. LPCWSTR lpFileName
  146. )
  147. {
  148. WCHAR w;
  149. if (GetDriveTypeFromFileNameW(lpFileName, &w) == DRIVE_CDROM) {
  150. return StopDrive(w);
  151. } else {
  152. return FALSE;
  153. }
  154. }
  155. /*++
  156. Attempts to stop the CD on the last opened CDROM Drive.
  157. Returns true on a successful stop.
  158. --*/
  159. BOOL
  160. StopCDH( )
  161. {
  162. if (g_wLastCDDrive) {
  163. return StopDrive(g_wLastCDDrive);
  164. } else {
  165. return FALSE;
  166. }
  167. }
  168. /*++
  169. Check for CD file.
  170. --*/
  171. HANDLE
  172. APIHOOK(FindFirstFileA)(
  173. LPCSTR lpFileName,
  174. LPWIN32_FIND_DATAA lpFindFileData
  175. )
  176. {
  177. HANDLE hRet = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  178. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  179. StopCDA(lpFileName);
  180. hRet = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  181. if (hRet == INVALID_HANDLE_VALUE) {
  182. DPFN( eDbgLevelWarning,
  183. "[FindFirstFileA] failure \"%s\" Error %d.\n",
  184. lpFileName, GetLastError());
  185. } else {
  186. LOGN(
  187. eDbgLevelInfo,
  188. "[FindFirstFileA] Success after CD stop: \"%s\".", lpFileName);
  189. }
  190. }
  191. return hRet;
  192. }
  193. /*++
  194. Check for CD file.
  195. --*/
  196. HANDLE
  197. APIHOOK(FindFirstFileW)(
  198. LPCWSTR lpFileName,
  199. LPWIN32_FIND_DATAW lpFindFileData
  200. )
  201. {
  202. HANDLE hRet = ORIGINAL_API(FindFirstFileW)(lpFileName, lpFindFileData);
  203. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  204. StopCDW(lpFileName);
  205. hRet = ORIGINAL_API(FindFirstFileW)(lpFileName, lpFindFileData);
  206. if (hRet == INVALID_HANDLE_VALUE) {
  207. DPFN( eDbgLevelWarning,
  208. "[FindFirstFileW] failure \"%S\" Error %d.\n",
  209. lpFileName, GetLastError());
  210. } else {
  211. LOGN(
  212. eDbgLevelInfo,
  213. "[FindFirstFileW] Success after CD stop: \"%S\".", lpFileName);
  214. }
  215. }
  216. return hRet;
  217. }
  218. /*++
  219. Check for CD file.
  220. --*/
  221. HANDLE
  222. APIHOOK(FindFirstFileExA)(
  223. LPCSTR lpFileName,
  224. FINDEX_INFO_LEVELS fInfoLevelId,
  225. LPVOID lpFindFileData,
  226. FINDEX_SEARCH_OPS fSearchOp,
  227. LPVOID lpSearchFilter,
  228. DWORD dwAdditionalFlags
  229. )
  230. {
  231. HANDLE hRet = ORIGINAL_API(FindFirstFileExA)(
  232. lpFileName,
  233. fInfoLevelId,
  234. lpFindFileData,
  235. fSearchOp,
  236. lpSearchFilter,
  237. dwAdditionalFlags);
  238. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  239. StopCDA(lpFileName);
  240. hRet = ORIGINAL_API(FindFirstFileExA)(
  241. lpFileName,
  242. fInfoLevelId,
  243. lpFindFileData,
  244. fSearchOp,
  245. lpSearchFilter,
  246. dwAdditionalFlags);
  247. if (hRet == INVALID_HANDLE_VALUE) {
  248. DPFN( eDbgLevelWarning,
  249. "[FindFirstFileExA] failure \"%s\" Error %d.\n",
  250. lpFileName, GetLastError());
  251. } else {
  252. LOGN(
  253. eDbgLevelInfo,
  254. "[FindFirstFileExA] Success after CD stop: \"%s\".", lpFileName);
  255. }
  256. }
  257. return hRet;
  258. }
  259. /*++
  260. Check for CD file.
  261. --*/
  262. HANDLE
  263. APIHOOK(FindFirstFileExW)(
  264. LPCWSTR lpFileName,
  265. FINDEX_INFO_LEVELS fInfoLevelId,
  266. LPVOID lpFindFileData,
  267. FINDEX_SEARCH_OPS fSearchOp,
  268. LPVOID lpSearchFilter,
  269. DWORD dwAdditionalFlags
  270. )
  271. {
  272. HANDLE hRet = ORIGINAL_API(FindFirstFileExW)(
  273. lpFileName,
  274. fInfoLevelId,
  275. lpFindFileData,
  276. fSearchOp,
  277. lpSearchFilter,
  278. dwAdditionalFlags);
  279. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  280. StopCDW(lpFileName);
  281. hRet = ORIGINAL_API(FindFirstFileExW)(
  282. lpFileName,
  283. fInfoLevelId,
  284. lpFindFileData,
  285. fSearchOp,
  286. lpSearchFilter,
  287. dwAdditionalFlags);
  288. if (hRet == INVALID_HANDLE_VALUE) {
  289. DPFN( eDbgLevelWarning,
  290. "[FindFirstFileExW] failure \"%S\" Error %d.\n",
  291. lpFileName, GetLastError());
  292. } else {
  293. LOGN(
  294. eDbgLevelInfo,
  295. "[FindFirstFileExW] Success after CD stop: \"%S\".", lpFileName);
  296. }
  297. }
  298. return hRet;
  299. }
  300. /*++
  301. Check for CD file.
  302. --*/
  303. HANDLE
  304. APIHOOK(CreateFileA)(
  305. LPSTR lpFileName,
  306. DWORD dwDesiredAccess,
  307. DWORD dwShareMode,
  308. LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  309. DWORD dwCreationDisposition,
  310. DWORD dwFlagsAndAttributes,
  311. HANDLE hTemplateFile
  312. )
  313. {
  314. HANDLE hRet = ORIGINAL_API(CreateFileA)(
  315. lpFileName,
  316. dwDesiredAccess,
  317. dwShareMode,
  318. lpSecurityAttributes,
  319. dwCreationDisposition,
  320. dwFlagsAndAttributes,
  321. hTemplateFile);
  322. InitializeCDA(lpFileName);
  323. if ((INVALID_HANDLE_VALUE == hRet) && (ERROR_BUSY == GetLastError())) {
  324. StopCDA(lpFileName);
  325. hRet = ORIGINAL_API(CreateFileA)(
  326. lpFileName,
  327. dwDesiredAccess,
  328. dwShareMode,
  329. lpSecurityAttributes,
  330. dwCreationDisposition,
  331. dwFlagsAndAttributes,
  332. hTemplateFile);
  333. if (hRet == INVALID_HANDLE_VALUE) {
  334. DPFN( eDbgLevelWarning,
  335. "[CreateFileA] failure \"%s\" Error %d.\n",
  336. lpFileName, GetLastError());
  337. } else {
  338. LOGN(
  339. eDbgLevelInfo,
  340. "[CreateFileA] Success after CD stop: \"%s\".", lpFileName);
  341. }
  342. }
  343. return hRet;
  344. }
  345. /*++
  346. Check for CD file.
  347. --*/
  348. HANDLE
  349. APIHOOK(CreateFileW)(
  350. LPWSTR lpFileName,
  351. DWORD dwDesiredAccess,
  352. DWORD dwShareMode,
  353. LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  354. DWORD dwCreationDisposition,
  355. DWORD dwFlagsAndAttributes,
  356. HANDLE hTemplateFile
  357. )
  358. {
  359. HANDLE hRet = ORIGINAL_API(CreateFileW)(
  360. lpFileName,
  361. dwDesiredAccess,
  362. dwShareMode,
  363. lpSecurityAttributes,
  364. dwCreationDisposition,
  365. dwFlagsAndAttributes,
  366. hTemplateFile);
  367. InitializeCDW(lpFileName);
  368. if ((INVALID_HANDLE_VALUE == hRet) && (ERROR_BUSY == GetLastError())) {
  369. StopCDW(lpFileName);
  370. hRet = ORIGINAL_API(CreateFileW)(
  371. lpFileName,
  372. dwDesiredAccess,
  373. dwShareMode,
  374. lpSecurityAttributes,
  375. dwCreationDisposition,
  376. dwFlagsAndAttributes,
  377. hTemplateFile);
  378. if (hRet == INVALID_HANDLE_VALUE) {
  379. DPFN( eDbgLevelWarning,
  380. "[CreateFileW] failure \"%S\" Error %d.\n",
  381. lpFileName, GetLastError());
  382. } else {
  383. LOGN(
  384. eDbgLevelInfo,
  385. "[CreateFileW] Success after CD stop: \"%S\".", lpFileName);
  386. }
  387. }
  388. return hRet;
  389. }
  390. /*++
  391. Check for _lseek error.
  392. --*/
  393. long
  394. APIHOOK(_lseek)(
  395. int handle,
  396. long offset,
  397. int origin
  398. )
  399. {
  400. long iRet = ORIGINAL_API(_lseek)(handle, offset, origin);
  401. if (iRet == -1L && IsOnCDRom((HANDLE)handle)) {
  402. StopCDH();
  403. iRet = ORIGINAL_API(_lseek)(handle, offset, origin);
  404. if (iRet == -1L) {
  405. DPFN( eDbgLevelWarning,
  406. "[_lseek] failure: Error %d.\n", GetLastError());
  407. } else {
  408. LOGN(
  409. eDbgLevelInfo,
  410. "[_lseek] Success after CD stop.");
  411. }
  412. }
  413. return iRet;
  414. }
  415. /*++
  416. Check for _hread error.
  417. --*/
  418. long
  419. APIHOOK(_hread)(
  420. HFILE hFile,
  421. LPVOID lpBuffer,
  422. long lBytes
  423. )
  424. {
  425. long iRet = ORIGINAL_API(_hread)(hFile, lpBuffer, lBytes);
  426. if (iRet == HFILE_ERROR && IsOnCDRom((HANDLE)hFile)) {
  427. StopCDH();
  428. iRet = ORIGINAL_API(_hread)(hFile, lpBuffer, lBytes);
  429. if (iRet == HFILE_ERROR) {
  430. DPFN( eDbgLevelWarning,
  431. "[_hread] failure: Error %d.\n", GetLastError());
  432. } else {
  433. LOGN(
  434. eDbgLevelInfo,
  435. "[_hread] Success after CD stop.");
  436. }
  437. }
  438. return iRet;
  439. }
  440. /*++
  441. Check for ReadFile error.
  442. --*/
  443. BOOL
  444. APIHOOK(ReadFile)(
  445. HANDLE hFile,
  446. LPVOID lpBuffer,
  447. DWORD nNumberOfBytesToRead,
  448. LPDWORD lpNumberOfBytesRead,
  449. LPOVERLAPPED lpOverlapped
  450. )
  451. {
  452. BOOL bRet = ORIGINAL_API(ReadFile)(
  453. hFile,
  454. lpBuffer,
  455. nNumberOfBytesToRead,
  456. lpNumberOfBytesRead,
  457. lpOverlapped);
  458. if ((bRet == FALSE) && (ERROR_BUSY == GetLastError()) && IsOnCDRom(hFile)) {
  459. StopCDH();
  460. bRet = ORIGINAL_API(ReadFile)(
  461. hFile,
  462. lpBuffer,
  463. nNumberOfBytesToRead,
  464. lpNumberOfBytesRead,
  465. lpOverlapped);
  466. if (bRet == FALSE) {
  467. DPFN( eDbgLevelWarning,
  468. "[ReadFile] failure Error %d.\n", GetLastError());
  469. } else {
  470. LOGN(
  471. eDbgLevelInfo,
  472. "[ReadFile] Success after CD stop.");
  473. }
  474. }
  475. return bRet;
  476. }
  477. /*++
  478. Register hooked functions
  479. --*/
  480. HOOK_BEGIN
  481. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileA)
  482. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileW)
  483. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileExA)
  484. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileExW)
  485. APIHOOK_ENTRY(KERNEL32.DLL, CreateFileA)
  486. APIHOOK_ENTRY(KERNEL32.DLL, CreateFileW)
  487. APIHOOK_ENTRY(KERNEL32.DLL, ReadFile)
  488. APIHOOK_ENTRY(KERNEL32.DLL, _hread)
  489. APIHOOK_ENTRY(LIBC.DLL, _lseek)
  490. HOOK_END
  491. IMPLEMENT_SHIM_END