diff --git a/src/.msvc/rufus.vcxproj b/src/.msvc/rufus.vcxproj index 5fe26cff..bef861bd 100644 --- a/src/.msvc/rufus.vcxproj +++ b/src/.msvc/rufus.vcxproj @@ -189,6 +189,7 @@ + @@ -210,6 +211,7 @@ + diff --git a/src/.msvc/rufus.vcxproj.filters b/src/.msvc/rufus.vcxproj.filters index e5321e31..31c35c28 100644 --- a/src/.msvc/rufus.vcxproj.filters +++ b/src/.msvc/rufus.vcxproj.filters @@ -63,6 +63,9 @@ Source Files + + Source Files + @@ -110,6 +113,9 @@ Header Files + + Header Files + diff --git a/src/.msvc/rufus_sources b/src/.msvc/rufus_sources index 94f66859..38385424 100644 --- a/src/.msvc/rufus_sources +++ b/src/.msvc/rufus_sources @@ -43,6 +43,7 @@ SOURCES=rufus.c \ dos_locale.c \ badblocks.c \ drive.c \ + smart.c \ syslinux.c \ vhd.c \ rufus.rc diff --git a/src/Makefile.am b/src/Makefile.am index cc62540b..97426851 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,7 +9,7 @@ pkg_v_rc_0 = @echo " RC $@"; %_rc.o: %.rc ../res/localization/embedded.loc $(pkg_v_rc)$(WINDRES) $(AM_RCFLAGS) -i $< -o $@ -rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c stdio.c stdfn.c stdlg.c rufus.c +rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS) rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ diff --git a/src/Makefile.in b/src/Makefile.in index 11f235ae..cabe4936 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -48,9 +48,9 @@ am_rufus_OBJECTS = rufus-drive.$(OBJEXT) rufus-icon.$(OBJEXT) \ rufus-iso.$(OBJEXT) rufus-net.$(OBJEXT) rufus-dos.$(OBJEXT) \ rufus-dos_locale.$(OBJEXT) rufus-badblocks.$(OBJEXT) \ rufus-syslinux.$(OBJEXT) rufus-vhd.$(OBJEXT) \ - rufus-format.$(OBJEXT) rufus-stdio.$(OBJEXT) \ - rufus-stdfn.$(OBJEXT) rufus-stdlg.$(OBJEXT) \ - rufus-rufus.$(OBJEXT) + rufus-format.$(OBJEXT) rufus-smart.$(OBJEXT) \ + rufus-stdio.$(OBJEXT) rufus-stdfn.$(OBJEXT) \ + rufus-stdlg.$(OBJEXT) rufus-rufus.$(OBJEXT) rufus_OBJECTS = $(am_rufus_OBJECTS) rufus_DEPENDENCIES = rufus_rc.o ms-sys/libmssys.a \ syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ @@ -186,7 +186,7 @@ SUBDIRS = ms-sys syslinux/libfat syslinux/libinstaller libcdio/iso9660 libcdio/u pkg_v_rc = $(pkg_v_rc_$(V)) pkg_v_rc_ = $(pkg_v_rc_$(AM_DEFAULT_VERBOSITY)) pkg_v_rc_0 = @echo " RC $@"; -rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c stdio.c stdfn.c stdlg.c rufus.c +rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS) rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ @@ -343,6 +343,14 @@ rufus-format.obj: format.c $(AM_V_CC) @AM_BACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format.obj `if test -f 'format.c'; then $(CYGPATH_W) 'format.c'; else $(CYGPATH_W) '$(srcdir)/format.c'; fi` +rufus-smart.o: smart.c + $(AM_V_CC) @AM_BACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-smart.o `test -f 'smart.c' || echo '$(srcdir)/'`smart.c + +rufus-smart.obj: smart.c + $(AM_V_CC) @AM_BACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-smart.obj `if test -f 'smart.c'; then $(CYGPATH_W) 'smart.c'; else $(CYGPATH_W) '$(srcdir)/smart.c'; fi` + rufus-stdio.o: stdio.c $(AM_V_CC) @AM_BACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-stdio.o `test -f 'stdio.c' || echo '$(srcdir)/'`stdio.c diff --git a/src/drive.c b/src/drive.c index dd44525e..8967ed42 100644 --- a/src/drive.c +++ b/src/drive.c @@ -38,6 +38,9 @@ RUFUS_DRIVE_INFO SelectedDrive; extern BOOL enable_fixed_disks; +// TODO: add a DetectSectorSize()? +// http://msdn.microsoft.com/en-us/library/ff800831.aspx + /* * Working with drive indexes quite risky (left unchecked,inadvertently passing 0 as * index would return a handle to C:, which we might then proceed to unknowingly diff --git a/src/rufus.c b/src/rufus.c index 73cac94e..7b7da40c 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -730,6 +730,8 @@ static BOOL GetUSBDevices(DWORD devnum) continue; } +// Identify(hDrive); + if (GetDriveLabel(device_number.DeviceNumber + DRIVE_INDEX_MIN, &drive_letter, &label)) { // Must ensure that the combo box is UNSORTED for indexes to be the same StrArrayAdd(&DriveID, buffer); diff --git a/src/rufus.h b/src/rufus.h index 8e9f1dca..a9a49600 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -350,6 +350,7 @@ extern char* replace_in_token_data(const char* filename, const char* token, cons extern void parse_update(char* buf, size_t len); extern BOOL WimExtractCheck(void); extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst); +extern BOOL Identify(HANDLE hPhysical); static __inline BOOL UnlockDrive(HANDLE hDrive) { diff --git a/src/rufus.rc b/src/rufus.rc index 44f6021e..70cc4500 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 206, 329 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rufus v1.4.0.314" +CAPTION "Rufus v1.4.0.315" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,291,50,14 @@ -289,8 +289,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,4,0,314 - PRODUCTVERSION 1,4,0,314 + FILEVERSION 1,4,0,315 + PRODUCTVERSION 1,4,0,315 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -307,13 +307,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.4.0.314" + VALUE "FileVersion", "1.4.0.315" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2013 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.4.0.314" + VALUE "ProductVersion", "1.4.0.315" END END BLOCK "VarFileInfo" diff --git a/src/smart.c b/src/smart.c new file mode 100644 index 00000000..7fa7f471 --- /dev/null +++ b/src/smart.c @@ -0,0 +1,400 @@ +/* + * Rufus: The Reliable USB Formatting Utility + * SMART HDD vs Flash detection (using ATA over USB, S.M.A.R.T., etc.) + * Copyright © 2013 Pete Batard + * + * Based in part on scsiata.cpp from Smartmontools: http://smartmontools.sourceforge.net + * Copyright © 2006-12 Douglas Gilbert + * Copyright © 2009-13 Christian Franke + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifdef _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "msapi_utf8.h" +#include "rufus.h" +#include "smart.h" + + +/* Helper functions */ +static uint8_t GetAtaDirection(uint8_t AtaCmd, uint8_t Features) { + // Far from complete -- only the commands we *may* use. + + // Most SMART commands require DATA_IN but there are a couple exceptions + BOOL smart_out = (AtaCmd == ATA_SMART_CMD) && + ((Features == ATA_SMART_STATUS) || (Features == ATA_SMART_WRITE_LOG_SECTOR)); + + switch (AtaCmd) { + case ATA_IDENTIFY_DEVICE: + case ATA_READ_LOG_EXT: + return ATA_PASSTHROUGH_DATA_IN; + case ATA_SMART_CMD: + if (!smart_out) + return ATA_PASSTHROUGH_DATA_IN; + // fall through + case ATA_DATA_SET_MANAGEMENT: + return ATA_PASSTHROUGH_DATA_OUT; + default: + return ATA_PASSTHROUGH_DATA_NONE; + } +} + +const char* SptStrerr(int errcode) +{ + static char scsi_err[64]; + + if ((errcode > 0) && (errcode <= 0xff)) { + safe_sprintf(scsi_err, sizeof(scsi_err), "SCSI status: 0x%02X", (uint8_t)errcode); + return (const char*)scsi_err; + } + + switch(errcode) { + case SPT_SUCCESS: + return "Success"; + case SPT_ERROR_CDB_LENGTH: + return "Invalid CDB length"; + case SPT_ERROR_BUFFER: + return "Buffer must be aligned to a page boundary and less than 64KB in size"; + case SPT_ERROR_DIRECTION: + return "Invalid Direction"; + case SPT_ERROR_EXTENDED_CDB: + return "Extended and variable length CDB commands are not supported"; + case SPT_ERROR_CDB_OPCODE: + return "Opcodes above 0xC0 are not supported"; + case SPT_ERROR_TIMEOUT: + return "Timeout"; + case SPT_ERROR_INVALID_PARAMETER: + return "Invalid DeviceIoControl parameter"; + case SPT_ERROR_CHECK_STATUS: + return "SCSI error (check Status)"; + default: + return "Unknown error"; + } +} + +/* + * SCSI Passthrough (using IOCTL_SCSI_PASS_THROUGH_DIRECT) + * Should be provided a handle to the physical device (R/W) as well as a Cdb and a buffer that is page aligned + * Direction should be one of SCSI_IOCTL_DATA_### + * + * Returns 0 (SPT_SUCCESS) on success, a positive SCSI Status in case of an SCSI error or negative otherwise. + */ + +BOOL ScsiPassthroughDirect(HANDLE hPhysical, uint8_t* Cdb, size_t CdbLen, uint8_t Direction, + void* DataBuffer, size_t BufLen, uint32_t Timeout) +{ + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdwb = {{0}, 0, {0}}; + DWORD err, size = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER); + BOOL r; + + // Sanity checks + if ((CdbLen == 0) || (CdbLen > sizeof(sptdwb.sptd.Cdb))) + return SPT_ERROR_CDB_LENGTH; + + if (((uintptr_t)DataBuffer % 0x10 != 0) || (BufLen > 0xFFFF)) + return SPT_ERROR_BUFFER; + + if (Direction > SCSI_IOCTL_DATA_UNSPECIFIED) + return SPT_ERROR_DIRECTION; + + // http://en.wikipedia.org/wiki/SCSI_command + if ((Cdb[0] == 0x7e) || (Cdb[0] == 0x7f)) + return SPT_ERROR_EXTENDED_CDB; + + // Opcodes above 0xC0 are unsupported (apart for the special JMicron/Sunplus modes) + if ( (Cdb[0] >= 0xc0) && (Cdb[0] != USB_JMICRON_ATA_PASSTHROUGH) + && (Cdb[0] != USB_SUNPLUS_ATA_PASSTHROUGH) ) + return SPT_ERROR_CDB_OPCODE; + + sptdwb.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + sptdwb.sptd.PathId = 0; + sptdwb.sptd.TargetId = 0; + sptdwb.sptd.Lun = 0; + sptdwb.sptd.CdbLength = (uint8_t)CdbLen; + sptdwb.sptd.DataIn = Direction; // One of SCSI_IOCTL_DATA_### + sptdwb.sptd.SenseInfoLength = SPT_SENSE_LENGTH; + sptdwb.sptd.DataTransferLength = (uint16_t)BufLen; + sptdwb.sptd.TimeOutValue = Timeout; + sptdwb.sptd.DataBuffer = DataBuffer; + sptdwb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseBuf); + + memcpy(sptdwb.sptd.Cdb, Cdb, CdbLen); + + r = DeviceIoControl(hPhysical, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptdwb, size, &sptdwb, size, &size, FALSE); + if ((r) && (sptdwb.sptd.ScsiStatus == 0)) { + return SPT_SUCCESS; + } + + if (sptdwb.sptd.ScsiStatus != 0) { + // uprintf("ScsiPassthroughDirect: CDB command 0x%02X failed (SCSI status 0x%02X)\n", Cdb[0], sptdwb.sptd.ScsiStatus); + return (int)sptdwb.sptd.ScsiStatus; + } else { + err = GetLastError(); + // uprintf("ScsiPassthroughDirect: CDB command 0x%02X failed %s\n", Cdb[0], WindowsErrorString()); SetLastError(err); + switch(err) { + case ERROR_SEM_TIMEOUT: + return SPT_ERROR_TIMEOUT; + case ERROR_INVALID_PARAMETER: + return SPT_ERROR_INVALID_PARAMETER; + default: + return SPT_ERROR_UNKNOWN_ERROR; + } + } + return FALSE; +} + + +/* See ftp://ftp.t10.org/t10/document.04/04-262r8.pdf, http://www.scsitoolbox.com/pdfs/UsingSAT.pdf, + * as well as http://nevar.pl/pliki/ATA8-ACS-3.pdf‎ */ +static int SatAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout) +{ + uint8_t Cdb[12] = {0}; + int extend = 0; /* For 48-bit ATA command (unused here) */ + int ck_cond = 0; /* Set to 1 to read register(s) back */ + int protocol = 3; /* Non-data */ + int t_dir = 1; /* 0 -> to device, 1 -> from device */ + int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ + int t_length = 0; /* 0 -> no data transferred */ + uint8_t Direction; + + if (BufLen % 512 != 0) { + uprintf("SatAtaPassthrough: BufLen must be a multiple of \n"); + return SPT_ERROR_BUFFER; + } + + // Set data direction + Direction = GetAtaDirection(Command->AtaCmd, Command->Features); + if (BufLen != 0) { + switch (Direction) { + case ATA_PASSTHROUGH_DATA_NONE: + break; + case ATA_PASSTHROUGH_DATA_IN: + protocol = 4; // PIO data-in + t_length = 2; // The transfer length is specified in the sector_count field + break; + case ATA_PASSTHROUGH_DATA_OUT: + protocol = 5; // PIO data-out + t_length = 2; // The transfer length is specified in the sector_count field + t_dir = 0; // to device + break; + } + } + + Cdb[0] = SAT_ATA_PASSTHROUGH_12; + Cdb[1] = (protocol << 1) | extend; + Cdb[2] = (ck_cond << 5) | (t_dir << 3) | (byte_block << 2) | t_length; + Cdb[3] = Command->Features; + Cdb[4] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT); + Cdb[5] = Command->Lba_low; + Cdb[6] = Command->Lba_mid; + Cdb[7] = Command->Lba_high; + Cdb[8] = Command->Device; // (m_port == 0 ? 0xa0 : 0xb0); // Must be 0 for identify + Cdb[9] = Command->AtaCmd; + + return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb), Direction, DataBuffer, BufLen, Timeout); +} + +/* The only differences between JMicron and Prolific are the extra 2 bytes for the CDB */ +static int _UsbJMPLAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, + void* DataBuffer, size_t BufLen, uint32_t Timeout, BOOL prolific) +{ + uint8_t Cdb[14] = {0}; + uint8_t Direction; + + Direction = GetAtaDirection(Command->AtaCmd, Command->Features); + + Cdb[0] = USB_JMICRON_ATA_PASSTHROUGH; + Cdb[1] = ((BufLen != 0) && (Direction == ATA_PASSTHROUGH_DATA_OUT))?0x00:0x10; + Cdb[3] = (uint8_t)(BufLen >> 8); + Cdb[4] = (uint8_t)(BufLen); + Cdb[5] = Command->Features; + Cdb[6] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT); + Cdb[7] = Command->Lba_low; + Cdb[8] = Command->Lba_mid; + Cdb[9] = Command->Lba_high; + Cdb[10] = Command->Device; // (m_port == 0 ? 0xa0 : 0xb0); // Must be 0 for identify + Cdb[11] = Command->AtaCmd; + // Prolific PL3507 + Cdb[12] = 0x06; + Cdb[13] = 0x7b; + + return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb)-(prolific?2:0), Direction, DataBuffer, BufLen, Timeout); +} + +static int UsbJmicronAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout) +{ + return _UsbJMPLAtaPassthrough(hPhysical, Command, DataBuffer, BufLen, Timeout, FALSE); +} + +/* UNTESTED!!! */ +static int UsbProlificAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout) +{ + return _UsbJMPLAtaPassthrough(hPhysical, Command, DataBuffer, BufLen, Timeout, TRUE); +} + +/* UNTESTED!!! */ +static int UsbSunPlusAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout) +{ + uint8_t Cdb[12] = {0}; + uint8_t Direction; + + Direction = GetAtaDirection(Command->AtaCmd, Command->Features); + + Cdb[0] = USB_SUNPLUS_ATA_PASSTHROUGH; + Cdb[2] = 0x22; + if (BufLen != 0) { + if (Direction == ATA_PASSTHROUGH_DATA_IN) + Cdb[3] = 0x10; + else if (Direction == ATA_PASSTHROUGH_DATA_OUT) + Cdb[3] = 0x11; + } + Cdb[4] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT); + Cdb[5] = Command->Features; + Cdb[6] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT); + Cdb[7] = Command->Lba_low; + Cdb[8] = Command->Lba_mid; + Cdb[9] = Command->Lba_high; + Cdb[10] = Command->Device | 0xa0; + Cdb[11] = Command->AtaCmd; + + return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb), Direction, DataBuffer, BufLen, Timeout); +} + +/* UNTESTED!!! */ +/* See: http://kernel.opensuse.org/cgit/kernel/tree/drivers/usb/storage/cypress_atacb.c */ +static int UsbCypressAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout) +{ + uint8_t Cdb[16] = {0}; + uint8_t Direction; + + Direction = GetAtaDirection(Command->AtaCmd, Command->Features); + + Cdb[0] = USB_CYPRESS_ATA_PASSTHROUGH; + Cdb[1] = USB_CYPRESS_ATA_PASSTHROUGH; + if (Command->AtaCmd == ATA_IDENTIFY_DEVICE || Command->AtaCmd == ATA_IDENTIFY_PACKET_DEVICE) + Cdb[2] = (1<<7); // Set IdentifyPacketDevice + Cdb[3] = 0xff - (1<<0) - (1<<6); // Features, sector count, lba low, lba med, lba high + Cdb[4] = 1; // Units in blocks rather than bytes + + Cdb[6] = Command->Features; + Cdb[7] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT); + Cdb[8] = Command->Lba_low; + Cdb[9] = Command->Lba_mid; + Cdb[10] = Command->Lba_high; + Cdb[11] = Command->Device; + Cdb[12] = Command->AtaCmd; + + return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb), Direction, DataBuffer, BufLen, Timeout); +} + +/* The various bridges we will try, in order */ +AtaPassThroughType pt[] = { + { SatAtaPassthrough, "SAT" }, + { UsbJmicronAtaPassthrough, "JMicron" }, + { UsbProlificAtaPassthrough, "Prolific" }, + { UsbSunPlusAtaPassthrough, "SunPlus" }, + { UsbCypressAtaPassthrough, "Cypress" }, +}; + +BOOL Identify(HANDLE hPhysical) +{ + ATA_PASSTHROUGH_CMD Command = {0}; + IDENTIFY_DEVICE_DATA* idd; + int i, r; + + Command.AtaCmd = ATA_IDENTIFY_DEVICE; + + // You'll get an error here if your compiler does not properly pack the IDENTIFY struct + COMPILE_TIME_ASSERT(sizeof(IDENTIFY_DEVICE_DATA) == 512); + + idd = (IDENTIFY_DEVICE_DATA*)_aligned_malloc(sizeof(IDENTIFY_DEVICE_DATA), 0x10); + if (idd == NULL) + return FALSE; + + for (i=0; iCommandSetSupport.SmartCommands) { + DumpBufferHex(idd, sizeof(IDENTIFY_DEVICE_DATA)); + uprintf("SMART support detected!\n"); + } else { + uprintf("No SMART support\n"); + } + break; + } + uprintf("No joy with: %s (%s)\n", pt[i].type, SptStrerr(r)); + } + if (i >= ARRAYSIZE(pt)) + uprintf("NO ATA FOR YOU!\n"); + + _aligned_free(idd); + return TRUE; +} + +/* Generic SMART access. Kept for reference, as it doesn't work for USB to ATA/SATA bridges */ +#if 0 +#pragma pack(1) +typedef struct { + UCHAR bVersion; + UCHAR bRevision; + UCHAR bReserved; + UCHAR bIDEDeviceMap; + ULONG fCapabilities; + ULONG dwReserved[4]; +} MY_GETVERSIONINPARAMS; +#pragma pack() + +#ifndef SMART_GET_VERSION +#define SMART_GET_VERSION \ + CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS) +#endif + +BOOL SmartGetVersion(HANDLE hdevice) +{ + MY_GETVERSIONINPARAMS vers; + DWORD size = sizeof(MY_GETVERSIONINPARAMS); + BOOL r; + + memset(&vers, 0, sizeof(vers)); + + r = DeviceIoControl(hdevice, SMART_GET_VERSION, NULL, 0, &vers, sizeof(vers), &size, NULL); + if ( (!r) || (size != sizeof(MY_GETVERSIONINPARAMS)) ) { + uprintf("SmartGetVersion failed: %s\n", r?"unexpected size":WindowsErrorString()); + return FALSE; + } + uprintf("Smart Version: %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n", + vers.bVersion, vers.bRevision, (unsigned)vers.fCapabilities, vers.bIDEDeviceMap); + + return vers.bIDEDeviceMap; +} +#endif + +/* + * TODO: SMART HDD vs UFD detection: + * - if the USB ID starts with + * "WDC", "IBM", "ST" + number, "STM", "HTS", "HITACHI", "SEAGATE", "MAXTOR", "SAMSUNG", "HP ", "FUJITSU", "TOSHIBA", "QUANTUM" + * - if IDENTIFY reports SMART capabilities + * - if it has extra non hidden partitions that aren't Windows + * - if the VID:PID (or VID) is of known USB to IDE/SATA bridge or known UFD maker + */ diff --git a/src/smart.h b/src/smart.h new file mode 100644 index 00000000..7b4dec63 --- /dev/null +++ b/src/smart.h @@ -0,0 +1,407 @@ +/* + * Rufus: The Reliable USB Formatting Utility + * SMART HDD vs Flash detection (using ATA over USB, S.M.A.R.T., etc.) + * Copyright © 2013 Pete Batard + * + * Based in part on Smartmontools: http://smartmontools.sourceforge.net + * Copyright © 2006-12 Douglas Gilbert + * Copyright © 2009-13 Christian Franke + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if defined(__MINGW32__) +#define _aligned_malloc __mingw_aligned_malloc +#define _aligned_free __mingw_aligned_free +#endif + +// From http://stackoverflow.com/a/9284679 +#define COMPILE_TIME_ASSERT(pred) switch(0) {case 0: case pred:;} + +// Official commands +#define ATA_DATA_SET_MANAGEMENT 0x06 // TRIM command for SSDs +#define ATA_READ_LOG_EXT 0x2f +#define ATA_CHECK_POWER_MODE 0xe5 +#define ATA_IDENTIFY_DEVICE 0xec +#define ATA_IDENTIFY_PACKET_DEVICE 0xa1 +#define ATA_IDLE 0xe3 +#define ATA_SMART_CMD 0xb0 +#define ATA_SECURITY_FREEZE_LOCK 0xf5 +#define ATA_SET_FEATURES 0xef +#define ATA_STANDBY_IMMEDIATE 0xe0 +#define SAT_ATA_PASSTHROUGH_12 0xa1 +// Non official pseudo commands +#define USB_CYPRESS_ATA_PASSTHROUGH 0x24 +#define USB_JMICRON_ATA_PASSTHROUGH 0xdf +#define USB_SUNPLUS_ATA_PASSTHROUGH 0xf8 + +// SMART ATA Subcommands +// Also see https://github.com/gregkh/ndas/blob/master/udev.c +#define ATA_SMART_READ_VALUES 0xd0 +#define ATA_SMART_READ_THRESHOLDS 0xd1 +#define ATA_SMART_AUTOSAVE 0xd2 +#define ATA_SMART_SAVE 0xd3 +#define ATA_SMART_IMMEDIATE_OFFLINE 0xd4 +#define ATA_SMART_READ_LOG_SECTOR 0xd5 +#define ATA_SMART_WRITE_LOG_SECTOR 0xd6 +#define ATA_SMART_WRITE_THRESHOLDS 0xd7 +#define ATA_SMART_ENABLE 0xd8 +#define ATA_SMART_DISABLE 0xd9 +#define ATA_SMART_STATUS 0xda + +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 + +#define ATA_PASSTHROUGH_DATA_OUT SCSI_IOCTL_DATA_OUT +#define ATA_PASSTHROUGH_DATA_IN SCSI_IOCTL_DATA_IN +#define ATA_PASSTHROUGH_DATA_NONE SCSI_IOCTL_DATA_UNSPECIFIED + +// Status codes returned by ScsiPassthroughDirect() +#define SPT_SUCCESS 0 +#define SPT_ERROR_CDB_LENGTH -1 +#define SPT_ERROR_BUFFER -2 +#define SPT_ERROR_DIRECTION -3 +#define SPT_ERROR_EXTENDED_CDB -4 +#define SPT_ERROR_CDB_OPCODE -5 +#define SPT_ERROR_TIMEOUT -6 +#define SPT_ERROR_INVALID_PARAMETER -7 +#define SPT_ERROR_CHECK_STATUS -8 +#define SPT_ERROR_UNKNOWN_ERROR -99 + +#define SPT_CDB_LENGTH 16 +#define SPT_SENSE_LENGTH 32 +#define SPT_TIMEOUT_VALUE 2 // In seconds +#define SECTOR_SIZE_SHIFT_BIT 9 // We use 512 bytes sectors always + +#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER +#define IOCTL_SCSI_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_SCSI_PASS_THROUGH_DIRECT \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG_PTR DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[SPT_CDB_LENGTH]; +} SCSI_PASS_THROUGH; + +typedef struct { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + PVOID DataBuffer; + ULONG SenseInfoOffset; + UCHAR Cdb[SPT_CDB_LENGTH]; +} SCSI_PASS_THROUGH_DIRECT; + +typedef struct { + SCSI_PASS_THROUGH_DIRECT sptd; + ULONG Align; + UCHAR SenseBuf[SPT_SENSE_LENGTH]; +} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; + +// Custom ATA over USB command +typedef struct { + uint8_t AtaCmd; // eg: ATA_SMART_CMD = 0xb0, IDENTIFY = 0xec, etc. + uint8_t Features; // SMART subcommand, eg: SMART_ENABLE_OPS = 0xd8, etc. + uint8_t Device; // 0x00 for Identify, 0xA0, 0xB0 for JMicron/SAT SMART ops + uint8_t Align; + uint8_t Lba_low; // LBA + uint8_t Lba_mid; + uint8_t Lba_high; + uint8_t Lba_unused; +} ATA_PASSTHROUGH_CMD; + +typedef BOOL (*AtaPassthroughFn_t)( + HANDLE hPhysical, + ATA_PASSTHROUGH_CMD* Command, + void* DataBuffer, + size_t BufLen, + uint32_t Timeout +); + +typedef struct { + AtaPassthroughFn_t fn; + const char* type; +} AtaPassThroughType; + +// From http://msdn.microsoft.com/en-us/library/windows/hardware/ff559006.aspx +#pragma pack(1) // Packed as the size must be 512 bytes exactly +typedef struct _IDENTIFY_DEVICE_DATA { + struct { + USHORT Reserved1 :1; + USHORT Retired3 :1; + USHORT ResponseIncomplete :1; + USHORT Retired2 :3; + USHORT FixedDevice :1; + USHORT RemovableMedia :1; + USHORT Retired1 :7; + USHORT DeviceType :1; + } GeneralConfiguration; + USHORT NumCylinders; + USHORT ReservedWord2; + USHORT NumHeads; + USHORT Retired1[2]; + USHORT NumSectorsPerTrack; + USHORT VendorUnique1[3]; + UCHAR SerialNumber[20]; + USHORT Retired2[2]; + USHORT Obsolete1; + UCHAR FirmwareRevision[8]; + UCHAR ModelNumber[40]; + UCHAR MaximumBlockTransfer; + UCHAR VendorUnique2; + USHORT ReservedWord48; + struct { + UCHAR ReservedByte49; + UCHAR DmaSupported :1; + UCHAR LbaSupported :1; + UCHAR IordyDisable :1; + UCHAR IordySupported :1; + UCHAR Reserved1 :1; + UCHAR StandybyTimerSupport :1; + UCHAR Reserved2 :2; + USHORT ReservedWord50; + } Capabilities; + USHORT ObsoleteWords51[2]; + USHORT TranslationFieldsValid :3; + USHORT Reserved3 :13; + USHORT NumberOfCurrentCylinders; + USHORT NumberOfCurrentHeads; + USHORT CurrentSectorsPerTrack; + ULONG CurrentSectorCapacity; + UCHAR CurrentMultiSectorSetting; + UCHAR MultiSectorSettingValid :1; + UCHAR ReservedByte59 :7; + ULONG UserAddressableSectors; + USHORT ObsoleteWord62; + USHORT MultiWordDMASupport :8; + USHORT MultiWordDMAActive :8; + USHORT AdvancedPIOModes :8; + USHORT ReservedByte64 :8; + USHORT MinimumMWXferCycleTime; + USHORT RecommendedMWXferCycleTime; + USHORT MinimumPIOCycleTime; + USHORT MinimumPIOCycleTimeIORDY; + USHORT ReservedWords69[6]; + USHORT QueueDepth :5; + USHORT ReservedWord75 :11; + USHORT ReservedWords76[4]; + USHORT MajorRevision; + USHORT MinorRevision; + struct { + USHORT SmartCommands :1; + USHORT SecurityMode :1; + USHORT RemovableMediaFeature :1; + USHORT PowerManagement :1; + USHORT Reserved1 :1; + USHORT WriteCache :1; + USHORT LookAhead :1; + USHORT ReleaseInterrupt :1; + USHORT ServiceInterrupt :1; + USHORT DeviceReset :1; + USHORT HostProtectedArea :1; + USHORT Obsolete1 :1; + USHORT WriteBuffer :1; + USHORT ReadBuffer :1; + USHORT Nop :1; + USHORT Obsolete2 :1; + USHORT DownloadMicrocode :1; + USHORT DmaQueued :1; + USHORT Cfa :1; + USHORT AdvancedPm :1; + USHORT Msn :1; + USHORT PowerUpInStandby :1; + USHORT ManualPowerUp :1; + USHORT Reserved2 :1; + USHORT SetMax :1; + USHORT Acoustics :1; + USHORT BigLba :1; + USHORT DeviceConfigOverlay :1; + USHORT FlushCache :1; + USHORT FlushCacheExt :1; + USHORT Resrved3 :2; + USHORT SmartErrorLog :1; + USHORT SmartSelfTest :1; + USHORT MediaSerialNumber :1; + USHORT MediaCardPassThrough :1; + USHORT StreamingFeature :1; + USHORT GpLogging :1; + USHORT WriteFua :1; + USHORT WriteQueuedFua :1; + USHORT WWN64Bit :1; + USHORT URGReadStream :1; + USHORT URGWriteStream :1; + USHORT ReservedForTechReport :2; + USHORT IdleWithUnloadFeature :1; + USHORT Reserved4 :2; + } CommandSetSupport; + struct { + USHORT SmartCommands :1; + USHORT SecurityMode :1; + USHORT RemovableMediaFeature :1; + USHORT PowerManagement :1; + USHORT Reserved1 :1; + USHORT WriteCache :1; + USHORT LookAhead :1; + USHORT ReleaseInterrupt :1; + USHORT ServiceInterrupt :1; + USHORT DeviceReset :1; + USHORT HostProtectedArea :1; + USHORT Obsolete1 :1; + USHORT WriteBuffer :1; + USHORT ReadBuffer :1; + USHORT Nop :1; + USHORT Obsolete2 :1; + USHORT DownloadMicrocode :1; + USHORT DmaQueued :1; + USHORT Cfa :1; + USHORT AdvancedPm :1; + USHORT Msn :1; + USHORT PowerUpInStandby :1; + USHORT ManualPowerUp :1; + USHORT Reserved2 :1; + USHORT SetMax :1; + USHORT Acoustics :1; + USHORT BigLba :1; + USHORT DeviceConfigOverlay :1; + USHORT FlushCache :1; + USHORT FlushCacheExt :1; + USHORT Resrved3 :2; + USHORT SmartErrorLog :1; + USHORT SmartSelfTest :1; + USHORT MediaSerialNumber :1; + USHORT MediaCardPassThrough :1; + USHORT StreamingFeature :1; + USHORT GpLogging :1; + USHORT WriteFua :1; + USHORT WriteQueuedFua :1; + USHORT WWN64Bit :1; + USHORT URGReadStream :1; + USHORT URGWriteStream :1; + USHORT ReservedForTechReport :2; + USHORT IdleWithUnloadFeature :1; + USHORT Reserved4 :2; + } CommandSetActive; + USHORT UltraDMASupport :8; + USHORT UltraDMAActive :8; + USHORT ReservedWord89[4]; + USHORT HardwareResetResult; + USHORT CurrentAcousticValue :8; + USHORT RecommendedAcousticValue :8; + USHORT ReservedWord95[5]; + ULONG Max48BitLBA[2]; + USHORT StreamingTransferTime; + USHORT ReservedWord105; + struct { + USHORT LogicalSectorsPerPhysicalSector :4; + USHORT Reserved0 :8; + USHORT LogicalSectorLongerThan256Words :1; + USHORT MultipleLogicalSectorsPerPhysicalSector :1; + USHORT Reserved1 :2; + } PhysicalLogicalSectorSize; + USHORT InterSeekDelay; + USHORT WorldWideName[4]; + USHORT ReservedForWorldWideName128[4]; + USHORT ReservedForTlcTechnicalReport; + USHORT WordsPerLogicalSector[2]; + struct { + USHORT ReservedForDrqTechnicalReport :1; + USHORT WriteReadVerifySupported :1; + USHORT Reserved01 :11; + USHORT Reserved1 :2; + } CommandSetSupportExt; + struct { + USHORT ReservedForDrqTechnicalReport :1; + USHORT WriteReadVerifyEnabled :1; + USHORT Reserved01 :11; + USHORT Reserved1 :2; + } CommandSetActiveExt; + USHORT ReservedForExpandedSupportandActive[6]; + USHORT MsnSupport :2; + USHORT ReservedWord1274 :14; + struct { + USHORT SecuritySupported :1; + USHORT SecurityEnabled :1; + USHORT SecurityLocked :1; + USHORT SecurityFrozen :1; + USHORT SecurityCountExpired :1; + USHORT EnhancedSecurityEraseSupported :1; + USHORT Reserved0 :2; + USHORT SecurityLevel :1; + USHORT Reserved1 :7; + } SecurityStatus; + USHORT ReservedWord129[31]; + struct { + USHORT MaximumCurrentInMA2 :12; + USHORT CfaPowerMode1Disabled :1; + USHORT CfaPowerMode1Required :1; + USHORT Reserved0 :1; + USHORT Word160Supported :1; + } CfaPowerModel; + USHORT ReservedForCfaWord161[8]; + struct { + USHORT SupportsTrim :1; + USHORT Reserved0 :15; + } DataSetManagementFeature; + USHORT ReservedForCfaWord170[6]; + USHORT CurrentMediaSerialNumber[30]; + USHORT ReservedWord206; + USHORT ReservedWord207[2]; + struct { + USHORT AlignmentOfLogicalWithinPhysical :14; + USHORT Word209Supported :1; + USHORT Reserved0 :1; + } BlockAlignment; + USHORT WriteReadVerifySectorCountMode3Only[2]; + USHORT WriteReadVerifySectorCountMode2Only[2]; + struct { + USHORT NVCachePowerModeEnabled :1; + USHORT Reserved0 :3; + USHORT NVCacheFeatureSetEnabled :1; + USHORT Reserved1 :3; + USHORT NVCachePowerModeVersion :4; + USHORT NVCacheFeatureSetVersion :4; + } NVCacheCapabilities; + USHORT NVCacheSizeLSW; + USHORT NVCacheSizeMSW; + USHORT NominalMediaRotationRate; + USHORT ReservedWord218; + struct { + UCHAR NVCacheEstimatedTimeToSpinUpInSeconds; + UCHAR Reserved; + } NVCacheOptions; + USHORT ReservedWord220[35]; + USHORT Signature :8; + USHORT CheckSum :8; +} IDENTIFY_DEVICE_DATA, *PIDENTIFY_DEVICE_DATA; +#pragma pack() diff --git a/src/stdio.c b/src/stdio.c index 2c32f3a4..80b3ed3c 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -129,6 +129,8 @@ static char err_string[256] = {0}; else safe_sprintf(err_string, sizeof(err_string), "Unknown error 0x%08X", error_code); } + + SetLastError(error_code); // Make sure we don't change the errorcode on exit return err_string; }