CreateFile: direct write operation to raw disk "Access is denied" - Vista, Win7

24,145

Solution 1

It's quite rare to want only GENERIC_WRITE. You most likely want GENERIC_READ|GENERIC_WRITE.

Solution 2

While the answer of @MSalters makes sense, it is not how my code works. In fact it is so counter-intuitive, I spent several days making sure the code does in fact work.

These code snippets are in a proven, mass consumer market software product. When it needs to modify an on-disk structure, it dismounts the win32 volume so it can modify NTFS or FAT filesystem structures. Interestingly, the volume access handle is read-only:

    char    fn [30];
    snprintf (fn, sizeof fn, "\\\\.\\%s:", vol -> GetVolName ());

    vol_handle = CreateFile (fn, GENERIC_READ,
                            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                            OPEN_EXISTING,
                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS,
                            NULL);

    if (vol_handle == INVALID_HANDLE_VALUE)
    {
          // show error message and exit
    }

If unable to get write access to a volume or partition, this code forces a dismount if the user authorizes such after a stern warning:

if (!DeviceIoControl (vol_handle, FSCTL_DISMOUNT_VOLUME,
                                            NULL, 0, NULL, 0, &status, NULL))
{
    DWORD err = GetLastError ();
    errormsg ("Error %d attempting to dismount volume: %s",
                                                        err, w32errtxt (err));
}

// lock volume
if (!DeviceIoControl (vol_handle, FSCTL_LOCK_VOLUME,
                                            NULL, 0, NULL, 0, &status, NULL))
{
     // error handling; not sure if retrying is useful
}

Writing is then fairly straightforward, except for positioning the file pointer by 512-byte sector:

    long    hipart = sect >> (32-9);
    long    lopart = sect << 9;
    long    err;

    SetLastError (0);       // needed before SetFilePointer post err detection
    lopart = SetFilePointer (vol_handle, lopart, &hipart, FILE_BEGIN);

    if (lopart == -1  &&  NO_ERROR != (err = GetLastError ()))
    {
            errormsg ("HWWrite: error %d seeking drive %x sector %ld:  %s",
                            err, drive, sect, w32errtxt (err));
            return false;
    }

    DWORD   n;

    if (!WriteFile (vol_handle, buf, num_sects*512, &n, NULL))
    {
            err = GetLastError ();
            errormsg ("WriteFile: error %d writing drive %x sectors %lu..%lu:  %s",
                            err, drv, sect, sect + num_sects - 1,
                            w32errtxt (err));
            return false;
    }

Solution 3

There is note in MSDN in documentation of CreateFile:

Direct access to the disk or to a volume is restricted. For more information, see "Changes to the file system and to the storage stack to restrict direct disk access and direct volume access in Windows Vista and in Windows Server 2008" in the Help and Support Knowledge Base at http://support.microsoft.com/kb/942448.

It refers to Vista/2008, but maybe apply to Win7 also.

Share:
24,145
Ali
Author by

Ali

Updated on July 14, 2022

Comments

  • Ali
    Ali almost 2 years

    The relevant Microsoft doc is:
    Blocking Direct Write Operations to Volumes and Disks
    CreateFile, remarks on Physical Disks and Volumes

    The executable is written in C++ and it calls CreateFile() to open an SD card that has no filesystem. The CreateFile() and consecutive ReadFile() calls are successful for GENERIC_READ without Administrator privileges.

    CreateFile fails for GENERIC_WRITE even with Administrator privileges. In the explorer, I set Run as Administrator under Properties > Compatibility > Privilege Level. I also tried to run the executable from an Administrator cmd (started with Ctrl+Shift+Enter, "Administrator:" is in the window title, properly elevated). Still, I get ERROR_ACCESS_DENIED (0x5).

    Do I have to pass something else to CreateFile? I have no idea what security attributes are, I just pass NULL, relevant code is here at line 92, and here at line 48.

    Or is there anything else that should be set to run the process with Administrator privileges?


    A related questions:

    Can I get write access to raw disk sectors under Vista and Windows 7 in user mode?
    Raw partition access in Windows Vista
    How to obtain direct access to raw HD data in C?
    Is there a clean way to obtain exclusive access to a physical partition under Windows?

  • Ali
    Ali over 12 years
    I have read that but what should I do? What is the problem? In the doc, I read this "The changes to the file system and to the storage stack do not apply if the volume is not mounted or if the volume has no file system." In my case there is no file system.
  • Sukasa
    Sukasa over 12 years
    Unfortunately, I only have VB.NET code to show for this, and as I'm using the .NET framework it's probably just a waste of your time to post it - but let me know if you wish me to do so anyways.
  • Ali
    Ali over 12 years
    OK, that was the problem :) Could you also explain me why?
  • MSalters
    MSalters over 12 years
    Not 100% certain, haven't reviewed your whole code. Many "modify" style operations work by reading a large chunk of data, altering the bit they want, and writing the whole chunk back. E.g. you can't physically write one byte to a harddisk, yet file systems pretend that you can. Obviously you need read and write permissions for that.
  • Ali
    Ali over 12 years
    Sorry, I had already awarded the bounty when I noticed your answer. Anyhow, could you make your answer more explicit, please? Are you trying to say that GENERIC_READ alone might be enough to get write access?
  • wallyk
    wallyk over 12 years
    @Ali: GENERIC_READ alone is indeed the case. Subsequent write operations work fine once the locking is successful. As for points, maybe you could upvote it? :-)
  • Ali
    Ali over 12 years
    Yes, I am really sorry about the bounty, I upvoted your question. I will try what you write, and if it really works, I will add a note at the end of the question. That is the best I can do now...
  • daminetreg
    daminetreg over 10 years
    Thank you for this, it saved my day. But I still needed five hours to figure out that for a \\.\PhysicalDriveX path, without GENERIC_WRITE it doesn't let you write on Windows 7.
  • wallyk
    wallyk over 10 years
    @daminetreg: I don't know of any feedback from customers running my code on Vista/Win7/Win8 since the company was sold a few years ago, but, from your comments, would seem the API was made more intuitive.
  • Jules
    Jules about 10 years
    It may be worth noting in passing that SD cards have an unusually large block size, typically 128k. I don't know how Windows handles this at a low level, but it may well be that you need the read access in order to write quantities smaller than this (at a hardware level, you'd read 128k into memory, change the portion you need to change, erase the block, and write it back; the Windows kernel may well decide that read access is required to perform this operation even though the read is not initiated by user level code).
  • MSalters
    MSalters about 10 years
    @Jules: that's hidden from the OS.
  • Masoud Rahimi
    Masoud Rahimi over 2 years
    @wallyk Is sect the starting sector for the write until num_sects?