Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
443 views
in Technique[技术] by (71.8m points)

windows - How to explicitly lock a mounted file system?

How do i write to a physical drive in Windows 7?

I am trying to write to a physical disk (e.g. \.PhysicalDrive0) in Windows 7.

This question has been asked to death, but has never been answered. It is something that used to work in Windows XP, but Microsoft intentionally broke in Windows Vista. Microsoft provides hints about how to do it, but nobody has even been able to figure it out.

It used to work

In the olden days, writing to a physical disk was allowed (as long as you were an administrator). The method to do it was even documented in a Knowledge Base article:

INFO: Direct Drive Access Under Win32

To open a physical hard drive for direct disk access (raw I/O) in a Win32-based application, use a device name of the form

\.PhysicalDriveN

where N is 0, 1, 2, and so forth, representing each of the physical drives in the system.

You can open a physical or logical drive using the CreateFile() application programming interface (API) with these device names provided that you have the appropriate access rights to the drive (that is, you must be an administrator). You must use both the CreateFile() FILE_SHARE_READ and FILE_SHARE_WRITE flags to gain access to the drive.

All that changed in Windows Vista, when addition security restrictions were put in place.

How do you write to a physical disk?

Many people, and many answers, on many stackoverflow questions confuse:

  • writing to a physical disk (e.g. \.PhysicalDrive0), and
  • writing to a logical volume (e.g. \.C$)

Microsoft notes the restrictions placed on both kinds of operations:

Blocking Direct Write Operations to Volumes and Disks

Write operations on a DASD (Direct access storage device) 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.
  • The write request has been flagged by a kernel-mode driver that indicates that this check should be bypassed. The flag is called SL_FORCE_DIRECT_WRITE and it is in the IrpSp->flags field. This flag is checked by both the file system and storage drivers.

In my case i am asking about writing to a Physical, not a Logical one. Microsoft notes the new set of restrictions on writing to a physical disk handle:

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.
  • My sectors being written do fall within a file system --> fail
  • My sectors being written do fall within mounted, unlocked, file system --> fail
  • My sectors being written do fall within a file system that is mounted, and in inside a logical volume that has a file system.

The hints on how to make it work revolve around:

  • unmounting a file system
  • locking a file system

But the question is how do you unmount a file system? How do you lock a file system?

What are you doing now?

I am able to read all physical sectors of a disk; that is no problem. The problem is when i want to write to a physical sector of the disk.

The current code i have is, in pseudo-code:

void ZeroSector(Int64 PhysicalSectorNumber)
{
    String diskName := '\.PhysicalDrive0'; 

    DWORD desiredAccess := GENERIC_READ or GENERIC_WRITE;

    //INFO: Direct Drive Access Under Win32
    //https://support.microsoft.com/en-us/kb/100027
    //says you nedd both
    DWORD shareMode := FILE_SHARE_READ or FILE_SHARE_WRITE;

    //Open the physical disk
    hDisk := CreateFile(diskName, desiredAccess, shareMode,
            nil, OPEN_EXISTING, 0, 0);
    if hDisk = INVALID_HANDLE_VALUE
        RaiseLastWin32Error();
    try
    {
        Int32 bytesPerPhysicalSector := 4096; //Determined elsewhere using IOCTL_STORAGE_QUERY_PROPERTY+STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR

        //Setup buffer for what we will be writing
        Byte[] buffer = new Byte[bytesPerPhysicalSector];

        //Calculate the byte offset of where the sector is
        Int64 byteOffset = PhysicalSectorNumber * bytesPerPhysicalSector;

        //Seek to that byte offset
        SetFilePointer(hDisk, byteOffset.Lo, byteOffset.Hi, FILE_BEGIN); 

        //Write the buffer
        DWORD numberOfBytesWritten;
        if (!WriteFile(hDisk, buffer, bytesPerPhysicalSector, out numberOfBytesWritten, nil))
            RaiseLastWin32Error();
    }
    finally
    {
        CloseHandle(hDisk);
    }
}

Surprisingly:

  • i can open the physical disk for GENERIC_READ + GENERIC_WRITE access
  • it doesn't fail until the actual WriteFile, which fails with:

    ERROR_ACCESS_DENIED

How to do what Microsoft says

Microsoft said that my write would fail, and they were right. They said that i need to explicitly lock the file system:

Write operations on a disk handle will succeed if:

  • The sectors being written to fall within a mounted file system that is locked explicitly.

Except i don't know how to do that.

I know i probably have to use DeviceIoControl and one of the IOCTLS to "lock" a volume. But that presents three challenges:

  • figuring out which volume(s) are on the physical disk selected
  • figuring out which IOCTL to use
  • figuring out how to unlock the locked volumes

Ignoring those problems, i blindly tried the LockFile API. Just before calling WriteFile:

//Try to lock the physical sector we will be writing
if (!LockFile(DiskHandle, byteOffset.Lo, byteOffset.Hi, bytesPerPhysicalSector, 0)
   RaiseLastWin32Error();

That fails with:

ERROR_INVALID_FUNCTION (1)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Check out FSCTL_LOCK_VOLUME, FSCTL_DISMOUNT_VOLUME control codes. I believe you would have to enum all volumes you have on your disk, and then dismount-and-lock them. After lock succeeded, the disk is all yours.

You probably won't be able to do this on a system drive though. I'd also guess that there will be caveats with volumes that contain page files.

after I have done this, I can write to the corresponding \.PhysicalDrive3. I could not before:

HANDLE hVol = CreateFileA(
    "\\.\X:", 
    FILE_READ_DATA | FILE_WRITE_DATA,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD unused;
BOOL b = DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
if (!b) {
    printf("%u", GetLastError());
    abort();
}

...
HANDLE h = CreateFileA(
    argv[1], // that's my \physicaldrive3
    FILE_READ_DATA | FILE_WRITE_DATA, 
    FILE_SHARE_READ | FILE_SHARE_WRITE, 
    NULL,
    OPEN_EXISTING,
    0,
    NULL);
...
    bResult = WriteFile(h, buf, cb, &dwWritten, &foo);
    if (!bResult) {
        // used to fail without messing with vol handle
        printf("Failed writing data. Error = %d.
", GetLastError()); 
        return 0;
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...