Search code examples
pythonwindowsfile-ioio

Writing raw data to physical disk (flash drive) fails with "Bad file descriptor" on windows - Python


I am trying to perform direct data reading and writing to a USB flash drive as a physical drive on windows (10 if it matters). I am using Python to do that.

I have followed the following discussion:

get writing access to raw devices using python with windows

I ran into the same problem kcstrom had in that question. I get a

    Traceback (most recent call last):
    File "C:\script.py", line 49, in <module>
    disk.write(data)
    IOError: [Errno 9] Bad file descriptor

Reading and seeking work as they should, and the data read is correct.

What I know so far:

  1. Handling the drive should be done in sector sized reads/writes/seeks.
  2. The disk must be opened with 'rb+' mode.
  3. Using both \\.\L: and \\.\PhysicalDriveN produce the same results.
  4. The script must run under administrator privileges.
  5. Unmounting the drive and trying to access the \\.\PhysicalDriveN file - "Permission Denied" even when running as admin.

To reproduce the error: (WARNING: THIS CODE CAN CORRUPT PHYSICAL DRIVES, RUN ONLY IF YOU KNOW WHAT YOU ARE DOING)

SOME_OFFSET = 123123
SOME_SIZE = 100
# replace L with the drive letter
disk = open('\\\\.\\L:','r+b')
# or use: (replace N with the drive number)
# disk = open('\\\\.\\PhysicalDriveN','r+b')
disk.seek(SOME_OFFSET*512)
data = disk.read(SOME_SIZE*512)
#modify data...
disk.seek(SOME_OFFSET*512)
disk.write(data)

I can't figure out if this is a permission issue or a problem with how I opened the drive.


Solution

  • According to the MSDN technical note "Blocking Direct Write Operations to Volumes and Disks":

    Write operations on a DASD volume handle will succeed if the file system is not mounted, or if:

    • The sectors being written to are the boot sectors.
    • The sectors being written to reside outside file system space.
    • The file system has been locked implicitly by requesting exclusive write access.
    • The file system has been locked explicitly by sending down a lock/dismount request.

    ....

    Write operations on a disk handle will succeed if:

    • The sectors being written to do not fall within a file system.
    • The sectors being written to fall within a mounted file system that is locked explicitly.
    • The sectors being written to fall within a file system that is not mounted or the volume has no file system.

    Here's a simple context manager to lock a volume. It uses the win32file and winoctlcon modules from PyWin32.

    import msvcrt
    import win32file
    import winioctlcon
    import contextlib
    
    @contextlib.contextmanager
    def lock_volume(vol):
        hVol = msvcrt.get_osfhandle(vol.fileno())
        win32file.DeviceIoControl(hVol, winioctlcon.FSCTL_LOCK_VOLUME,
                                  None, None)
        try:
            yield vol
        finally:
            try:
                vol.flush()
            finally:
                win32file.DeviceIoControl(hVol, winioctlcon.FSCTL_UNLOCK_VOLUME,
                                          None, None)
    
    if __name__ == '__main__':
        VOLUME_PATH = r'\\.\E:'
        OFFSET = 123123
        SIZE = 100
    
        with open(VOLUME_PATH, 'r+b') as disk:
            with lock_volume(disk):
                disk.seek(OFFSET * 512)
                data = disk.read(SIZE * 512)
                disk.seek(OFFSET * 512)
                disk.write(data)
                input('press enter to unlock the volume')