how to do multiple read/write HDD/USB bytes in c [

2019-08-21 20:20发布

问题:

This question already has an answer here:

  • C++ sector aligned read 2 answers
  • SetFilePointer without FILE_FLAG_NO_BUFFERING 2 answers

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...

  1. declare variables
  2. initialize variables
  3. SetFilePointer()
  4. ReadFile()
  5. output the read bytes to a file
  6. use ReadFile() to get more bytes
  7. output the read bytes to same file again
  8. repeat 6 and 7 (actually just repeating 4 and 5)

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, 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.

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. The posts stated that have the answers are not answers of the question in the title of this thread. Please read the title carefully. The "answers" provided are about SetFilePointer. The one answer that is here is also not an answer. This is all because none of the supposed answers contain how to read/write HDD/USB bytes in C.

回答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, 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++.