Search code examples
cfile-iobufferansi-c

How to fix creating a buffer and Writing it directly to disk (WriteFile)


The code:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <Windows.h>

HANDLE creatFile(void);
long WriteBuffer(HANDLE);
char * GetBuffer(void);

void main(void)
{
HANDLE hFile;
printf("CreateFile: ");
hFile = creatFile();
if(hFile != NULL) 
    {
    WriteBuffer(hFile);
    FlushFileBuffers(hFile);
    }
CloseHandle(hFile);
printf("\n\rDone");
getchar();
}

HANDLE creatFile(void)
{
HANDLE hFile;
LPCWSTR sFileName  = L"\\\\.\\E:";
DWORD dwDesiredAccess =  GENERIC_WRITE;
DWORD fShareMode = FILE_SHARE_WRITE | FILE_SHARE_WRITE;
DWORD fCreationDisposition = OPEN_EXISTING;
DWORD fFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;

hFile = CreateFile(sFileName, dwDesiredAccess,fShareMode,
    NULL, fCreationDisposition, fFlagsAndAttributes,
    NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
    hFile = NULL;
    printf("INVALID_HANDLE_VALUE: ");

    switch (GetLastError())
                {
    case 5:
        printf("\n\r Administrative Account required to run this program\n\r");
        break;
    case 87:
        printf("\n\r Invalid Parameter in CreateFile Call \n\r");
        break;
    default:

        printf("Error %d\n",GetLastError());
        break;
    }




    return NULL;
}
else
{
    printf("Attached -> %d\n\r",hFile);
    return hFile;
}
}


long WriteBuffer(HANDLE hFile)
{
char *str = GetBuffer(); // x 64 will give us 512 (sector sized buffer) ;
DWORD bytesWritten;
long totalBytesWritten = 0;
long idx = 0;
int len = strlen(str);

for(idx = 0; idx < 100000; idx ++)
{

    if(WriteFile(hFile, str, 512  * sizeof(char), &bytesWritten, NULL))
    {

        totalBytesWritten += bytesWritten;
        printf("Sectors Written : %d\r",idx+1);
    }
    else
    {
        int le = GetLastError();
        printf("Last Error : %d\r",GetLastError());
        break;
    }
}
printf("\n\r");
printf("Bytes Written: %d\n\r", totalBytesWritten);
printf("Handle -> %d\n\r",hFile);
return totalBytesWritten;
}

char * GetBuffer(void)
{
int i = 0, idx = 0;
const char * cstr_init = "ERASED1 ";
char *buffer = (char*)malloc(512);
char word2[512];

for (idx = 0; idx < 512; idx+=8) {
    for (i = 0; i < 8; i++) {
        buffer[idx+i] = cstr_init[i];
        if(strlen(buffer) == 512) 
            break;
    }
}


return buffer;
}

The problems:

  1. char * GetBuffer has extraneous data of 16 bytes in it. I modified WriteFile so that it only write 512 (instead of the 528) chars that the buffer actually holds.
  2. After 16 sectors of writing - WriteFile fails with GetLastError = 5 (Access Denied)

The Questions:

  1. How do I fix WriteFile so that it does NOT fail after 16 sectors and...

  2. How do I fix GetBuffer so it actually produces a 512 buffer and not 528?

Notes The application is ANSI C and the program is being run as admin.


Solution

  • I was unable to fix question 2 - the way I would have liked. However, by Telling WriteFile to write 512 bytes out of the buffer of 528 bytes - I got the desired results. As for Question 1.

    Because The Disk drive has a Filesystem on it - Windows OS recognises that fact and prevents writing to the full drive. All I needed to do was in fact Lock the drive and that would give me exclusive access to the drive.

    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <Windows.h>
    #include <winioctl.h>
    
    HANDLE creatFile(void);
    long WriteBuffer(HANDLE);
    char * GetBuffer(void);
    
    void main(void)
        {
    HANDLE hFile;
    printf("CreateFile: ");
    hFile = creatFile();
    if(hFile != NULL) 
    {
        WriteBuffer(hFile);
        FlushFileBuffers(hFile);
    }
    CloseHandle(hFile);
    printf("\n\rDone");
    getchar();
    }
    
    HANDLE creatFile(void)
    {
    HANDLE hFile;
    LPCWSTR sFileName  = L"\\\\.\\E:";
    DWORD dwDesiredAccess =  GENERIC_WRITE;
    DWORD fShareMode = FILE_SHARE_WRITE | FILE_SHARE_WRITE;
    DWORD fCreationDisposition = OPEN_EXISTING;
    DWORD fFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
    BOOL bResult = FALSE;                 // results flag
    LPDWORD lpBytesReturned = 0;
    
    hFile = CreateFile(sFileName, dwDesiredAccess,fShareMode,
        NULL, fCreationDisposition, fFlagsAndAttributes,
        NULL);
    
    if (hFile == INVALID_HANDLE_VALUE)
    {
        hFile = NULL;
        printf("INVALID_HANDLE_VALUE: ");
    
        switch (GetLastError())
        {
        case 5:
            printf("\n\r Administrative Account required to run this program\n\r");
            break;
        case 87:
            printf("\n\r Invalid Parameter in CreateFile Call \n\r");
            break;
        default:
    
            printf("Error %d\n",GetLastError());
            break;
        }
    
    
    
    
    return NULL;
    }
    else
    {
        printf("Attached -> %d\n\r",hFile);
    // HERE I JUST ADD THE FSCTL_LOCK_VOLUME command to stop Windows preventing me from writing to the drive        
        bResult = DeviceIoControl(hFile,                       // device to be queried
            FSCTL_LOCK_VOLUME,   // dwIoControlCode
            NULL, 0,                       // no input buffer
            NULL, 0,            // output buffer
            lpBytesReturned,                         // # bytes returned
            (LPOVERLAPPED) NULL);          // synchronous I/O
    
        return hFile;
    }
    }
    
    
    long WriteBuffer(HANDLE hFile)
    {
    char *str = GetBuffer(); // x 64 will give us 512 (sector sized buffer) ;
    DWORD bytesWritten;
    long totalBytesWritten = 0;
    long idx = 0;
    int len = strlen(str);
    
    for(idx = 0; idx < 100000; idx ++)
    {
    
        if(WriteFile(hFile, str, 512  * sizeof(char), &bytesWritten, NULL))
        {
    
            totalBytesWritten += bytesWritten;
            printf("Sectors Written : %d\r",idx+1);
        }
        else
        {
            int le = GetLastError();
            printf("\n\rLast Error : %d\r",GetLastError());
            break;
        }
    }
    printf("\n\r");
    printf("Bytes Written: %d\n\r", totalBytesWritten);
    printf("Handle -> %d\n\r",hFile);
    return totalBytesWritten;
    }
    
    char * GetBuffer(void)
    {
    const char * cstr_init = "ERASED2 ";
    const int cstr_init_len = strlen(cstr_init);
    char *buffer = (char*)malloc(513);
    int i;
    for (i = 0; i < 512; i+=8) {
        memcpy(buffer+i, cstr_init, cstr_init_len);
        // Or strcpy(buffer+1, cstr_init);
        // Or strcat(buffer, cstr_init); // Inefficient because each call runs from buffer[0] to find a '\0' for where to start appending
    }
    return buffer;
    }
    

    So for future reference, if you wish to write directly to the drive it is important to LOCK the volume first. I know that there are memory leaks within the above code - but as a learning exercise for the Writing of data to a drive I am not bothered. The code will be tidied up and made into a .dll