0

I need to directly write to some physical device. I follow the indications given in the posts listed bellow and in the MSDN page bellow. But my code still fails in the function writeDisk(HANDLE hDevice) with the error code:
[87] The parameter is incorrect.
But the root of the problem doesn't appears to be there.

Beside that. When I unmount the device, it's no working (I can still access it from the system explorer), the same occur after locking it.

I have already checked the following links:
* Microsoft Docs: Calling DeviceIoControl
* StackOverflow: How to explicitly lock a mounted file system?
* StackOverflow: CreateFile: direct write operation to raw disk "Access is denied"
* StackOverflow: Can I get write access to raw disk sectors under Vista and Windows 7 in user mode?

But sadly, none of them works for me. The most useful threads are the first two.

Here is my code. Once you compile it you must run the application with administrator permissions in order to open the device with WRITE access:

/* This program attempts to directly write to a device (without respecting the file system) */

#include <stdio.h>
#include <windows.h>

BOOL dismountDisk(HANDLE hDevice);
BOOL lockDisk(HANDLE hDevice);
BOOL writeDisk(HANDLE hDevice);
void getSystemMessageString(DWORD errorCode, char *decodedMessage, unsigned long size);

/* Main function
   * Access the device
   * Dismount the device
   * Lock the device explicitly
   * Write to the device
   * Close handler
*/
int main()
{
    /* Access the device */
    HANDLE hDevice = CreateFileA(
        "\\\\.\\PHYSICALDRIVE2", /* device id (get it with `$ wmic DISKDRIVE`) */
        FILE_READ_DATA | FILE_WRITE_DATA, /* read/write access */
        FILE_SHARE_READ | FILE_SHARE_WRITE, /* shared */
        NULL, /* default security attributes */
        OPEN_EXISTING, /* only open if the device exists */
        0, /* file attributes */
        NULL); /* do not copy file attributes */

    if (hDevice == INVALID_HANDLE_VALUE) { /* cannot open the physical drive */
        fprintf(stderr, "Cannot open the device\n");
    } else { /* dismount and lock */
        BOOL result = dismountDisk(hDevice) && lockDisk(hDevice); /* try to dismount and lock the device */
        if (!result) { /* device not dismounted/locked */
            abort(); /* abort the operation */
        } else { /* OK */
            fprintf(stderr, "All OK. Check if the device has been dismounted and locked and press return to write.\n");
            getchar();
            writeDisk(hDevice); /* write to the disk */
        }
        CloseHandle(hDevice); /* close the handler to the device */
    }

    return 0;
}

/* Dismount disk */
BOOL dismountDisk(HANDLE hDevice)
{
    DWORD unused;
    BOOL b = DeviceIoControl(hDevice, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
    if (!b) {
        DWORD errorCode = GetLastError();
        char strBuff[500] = {0}; /* save the error message here */
        getSystemMessageString(errorCode, strBuff, 500);
        fprintf(stderr, "Error dismounting the device: [%lu] %s\n", errorCode, strBuff); /* print the error code and message */
    }
    return b;
}

/* Lock disk */
BOOL lockDisk(HANDLE hDevice)
{
    DWORD unused;
    BOOL b = DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
    if (!b) {
        DWORD errorCode = GetLastError();
        char strBuff[500] = {0}; /* save the error message here */
        getSystemMessageString(errorCode, strBuff, 500);
        fprintf(stderr, "Error locking the device: [%lu] %s\n", errorCode, strBuff); /* print the error code and message */
    }
    return b;
}

/* Write disk */
BOOL writeDisk(HANDLE hDevice)
{
    BYTE buffer[1000]; /* write 100 bytes */
    DWORD bytesWritten; /* to get the total amount of bytes written */
    BOOL b = WriteFile(hDevice, buffer, sizeof (buffer), &bytesWritten, NULL);
    if (!b) {
        DWORD errorCode = GetLastError();
        char strBuff[500] = {0}; /* save the error message here */
        getSystemMessageString(errorCode, strBuff, 500);
        fprintf(stderr, "Error writting the device: [%lu] %s\n", errorCode, strBuff); /* print the error code and message */
    } else {
        fprintf(stderr, "%lu bytes written.\n", bytesWritten);
    }
    return b;
}

/* Convert the error code returned by GetLastError into a human-readable string */
void getSystemMessageString(DWORD errorCode, char *decodedMessage, unsigned long size)
{
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  decodedMessage, size + 1, NULL);
    SetLastError(0); /* clear the last error */
}

  • 1
    Which function is failing? – Jonathan Potter Apr 07 '19 at 21:40
  • It fails with the `writeDisk(HANDLE hDevice)` function, but the root of the error doesn't appears to be there. I will update the description of my problem to show the function that it's failing. – crowporation Apr 07 '19 at 21:42
  • 3
    You cannot write 1000 bytes. You must call WriteFile with buffer size aligned to disc sector size. – Denis Anisimov Apr 07 '19 at 21:58
  • Thanks, I will now get the sector size with `wmic` and then test and update my code. – crowporation Apr 07 '19 at 21:59
  • By the way, why can I still access my SD Card after I unmounted it and lock it? – crowporation Apr 07 '19 at 22:00
  • It works like a charm, thanks! What a dumb I am. After all the problem was located at `writeDisk(HANDLE hDevice)` function hahha. – crowporation Apr 07 '19 at 22:04
  • Post the answer please! – crowporation Apr 07 '19 at 22:04
  • And if you know, please tell me why a can still access my device after dismount and lock it. – crowporation Apr 07 '19 at 22:06
  • I now realized that my question loses all it's sense if I fix the code, so I reverted it to the wrong form (when I still write 1000 bytes) – crowporation Apr 07 '19 at 22:47
  • But still, while that answer the part "I cannot write" that doesn't answer why I can access the disk after dismounting and locking it. – crowporation Apr 07 '19 at 22:47
  • "PhysicalDrive2" is not a volume. It's a disk device, but it does have a volume parameter block (VPB) that manages volume-based access via the RAW filesystem, for which these IOCTL codes trivially succeed. You're only writing to the beginning of the disk, which you're allowed to do as long as it doesn't overlap an existing volume. If the write fails with access denied, you'll need to find the volume device that includes the target sectors and lock it. This will fail if there are existing handles for the volume or its files. You can provide an option to forcibly dismount it, with a warning. – Eryk Sun Apr 08 '19 at 00:53
  • Thanks. The first try it worked like a charm, but the SD Card was formatted in `EXT4` by then (Android filesystem). Once I formatted the device on Windows, the same code started to fail with `[5] Access denied`, just like you point above. What you are saying makes perfect sense. Thanks for that. I will now find how to list volumes and then I will test your suggestion ;) – crowporation Apr 08 '19 at 20:59
  • OMG! I hate the `winapi`... so much!!! – crowporation Apr 08 '19 at 21:00
  • Following Microsoft directions: [FSCTL_DISMOUNT_VOLUME control code](https://msdn.microsoft.com/en-us/library/windows/desktop/aa364562(v=vs.85).aspx): *The `FSCTL_DISMOUNT_VOLUME` control code will attempt to dismount a volume regardless of whether or not any other processes are using the volume, which can have unpredictable results for those processes if they do not hold a lock on the volume.* – crowporation Apr 08 '19 at 21:54
  • So the default behavior is to dismount the volume no matter if there is another handler opened, unless the volume has been locked by another process. – crowporation Apr 08 '19 at 21:56
  • If that's the case, I suppose that there is no way to dismount the volume, unless I kill the processes using it. While that is more complicated, it's also more documented. – crowporation Apr 08 '19 at 21:58
  • @crowporation, locking the volume suffices, but will fail if there are existing handles. You don't have to dismount the volume. If you can't get a lock, you can simply leave it up to the user to resolve the issue. This could be as simple as changing the working directory of a CMD shell instance. – Eryk Sun Apr 10 '19 at 10:50

0 Answers0