Question

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.

Was it helpful?

Solution 3

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

OTHER TIPS

I can't speak to the error with WriteFile(), however, you've got issues with your string operations.

C strings are null terminated, that is, the string literal "abc" is actually an array of characters like: {'a','b','c','\0'} All str...() operations rely on this fact. There is no information stored anywhere on the length of the string, only the fact that it is expected to end with a '\0'.

Your GetBuffer() function improved:

char * GetBuffer(void)
{
    int i = 0, idx = 0;
    const char * cstr_init = "ERASED1 ";
    char *buffer = malloc(513); // Space for a '\0'

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

You were getting weird results for strlen() because it looks for a '\0' and was only finding one at 528 bytes, reading outside of the 512 bytes malloced is invoking undefined behavior, you could have found a '\0' at 513 bytes, or never found one.

Other comments, after calling GetBuffer() you never free() the char * returned, that is a memory leak since it was malloced and lost outside of that context. Also, a better implementation of GetBuffer() would be:

char * GetBuffer(void)
{
    const char * cstr_init = "ERASED1 ";
    const int cstr_init_len = strlen(cstr_init);
    char * buffer = calloc(1,513); // Guaranteed zeroed
    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;
}

the INT type could only be a 2 byte number from 0 - 65536 unsigned. replace all INT types with LONG (long) to start with...Not sure what compiler environment you are in though - as this change may not apply

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top