First, here are a couple links i looked at...
Read and write hard disk sector directly and efficiently
Read specific sector on hard drive using C language on windows
I'm trying to do almost the same thing. What I'm having trouble with is reading the device multiple times so I can store the read bytes from a DEVICE (USB) to writing them into a FILE.
This is what I am trying to do...
This doesn't seem to be working. I would like to read x amount of bytes and store those values into a file then read more and store those values in the same file as last time. I would like it to repeat the process until it reads to the end of the device. I would like this program to work on any size of device. If I can put it in a loop then I can read and write infinite sized devices.
This is a how to read/write so I would like to do this in reverse as well. Read the file for values then write them to the device.
I'm using a 128MB USB. It contains 131858432 bytes. If any more info is needed then I will post it if I have it.
My code:
#include <windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
BYTE sector[0x400] = {0};
DWORD bytesRead;
HANDLE device = NULL;
int numSector = 1;
int maxRead = 1;
FILE *readChar = fopen("G:\\usb_128MB_Dec2.txt", "w+");
device = CreateFile("\\\\.\\L:", // Drive to open
GENERIC_READ|GENERIC_WRITE, // Access mode
FILE_SHARE_READ|FILE_SHARE_WRITE, // Share Mode
NULL, // Security Descriptor
OPEN_EXISTING, // How to create
0, // File attributes
NULL); // Handle to template
if(device == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
system("pause");
return 1;
}
// set the file pointer for first time
SetFilePointer(device, numSector, NULL, FILE_BEGIN);
// Edit 1. Comment 2.
if(GetLastError() != NO_ERROR)
{
printf("GetLastError: %d\n", GetLastError());
goto end; // end: is before closing files or handles at end of main()
}
// read device for maxRead number of bytes and store the reading into a file
ReadFile(device, sector, maxRead, &bytesRead, NULL);
fprintf(readChar, "%d\n", sector[0]);
// This part of code does not act like expected
SetFilePointer(device, numSector, NULL, FILE_CURRENT);
if(!ReadFile(device, sector, maxRead, &bytesRead, NULL))
printf("err\n");
else
fprintf(readChar, "%d", sector[0]);
end: // Edit 1. Comment 2.
CloseHandle(device);
fclose(readChar);
system("pause");
return 0;
}
Screen ouput:
GetLastError: 87
File output:
Nothing is in the file.
The file should contain an 8 bit decimal value instead of nothing or a 0.
Edit 1:
87 means INVALID PARAMETER.
Your reads have to be sector aligned so you can't seek to offset 1, but only 0, sector_size, 2sector_size, ..., nsector_size, Your reads have also be in multiplies of sector size, and your memory buffer has to be aligned to sector_size.
Sector size could be retrieved with GetDiskFreeSpace and aligned memory can be obtained with VirtualAlloc.
Maybe you should check also FSCTL_LOCK_VOLUME and FSCTL_DISMOUNT_VOLUME.
Here's the other code that works for one time reading of the device
#include <windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
BYTE sector[0x200] = {0};
DWORD bytesRead;
HANDLE device = NULL;
int numSector = 1;
int maxRead = 0x200;
long long int i;
FILE *readChar = fopen("G:\\usb_128MB_Dec.txt", "w+");
device = CreateFile("\\\\.\\L:", // Drive to open
GENERIC_READ|GENERIC_WRITE, // Access mode
FILE_SHARE_READ|FILE_SHARE_WRITE, // Share Mode
NULL, // Security Descriptor
OPEN_EXISTING, // How to create
0, // File attributes
NULL); // Handle to template
if(device == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
system("pause");
return 1;
}
SetFilePointer(device, numSector, NULL, FILE_BEGIN);
if (!ReadFile(device, sector, maxRead, &bytesRead, NULL))
{
printf("ReadFile: %u\n", GetLastError());
goto end;
}
else
{
printf("Success!\n");
}
if(readChar == NULL)
{
printf("Did not open file. Exit 2.");
goto end;
}
for(i = 0; i < maxRead - 1; i++)
{
fprintf(readChar, "%d\n", sector[i]);
}
fprintf(readChar, "%d", sector[i]); // so the previous loop wont add \n to the last read wanted
end:
CloseHandle(device);
fclose(readChar);
system("pause");
return 0;
}
File contents:
235
88
...
Each byte read is stored in decimal value on a new line.
So it may be better understood what I'm trying to do, here it is in code:
// What I want to do..
// This part works
SetFilePointer(device, numSector, NULL, FILE_BEGIN);
ReadFile(device, sector, maxRead, &bytesRead, NULL);
for(i = 0; i < size_of_device - 0x200; i += 512)
{
for(j = 0; j < maxRead; j++)
{
fprintf(readChar, "%d\n", sector[j]);
}
// stops working
SetFilePointer(device, numSector, NULL, FILE_CURRENT);
ReadFile(device, sector, maxRead, &bytesRead, NULL);
}
for(j = 0; j < maxRead - 1; j++)
{
fprintf(readChar, "%d\n", sector[j]);
}
fprintf(readChar, "%d", sector[j]);
// .. end of what i want to do
Edit 2: Working on reading multiple times now.
#include <windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
BYTE sector[0x200] = {0};
DWORD bytesRead;
HANDLE device = NULL;
//int numSector = 512; // original value was 1 not 512 but variable is not needed
int maxRead = 512;
int i, j, k = 0, l; // loop variables
FILE *readChar = fopen("G:\\wii u hdd image\\usb_128MB_Dec3.txt", "w+");
if(readChar == NULL)
{
printf("Error creating file.\n");
goto end;
}
device = CreateFile("\\\\.\\L:", // Drive to open
GENERIC_READ|GENERIC_WRITE, // Access mode
FILE_SHARE_READ|FILE_SHARE_WRITE, // Share Mode
NULL, // Security Descriptor
OPEN_EXISTING, // How to create
0, // File attributes
NULL); // Handle to template
// If device does not contain a handle value
if(device == INVALID_HANDLE_VALUE)
{
printf("Error. GetLastError: %u\n", GetLastError());
goto end;
}
for(i = 0; i < maxRead*503; i++) // maxRead * 503 = 257536
{
// If ReadFile() fails it will exit the program without adding a '\n' to the readChar file.
if(!ReadFile(device, sector, maxRead, &bytesRead, NULL))
{
printf("Error of ReadFile(). GetLastError(): %u\n", GetLastError());
goto end;
}
// If this is the first time through the loop then '\n' won't be added to the readChar file.
if(i != 0)
{
fprintf(readChar, "\n");
system("cls");
printf("%.2f%%\n", (i / 257536));
}
// Runs for 511 times. Then prints the 512th decimal value after the loop.
for(j = 0; j < maxRead - 1; j++)
{
fprintf(readChar, "%d\n", sector[j]);
}
fprintf(readChar, "%d", sector[j]);
}
end:
CloseHandle(device);
fclose(readChar);
system("pause");
return 0;
}
Edit 3:
This question has not been answered in another post.
87 means INVALID PARAMETER.
Your reads have to be sector aligned so you can't seek to offset 1, but only 0, sector_size, 2*sector_size, ..., n*sector_size, Your reads have also be in multiplies of sector size, and your memory buffer has to be aligned to sector_size.
Sector size could be retrieved with GetDiskFreeSpace
and aligned memory can be obtained with VirtualAlloc
.
Maybe you should check also FSCTL_LOCK_VOLUME
and FSCTL_DISMOUNT_VOLUME
.
Edit
Because direct reading (or writing) isn't buffered, if you want to operate on smaller sizes then sector size, you have to handle buffering yourself.
This code implements reading single bytes from arbitrary places using one sector buffering scheme. Disclaimer: Needs through testing. Beware of possible bugs.
#include <windows.h>
typedef __int64 int64;
struct MyDevice
{
HANDLE handle;
DWORD sector_size;
int64 current_sector_position;
void* sector_buffer;
};
BOOL OpenMyDevice( struct MyDevice* device, const char* name )
{
device->current_sector_position = -1;
device->handle = INVALID_HANDLE_VALUE;
device->sector_buffer = 0;
{
DWORD bytes_per_sector, unused1, unused2, unused3;
// GetDiskFreeSpace doesn't like "\\.\".
const char* name2 = name;
if ( strncmp( name, "\\\\.\\", 4 ) == 0 )
name2 = name + 4;
// For comaptibility reasons we will get logical sector size here.
// For Vista+ it would be better to use DeviceIoControl with IOCTL_STORAGE_QUERY_PROPERTY.
if ( !GetDiskFreeSpace( name2, &unused1, &bytes_per_sector, &unused2, &unused3 ) )
return FALSE;
device->sector_size = bytes_per_sector;
}
device->sector_buffer = VirtualAlloc( 0, device->sector_size, MEM_COMMIT, PAGE_READWRITE );
if ( !device->sector_buffer )
return FALSE;
device->handle = CreateFile(
name,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING, // Can be zero, but in most cases it assumed by Windows.
0 );
if ( device->handle == INVALID_HANDLE_VALUE )
{
VirtualFree( device->sector_buffer, 0, MEM_RELEASE );
device->sector_buffer = 0;
return FALSE;
}
return TRUE;
}
// Call only when OpenDevice was successful.
void CloseMyDevice( struct MyDevice* device )
{
CloseHandle( device->handle );
device->handle = INVALID_HANDLE_VALUE;
VirtualFree( device->sector_buffer, 0, MEM_RELEASE );
device->sector_buffer = 0;
}
BOOL LoadMyDeviceSector( struct MyDevice* device, int64 byte_offset )
{
// Calculate sector position.
int64 sector_position = ( byte_offset / device->sector_size ) * device->sector_size;
if ( sector_position == device->current_sector_position )
// No need to load, it is in buffer
return TRUE;
{
LARGE_INTEGER li;
li.QuadPart = sector_position;
if ( SetFilePointerEx( device->handle, li, 0, FILE_BEGIN ) )
{
DWORD read;
if ( ReadFile( device->handle, device->sector_buffer, device->sector_size, &read, 0 ) )
{
if ( read == device->sector_size )
{
device->current_sector_position = sector_position;
return TRUE;
}
// else Hmmm. Maybe this is read beyond EOF?
}
}
}
// Cant guarantee that device->sector_buffer contains valid data.
device->current_sector_position = -1;
return FALSE;
}
BOOL LoadNextMyDeviceSector( struct MyDevice* device )
{
DWORD read;
device->current_sector_position = -1;
if ( ReadFile( device->handle, device->sector_buffer, device->sector_size, &read, 0 ) )
{
if ( read == device->sector_size )
return TRUE;
// else Hmmm. Maybe this is read beyond EOF?
}
return FALSE;
}
BOOL SetMyDevicePos( struct MyDevice* device, int64 sector_aligned_byte_offset )
{
LARGE_INTEGER li;
li.QuadPart = sector_aligned_byte_offset;
device->current_sector_position = -1;
return SetFilePointerEx( device->handle, li, 0, FILE_BEGIN );
}
int GetMyDeviceByte( struct MyDevice* device, int64 offset )
{
if ( LoadMyDeviceSector( device, offset ) )
{
// Calculate position in sector buffer.
int64 offset_in_sector = offset - ( offset / device->sector_size ) * device->sector_size;
return ((unsigned char*)( device->sector_buffer ))[ offset_in_sector ];
}
return -1;
}
BOOL GetMyDeviceBytes( struct MyDevice* device, int64 byte_offset, void* dst_buffer, int64 count )
{
char* dst = (char*) dst_buffer;
int64 sector_position = ( byte_offset / device->sector_size ) * device->sector_size;
int64 start = byte_offset - sector_position; // First loop pass can be unaligned!
while ( count > 0 )
{
if ( LoadMyDeviceSector( device, byte_offset ) )
{
int64 chunk = device->sector_size - start;
if ( chunk > count )
chunk = count;
// chunk <= sector_size so conversion to int isn't harmful.
memcpy( dst, ((char*)(device->sector_buffer)) + start, (int)chunk );
dst += chunk;
byte_offset += chunk;
count -= chunk;
start = 0; // From now loop would be always sector_size aligned.
}
else
return FALSE;
}
return TRUE;
}
int main( int argc, char* argv[] )
{
struct MyDevice device = { INVALID_HANDLE_VALUE };
if ( OpenMyDevice( &device, "\\\\.\\K:" ) )
{
// #1: Get one byte from device.
{
int byte = GetMyDeviceByte( &device, 11111 );
if ( byte >= 0 )
{
}
}
// #2: Get multiple bytes from device.
{
char buff[1000];
if ( GetMyDeviceBytes( &device, 111111, buff, 1000 ) )
{
}
}
// #3: Scan 100 sectors beginning from sector 111 for byte 155.
{
if ( SetMyDevicePos( &device, 111*device.sector_size ) )
{
int i, j;
for ( i = 0 ; i < 100 ; ++i )
{
if ( !LoadNextMyDeviceSector( &device ) )
break;
for ( j = 0 ; j < (int)device.sector_size ; ++j )
{
int byte = ((unsigned char*)( device.sector_buffer ))[ j ];
if ( byte == 155 )
{
// FOUND!
}
}
}
}
}
CloseMyDevice( &device );
}
}
GetMyDeviceByte
and GetMyDeviceBytes
are rather random seek oriented for small byte tranfers. If someone needs to trasfer large amounts of data in sequential order it is a lot faster to SetMyDevicePos
and LoadNextMyDeviceSector
like #3
in main
.
I would prefer to write this code in C++.