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.

667 lines
18 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. spt.c
  5. Abstract:
  6. A user mode library that allows simple commands to be sent to a
  7. selected scsi device.
  8. Environment:
  9. User mode only
  10. Revision History:
  11. 4/10/2000 - created
  12. --*/
  13. #include "sptlibp.h"
  14. //
  15. // this routine allows safer DeviceIoControl, specifically handling
  16. // overlapped handles. however, IOCTL_SCSI_PASS_THROUGH and
  17. // IOCTL_SCSI_PASS_THROUGH_DIRECT are blocking calls, so there is
  18. // no support for overlapped IO
  19. //
  20. BOOL
  21. SptpSaferDeviceIoControl(
  22. IN HANDLE VolumeHandle,
  23. IN DWORD IoControlCode,
  24. IN LPVOID InBuffer,
  25. IN DWORD InBufferSize,
  26. IN LPVOID OutBuffer,
  27. IN DWORD OutBufferSize,
  28. OUT LPDWORD BytesReturned
  29. );
  30. BOOL
  31. SptUtilValidateCdbLength(
  32. IN PCDB Cdb,
  33. IN UCHAR CdbSize
  34. )
  35. {
  36. UCHAR commandGroup = (Cdb->AsByte[0] >> 5) & 0x7;
  37. switch (commandGroup) {
  38. case 0:
  39. return (CdbSize == 6);
  40. case 1:
  41. case 2:
  42. return (CdbSize == 10);
  43. case 5:
  44. return (CdbSize == 12);
  45. default:
  46. return TRUE;
  47. }
  48. }
  49. BOOL
  50. SptSendCdbToDevice(
  51. IN HANDLE DeviceHandle,
  52. IN PCDB Cdb,
  53. IN UCHAR CdbSize,
  54. IN PUCHAR Buffer,
  55. IN OUT PDWORD BufferSize,
  56. IN BOOLEAN GetDataFromDevice
  57. )
  58. {
  59. return SptSendCdbToDeviceEx(DeviceHandle,
  60. Cdb,
  61. CdbSize,
  62. Buffer,
  63. BufferSize,
  64. NULL,
  65. 0,
  66. GetDataFromDevice,
  67. SPT_DEFAULT_TIMEOUT
  68. );
  69. }
  70. /*++
  71. Routine Description:
  72. Arguments:
  73. Return Value:
  74. --*/
  75. BOOL
  76. SptSendCdbToDeviceEx(
  77. IN HANDLE DeviceHandle,
  78. IN PCDB Cdb,
  79. IN UCHAR CdbSize,
  80. IN OUT PUCHAR Buffer,
  81. IN OUT PDWORD BufferSize,
  82. OUT PSENSE_DATA SenseData OPTIONAL,
  83. IN UCHAR SenseDataSize,
  84. IN BOOLEAN GetDataFromDevice,
  85. IN DWORD TimeOut // in seconds
  86. )
  87. {
  88. PSPTD_WITH_SENSE p;
  89. DWORD packetSize;
  90. DWORD returnedBytes;
  91. BOOL returnValue;
  92. PSENSE_DATA senseBuffer;
  93. if ((SenseDataSize == 0) && (SenseData != NULL)) {
  94. SetLastError(ERROR_INVALID_PARAMETER);
  95. return FALSE;
  96. }
  97. if ((SenseDataSize != 0) && (SenseData == NULL)) {
  98. SetLastError(ERROR_INVALID_PARAMETER);
  99. return FALSE;
  100. }
  101. if (SenseData && SenseDataSize) {
  102. RtlZeroMemory(SenseData, SenseDataSize);
  103. }
  104. if (Cdb == NULL) {
  105. // cannot send NULL cdb
  106. SetLastError(ERROR_INVALID_PARAMETER);
  107. return FALSE;
  108. }
  109. if (CdbSize < 1 || CdbSize > 16) {
  110. // Cdb size too large or too small for this library currently
  111. SetLastError(ERROR_INVALID_PARAMETER);
  112. return FALSE;
  113. }
  114. if (!SptUtilValidateCdbLength(Cdb, CdbSize)) {
  115. // OpCode Cdb->AsByte[0] is not size CdbSize
  116. SetLastError(ERROR_INVALID_PARAMETER);
  117. return FALSE;
  118. }
  119. if (BufferSize == NULL) {
  120. // BufferSize pointer cannot be NULL
  121. SetLastError(ERROR_INVALID_PARAMETER);
  122. return FALSE;
  123. }
  124. if ((*BufferSize != 0) && (Buffer == NULL)) {
  125. // buffer cannot be NULL if *BufferSize is non-zero
  126. SetLastError(ERROR_INVALID_PARAMETER);
  127. return FALSE;
  128. }
  129. if ((*BufferSize == 0) && (Buffer != NULL)) {
  130. // buffer must be NULL if *BufferSize is zero
  131. SetLastError(ERROR_INVALID_PARAMETER);
  132. return FALSE;
  133. }
  134. if ((*BufferSize) && GetDataFromDevice) {
  135. //
  136. // pre-zero output buffer (not input buffer)
  137. //
  138. memset(Buffer, 0, (*BufferSize));
  139. }
  140. packetSize = sizeof(SPTD_WITH_SENSE);
  141. if (SenseDataSize > sizeof(SENSE_DATA)) {
  142. packetSize += SenseDataSize - sizeof(SENSE_DATA);
  143. }
  144. p = (PSPTD_WITH_SENSE)LocalAlloc(LPTR, packetSize);
  145. if (p == NULL) {
  146. // could not allocate memory for pass-through
  147. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  148. return FALSE;
  149. }
  150. //
  151. // this has the side effect of pre-zeroing the output buffer
  152. // if DataIn is TRUE, the SenseData (always), etc.
  153. //
  154. memset(p, 0, packetSize);
  155. memcpy(p->Sptd.Cdb, Cdb, CdbSize);
  156. p->Sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
  157. p->Sptd.CdbLength = CdbSize;
  158. p->Sptd.SenseInfoLength = SenseDataSize;
  159. if (*BufferSize != 0) {
  160. if (GetDataFromDevice) {
  161. p->Sptd.DataIn = SCSI_IOCTL_DATA_IN; // from device
  162. } else {
  163. p->Sptd.DataIn = SCSI_IOCTL_DATA_OUT; // to device
  164. }
  165. } else {
  166. p->Sptd.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
  167. }
  168. p->Sptd.DataTransferLength = (*BufferSize);
  169. p->Sptd.TimeOutValue = TimeOut;
  170. p->Sptd.DataBuffer = Buffer;
  171. p->Sptd.SenseInfoOffset = FIELD_OFFSET(SPTD_WITH_SENSE, SenseData);
  172. returnedBytes = 0;
  173. returnValue = SptpSaferDeviceIoControl(DeviceHandle,
  174. IOCTL_SCSI_PASS_THROUGH_DIRECT,
  175. p,
  176. packetSize,
  177. p,
  178. packetSize,
  179. &returnedBytes);
  180. *BufferSize = p->Sptd.DataTransferLength;
  181. senseBuffer = &(p->SenseData);
  182. if (senseBuffer->SenseKey & 0xf) {
  183. UCHAR length;
  184. // determine appropriate length to return
  185. length = senseBuffer->AdditionalSenseLength;
  186. length += RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
  187. if (length > SENSE_BUFFER_SIZE) {
  188. length = SENSE_BUFFER_SIZE;
  189. }
  190. length = min(length, SenseDataSize);
  191. // copy the sense data back to the user regardless
  192. RtlCopyMemory(SenseData, senseBuffer, length);
  193. returnValue = FALSE; // some error (possibly recovered) occurred
  194. } else if (p->Sptd.ScsiStatus != 0) { // scsi protocol error
  195. UCHAR length;
  196. // determine appropriate length to return
  197. length = senseBuffer->AdditionalSenseLength;
  198. length += RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
  199. if (length > SENSE_BUFFER_SIZE) {
  200. length = SENSE_BUFFER_SIZE;
  201. }
  202. length = min(length, SenseDataSize);
  203. // copy the sense data back to the user regardless
  204. RtlCopyMemory(SenseData, senseBuffer, length);
  205. returnValue = FALSE; // some error (possibly recovered) occurred
  206. } else if (!returnValue) {
  207. // returnValue = returnValue;
  208. } else {
  209. // success!
  210. }
  211. //
  212. // free our memory and return
  213. //
  214. LocalFree(p);
  215. return returnValue;
  216. }
  217. /*++
  218. Routine Description:
  219. NOTE: we default to RETRY==TRUE except for known error classes
  220. Arguments:
  221. Return Value:
  222. --*/
  223. VOID
  224. SptUtilInterpretSenseInfo(
  225. IN PSENSE_DATA SenseData,
  226. IN UCHAR SenseDataSize,
  227. OUT PDWORD ErrorValue, // from WinError.h
  228. OUT PBOOLEAN SuggestRetry OPTIONAL,
  229. OUT PDWORD SuggestRetryDelay OPTIONAL
  230. )
  231. {
  232. DWORD error;
  233. DWORD retryDelay;
  234. BOOLEAN retry;
  235. UCHAR senseKey;
  236. UCHAR asc;
  237. UCHAR ascq;
  238. if (SenseDataSize == 0) {
  239. retry = FALSE;
  240. retryDelay = 0;
  241. error = ERROR_IO_DEVICE;
  242. goto SetAndExit;
  243. }
  244. //
  245. // default to suggesting a retry in 1/10 of a second,
  246. // with a status of ERROR_IO_DEVICE.
  247. //
  248. retry = TRUE;
  249. retryDelay = 1;
  250. error = ERROR_IO_DEVICE;
  251. //
  252. // if the device didn't provide any sense this time, return.
  253. //
  254. if ((SenseData->SenseKey & 0xf) == 0) {
  255. retry = FALSE;
  256. retryDelay = 0;
  257. error = ERROR_SUCCESS;
  258. goto SetAndExit;
  259. }
  260. //
  261. // if we can't even see the sense key, just return.
  262. // can't use bitfields in these macros, so use next field.
  263. //
  264. if (SenseDataSize < FIELD_OFFSET(SENSE_DATA, Information)) {
  265. goto SetAndExit;
  266. }
  267. senseKey = SenseData->SenseKey;
  268. { // set the size to what's actually useful.
  269. UCHAR validLength;
  270. // figure out what we could have gotten with a large sense buffer
  271. if (SenseDataSize <
  272. RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)) {
  273. validLength = SenseDataSize;
  274. } else {
  275. validLength =
  276. RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
  277. validLength += SenseData->AdditionalSenseLength;
  278. }
  279. // use the smaller of the two values.
  280. SenseDataSize = min(SenseDataSize, validLength);
  281. }
  282. if (SenseDataSize <
  283. RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) {
  284. asc = SCSI_ADSENSE_NO_SENSE;
  285. } else {
  286. asc = SenseData->AdditionalSenseCode;
  287. }
  288. if (SenseDataSize <
  289. RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) {
  290. ascq = SCSI_SENSEQ_CAUSE_NOT_REPORTABLE; // 0x00
  291. } else {
  292. ascq = SenseData->AdditionalSenseCodeQualifier;
  293. }
  294. //
  295. // interpret :P
  296. //
  297. switch (senseKey & 0xf) {
  298. case SCSI_SENSE_RECOVERED_ERROR: { // 0x01
  299. if (SenseData->IncorrectLength) {
  300. error = ERROR_INVALID_BLOCK_LENGTH;
  301. } else {
  302. error = ERROR_SUCCESS;
  303. }
  304. retry = FALSE;
  305. break;
  306. } // end SCSI_SENSE_RECOVERED_ERROR
  307. case SCSI_SENSE_NOT_READY: { // 0x02
  308. error = ERROR_NOT_READY;
  309. switch (asc) {
  310. case SCSI_ADSENSE_LUN_NOT_READY: {
  311. switch (ascq) {
  312. case SCSI_SENSEQ_BECOMING_READY:
  313. case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
  314. retryDelay = SPT_NOT_READY_RETRY_INTERVAL;
  315. break;
  316. }
  317. case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE:
  318. case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
  319. case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
  320. retry = FALSE;
  321. break;
  322. }
  323. case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
  324. retry = FALSE;
  325. break;
  326. }
  327. } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
  328. break;
  329. }
  330. case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
  331. error = ERROR_NOT_READY;
  332. retry = FALSE;
  333. break;
  334. }
  335. } // end switch (senseBuffer->AdditionalSenseCode)
  336. break;
  337. } // end SCSI_SENSE_NOT_READY
  338. case SCSI_SENSE_MEDIUM_ERROR: { // 0x03
  339. error = ERROR_CRC;
  340. retry = FALSE;
  341. //
  342. // Check if this error is due to unknown format
  343. //
  344. if (asc == SCSI_ADSENSE_INVALID_MEDIA) {
  345. switch (ascq) {
  346. case SCSI_SENSEQ_UNKNOWN_FORMAT: {
  347. error = ERROR_UNRECOGNIZED_MEDIA;
  348. break;
  349. }
  350. case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
  351. error = ERROR_UNRECOGNIZED_MEDIA;
  352. //error = ERROR_CLEANER_CARTRIDGE_INSTALLED;
  353. break;
  354. }
  355. } // end switch AdditionalSenseCodeQualifier
  356. } // end SCSI_ADSENSE_INVALID_MEDIA
  357. break;
  358. } // end SCSI_SENSE_MEDIUM_ERROR
  359. case SCSI_SENSE_ILLEGAL_REQUEST: { // 0x05
  360. error = ERROR_INVALID_FUNCTION;
  361. retry = FALSE;
  362. switch (asc) {
  363. case SCSI_ADSENSE_ILLEGAL_BLOCK: {
  364. error = ERROR_SECTOR_NOT_FOUND;
  365. break;
  366. }
  367. case SCSI_ADSENSE_INVALID_LUN: {
  368. error = ERROR_FILE_NOT_FOUND;
  369. break;
  370. }
  371. case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
  372. error = ERROR_FILE_ENCRYPTED;
  373. //error = ERROR_SPT_LIB_COPY_PROTECTION_FAILURE;
  374. switch (ascq) {
  375. case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
  376. //error = ERROR_SPT_LIB_AUTHENTICATION_FAILURE;
  377. break;
  378. case SCSI_SENSEQ_KEY_NOT_PRESENT:
  379. //error = ERROR_SPT_LIB_KEY_NOT_PRESENT;
  380. break;
  381. case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
  382. //error = ERROR_SPT_LIB_KEY_NOT_ESTABLISHED;
  383. break;
  384. case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
  385. //error = ERROR_SPT_LIB_SCRAMBLED_SECTOR;
  386. break;
  387. case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
  388. //error = ERROR_SPT_LIB_REGION_MISMATCH;
  389. break;
  390. case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
  391. //error = ERROR_SPT_LIB_RESETS_EXHAUSTED;
  392. break;
  393. } // end switch of ASCQ for COPY_PROTECTION_FAILURE
  394. break;
  395. }
  396. } // end switch (senseBuffer->AdditionalSenseCode)
  397. break;
  398. } // end SCSI_SENSE_ILLEGAL_REQUEST
  399. case SCSI_SENSE_DATA_PROTECT: { // 0x07
  400. error = ERROR_WRITE_PROTECT;
  401. retry = FALSE;
  402. break;
  403. } // end SCSI_SENSE_DATA_PROTECT
  404. case SCSI_SENSE_BLANK_CHECK: { // 0x08
  405. error = ERROR_NO_DATA_DETECTED;
  406. break;
  407. } // end SCSI_SENSE_BLANK_CHECK
  408. case SCSI_SENSE_NO_SENSE: { // 0x00
  409. if (SenseData->IncorrectLength) {
  410. error = ERROR_INVALID_BLOCK_LENGTH;
  411. retry = FALSE;
  412. } else {
  413. error = ERROR_IO_DEVICE;
  414. }
  415. break;
  416. } // end SCSI_SENSE_NO_SENSE
  417. case SCSI_SENSE_HARDWARE_ERROR: // 0x04
  418. case SCSI_SENSE_UNIT_ATTENTION: // 0x06
  419. case SCSI_SENSE_UNIQUE: // 0x09
  420. case SCSI_SENSE_COPY_ABORTED: // 0x0A
  421. case SCSI_SENSE_ABORTED_COMMAND: // 0x0B
  422. case SCSI_SENSE_EQUAL: // 0x0C
  423. case SCSI_SENSE_VOL_OVERFLOW: // 0x0D
  424. case SCSI_SENSE_MISCOMPARE: // 0x0E
  425. case SCSI_SENSE_RESERVED: // 0x0F
  426. default: {
  427. error = ERROR_IO_DEVICE;
  428. break;
  429. }
  430. } // end switch(SenseKey)
  431. SetAndExit:
  432. if (ARGUMENT_PRESENT(SuggestRetry)) {
  433. *SuggestRetry = retry;
  434. }
  435. if (ARGUMENT_PRESENT(SuggestRetryDelay)) {
  436. *SuggestRetryDelay = retryDelay;
  437. }
  438. *ErrorValue = error;
  439. return;
  440. }
  441. /*++
  442. Routine Description:
  443. Locks the device for exclusive access. Uses the same method format and
  444. chkdsk use to gain exclusive access to the volume.
  445. Arguments:
  446. VolumeHandle - Handle to the volume. Typically created using CreateFile()
  447. to a device of the format \\.\D:
  448. ForceDismount - If TRUE, will try to force dismount the disk without
  449. prompting the user.
  450. Quiet - If TRUE, will not prompt the user. Can be used to fail
  451. if the volume is already opened without providing the
  452. user an opportunity to force the volume to dismount
  453. Return Value:
  454. --*/
  455. BOOL
  456. SptUtilLockVolumeByHandle(
  457. IN HANDLE VolumeHandle,
  458. IN BOOLEAN ForceDismount
  459. )
  460. {
  461. ULONG tmp;
  462. BOOL succeeded;
  463. tmp = 0;
  464. succeeded = SptpSaferDeviceIoControl(VolumeHandle,
  465. FSCTL_LOCK_VOLUME,
  466. NULL, 0,
  467. NULL, 0,
  468. &tmp);
  469. // if we locked the volume successfully, or the user wants to force
  470. // the FS to become invalid, mark it as such so when the handle closes
  471. // the FS reverifies the file system.
  472. // if the lock failed and we're not forcing the issue, the routine
  473. // will fail.
  474. if (succeeded || ForceDismount) {
  475. tmp = 0;
  476. succeeded = SptpSaferDeviceIoControl(VolumeHandle,
  477. FSCTL_DISMOUNT_VOLUME,
  478. NULL, 0,
  479. NULL, 0,
  480. &tmp);
  481. }
  482. return succeeded;
  483. }
  484. /*++
  485. Routine Description:
  486. Arguments:
  487. Return Value:
  488. --*/
  489. BOOL
  490. SptpSaferDeviceIoControl(
  491. IN HANDLE VolumeHandle,
  492. IN DWORD IoControlCode,
  493. IN LPVOID InBuffer,
  494. IN DWORD InBufferSize,
  495. IN LPVOID OutBuffer,
  496. IN DWORD OutBufferSize,
  497. OUT LPDWORD BytesReturned
  498. )
  499. {
  500. BOOL succeeded;
  501. OVERLAPPED overlapped;
  502. RtlZeroMemory(&overlapped, sizeof(OVERLAPPED));
  503. overlapped.hEvent = CreateEvent(NULL, // default SD
  504. TRUE, // must be manually reset
  505. FALSE, // initially unset
  506. NULL); // unnamed event
  507. if (overlapped.hEvent == NULL) {
  508. return FALSE;
  509. }
  510. succeeded = DeviceIoControl(VolumeHandle,
  511. IoControlCode,
  512. InBuffer,
  513. InBufferSize,
  514. OutBuffer,
  515. OutBufferSize,
  516. BytesReturned,
  517. &overlapped);
  518. if (!succeeded && (GetLastError() == ERROR_IO_PENDING)) {
  519. succeeded = GetOverlappedResult( VolumeHandle,
  520. &overlapped,
  521. BytesReturned,
  522. TRUE);
  523. }
  524. CloseHandle( overlapped.hEvent );
  525. overlapped.hEvent = NULL;
  526. return succeeded;
  527. }