1

I want to read a particular sector(MBR Sector) of a disk using ATA commands in vc++. I am new to VC++ so i am facing a problem when sending command to disk using DeviceIoControl. I am providing a code that i am using to read out a sector using command Read Sector(s)(0x20).

  BOOL status = FALSE;
 PATA_PASS_THROUGH_EX pATAData;
DWORD dataSize = sizeof(ATA_PASS_THROUGH_EX) + 512;
BYTE Buffer[sizeof(ATA_PASS_THROUGH_EX) + 512];
DWORD bytescopied = 0;

    pATAData = (ATA_PASS_THROUGH_EX*)Buffer;

    ZeroMemory(pATAData,dataSize); // clears the buffer

    pATAData->Length = sizeof(ATA_PASS_THROUGH_EX);
    pATAData->DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);
    pATAData->DataTransferLength = 512;
    pATAData->TimeOutValue = 2;

    pATAData->CurrentTaskFile[1] = 0x01;
    pATAData->CurrentTaskFile[2] = 0x00;
    pATAData->CurrentTaskFile[3] = 0x00;
    pATAData->CurrentTaskFile[4] = 0x00;
    pATAData->AtaFlags =ATA_FLAGS_DATA_IN;

    pATAData->CurrentTaskFile[6] = 0x20; // command Read Sector(s)(0x20)
    /* sends the command to the device, **hDevice** is device handle*/
    status = DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH, pATAData, dataSize,Buffer, dataSize, &bytescopied, NULL );

I can't undersatnd that what's wrong in this code and what i am missing here, but it is not working. What i am missing here ? If there is a problem with parametres of PATA_PASS_THROUGH_EX structure than tell how to read first sector(MBR).

MSalters
  • 173,980
  • 10
  • 155
  • 350
Sam
  • 61
  • 1
  • 10
  • 1
    Did you call GetLastError to see why it failed? – Retired Ninja May 08 '15 at 05:02
  • Not sure if this applies since you don't show the code for opening the device, but you might take a look anyway. http://stackoverflow.com/questions/3362037/problem-reading-mbr-with-deviceiocontrol-function – Retired Ninja May 08 '15 at 05:14
  • 1
    Why does this have the `qt` tag? You may get better help with other tags such as: `device`, `hard-drive` or `sata`. Also, what is the error you get? – titusjan May 08 '15 at 07:30
  • Yes i call GetLastError. It give an error ERROR_INVALID_PARAMETER (0x57). But i can't understand which parameter is wrong. Actually I am new to windows progarmming. – Sam May 08 '15 at 09:28
  • Tags fixed to match code. – MSalters May 08 '15 at 09:51
  • Thanks @RetiredNinja for your help. I read your link but it give partition info only, actually i want to read a complete sector or multiple sectors. Actually i have to do both read/write with ATA commands. – Sam May 11 '15 at 03:58
  • I don't see any obvious errors in the code. (1) Have you tried making your buffer cache align? (2) Have you tried a more basic command, such as IDENTIFY_DEVICE (ECh)? – tchau.dev May 11 '15 at 22:38

3 Answers3

1

Thankyou everyone for your help. I got the solution. A little bit thing that i have not noticed. That is Ata Flags. I have to send multiple flags. Eg.

pATAData->ataFlags = ATA_FLAGS_48BIT_COMMAND | ATA_FLAGS_DRDY_REQUIRED | ATA_FLAGS_DATA_IN

and also send the ATA command Opcode in PreviousTaskFile[6] also. And the few things to be noticed is What is the block size of the HDD? It could be more than 512 bytes, especially if it is a large disk like(1TB...4TB). As such, you'll need to adjust the sizes accordingly. You can see what the size is by doing the EC identify ATA command, and then look at the resulting data structure.

Sam
  • 61
  • 1
  • 10
0

Thanks for your help guys. I got the solution. I was not assigning the Device Handle in CurrentTaskFile.

pATAData->CurrentTaskFile[5] = (UCHAR)hDevice;

But IDENTIFY_DEVICE(ECh) command was sending succesfully without this. I don't know it's right or wrong but this is working.

Sam
  • 61
  • 1
  • 10
  • Now i can read sectors, but now i have to read large storage disk(upto 4 TB). Can anyone help me how to send details in CurrentTaskFile[] to read a large space disk. Because CurrentTaskFile[] is UCHAR type array. If i want to read last sector from a 1 TB disk . – Sam May 12 '15 at 05:01
  • You need to use the extended commands (EXT) along with the PreviousTaskFile[] – tchau.dev May 19 '15 at 07:56
  • Regarding the CurrentTaskFile[5] aka the "Device" register. The old ATA spec reserved bit 6 of this register for "LBA mode". This means you should set this bit to enable "LBA mode", and clear it to use "CHS mode". To keep things simple, just always use LBA mode by setting this register to 0x40. The reason why the IDENTIFY_DEVICE command works is because it doesn't access any sectors, so it doesn't care about LBA or CHS mode. – tchau.dev May 19 '15 at 08:07
  • THanks @tchau.dev . Your last two comments is very helpfull to me. Yes i have to use the extended commands (EXT) along with the PreviousTaskFile[] for large disk. – Sam May 19 '15 at 09:50
0

Thanks guys for your help. After a long discussion i find that i have to send extended commands for large disk. But now i am sending Read Sector(s) Ext(0x24 oppcode) command.

BOOL status = FALSE;
PATA_PASS_THROUGH_EX pATAData;
DWORD dataSize = sizeof(ATA_PASS_THROUGH_EX) + 512;
BYTE Buffer[sizeof(ATA_PASS_THROUGH_EX) + 512];
DWORD bytescopied = 0;

pATAData = (ATA_PASS_THROUGH_EX*)Buffer;

ZeroMemory(pATAData,dataSize); // clears the buffer

pATAData->Length = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataTransferLength = 512;
pATAData->TimeOutValue = 2;

pATAData->CurrentTaskFile[0] = 0x00;
pATAData->CurrentTaskFile[1] = 0x01;
pATAData->CurrentTaskFile[2] = 0x01;
pATAData->CurrentTaskFile[3] = 0x00;
pATAData->CurrentTaskFile[4] = 0x00;
pATAData->CurrentTaskFile[5] = 0x40;
pATAData->CurrentTaskFile[7]=  0x00;
pATAData->AtaFlags =ATA_FLAGS_48BIT_COMMAND;

pATAData->PreviousTaskFile[0] = 0x00;
pATAData->PreviousTaskFile[1] = 0x00;
pATAData->PreviousTaskFile[2] = 0x00;
pATAData->PreviousTaskFile[3] = 0x00;
pATAData->PreviousTaskFile[4] = 0x00;
pATAData->PreviousTaskFile[5] = 0x04;
pATAData->PreviousTaskFile[7]=  0x00;

pATAData->CurrentTaskFile[6] = 0x24; // command Read Sector(s) Ext(0x24)
/* sends the command to the device, **hDevice** is device handle*/
status = DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH, pATAData, dataSize,Buffer, dataSize, &bytescopied, NULL );

But here is the same problem. command executed successfully but it does not read any sector. I am unable to find any error.

Sam
  • 61
  • 1
  • 10
  • There are 2 types of errors you need to take into consideration when using DeviceIoControl. (1) The first is from DeviceIoControl telling you if the command was sent successfully to the device. (2) The second is from the device it self. In this case, a SATA or PATA drive. Every command will return a status indicating if the command is successful or not. This will be in the "Status" register (CurrentTaskFile[7]) and the "Error" register (CurrentTaskFile[0]). The "Status" register tells you if there is an error, and the "Error" register will tell you the type of error. – tchau.dev May 19 '15 at 17:34
  • I got none of the above errors. Everything looks like ok but no buffer is read from drive. The "Status" register (CurrentTaskFile[7] and "Error" register (CurrentTaskFile[0]) both are zero. GetLastError() retruns code(0x57)(ERROR_INVALID_PARAMETER). DeviceIoControl returns TRUE but output buffer is empty. The lpBytesReturned parameter's value is 40(exactly the size of PATA_PASS_THROUGH_EX structure) it means no data is read from the drive. – Sam May 20 '15 at 05:23
  • Error register can be zero, but the Status register should at least have the "Device Ready" bit set to one (bit 6). Don't trust the DeviceIoControl() return status. It looks like GetLastError() is telling the truth. – tchau.dev May 20 '15 at 06:03
  • Correction on my part, the "Status" register should be the same as the "Command" register, which is CurrentTaskFile[6]. – tchau.dev May 20 '15 at 06:06
  • No error no result, what should i do. The "Status" register (CurrentTaskFile[7] and "Error" register (CurrentTaskFile[0]) both are zero. Only two registers "Command" register is 0x58, and "Device" register is 0xE0 has any returned value that is not helping. What should i do. – Sam May 25 '15 at 08:19
  • Are you still stuck on this? If so, you can send me the code, and I'll test it out. – tchau.dev Jun 01 '15 at 20:48
  • Thanks @tchau.dev . Now its working. But here is a problem, does not pass >256 sector count transfer length (ATA 48-bit is capable to transfer upto 65536 sectors). I have used all commands Read Sector(s), Read Multiple. I am not able to make a transfer more than 256 sectors in one time. For 256 sectors working but if i send 257 or more than DeviceIOControl returns 0. Do you know any thing about this. – Sam Jun 02 '15 at 09:03
  • Yeah it's a limitation. Not sure why, but the only way around it is to send ReadFile and WriteFile. The problem with this is the fact that you can't send the specific read write command that you want. – tchau.dev Jun 02 '15 at 14:43
  • Any idea to overcome this limitation. Any alternative. why can we use full capacity of ATA 48-bit commands(ATA 48-bit is capable to transfer upto 65536 sectors). Only ATA i have to use. Thanks @tchau.dev. – Sam Jun 05 '15 at 06:16
  • The limitation is at the kernel level. Most companies write their own driver to overcome this limitation. You can do the same. That said, I wouldn't recommend it unless you really know what you're doing, or know someone who does. – tchau.dev Jun 05 '15 at 07:21
  • @Sam Sam, what was the reason for your error in this code? how did you fix it? – Mery Ted Jun 30 '20 at 07:25