Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ER: Ask user about disabling exclusive locking if we can't lock the drive #773

Closed
pbatard opened this issue Jun 9, 2016 · 10 comments
Closed
Assignees
Milestone

Comments

@pbatard
Copy link
Owner

pbatard commented Jun 9, 2016

If you have a DOS prompt open on a drive that you are attempting to format, the Windows 10 default formatting tool will ask the following:
formatting_e_with_a_dos_prompt_open_windows_default

I suspect they attempt to exclusively lock the drive, find that it fails, and, if the user answers 'Yes', try formatting without the exclusive lock.

Maybe we should do something similar in Rufus?

@pbatard pbatard self-assigned this Jun 9, 2016
@pbatard pbatard added this to the 3.0 milestone Jun 9, 2016
@pbatard pbatard modified the milestones: 2.11, 3.0 Jul 9, 2016
@pbatard pbatard modified the milestones: 2.12, 2.11 Aug 26, 2016
@pbatard
Copy link
Owner Author

pbatard commented Nov 21, 2016

Mmm, this is more tricky than it looks. Or at least, I think Windows is using a different method than trying to lock the drive and seeing if it fails before producing that message.

The message itself is generated from shell32.dll (MUI String ID 28701/0x701d - "This drive is in use. Another program or process is using this drive. Do you want to format it anyway?", as per C:\Windows\System32\en-GB\shell32.dll.mui on Windows 10), but I can't say I'm too thrilled about the idea of having to disassemble the 21.1MB shell32.dll to figure out how Microsoft does it...

By the way, MUI Dialog ID 28672/0x7000 is the Format Dialog.

@pbatard
Copy link
Owner Author

pbatard commented Dec 13, 2016

Looking further into this, it doesn't look like shell32.dll is doing too good a job at detecting physical drive holdup anyway. It may be able to detect a DOS prompt open against the logical drive, but it doesn't seem to detect something like HxD accessing the physical one.

The only way I'm seeing to actually achieve what we want is to go over ALL processes, and replicate the code that Process Explorer uses to list the files opened by each process, as it's the only application I know of that is able to list physical drives (for instance, it'll list \Device\Harddisk5\DR7 as open when HxD is accessing a removable drive)...

Yay, more reverse engineering for me!

At any rate, this isn't something I'm going to look into for the 2.12 timeframe.

@pbatard pbatard modified the milestones: 3.0, 2.12 Dec 13, 2016
@pbatard pbatard removed this from the 3.0 milestone Jan 17, 2017
@pbatard
Copy link
Owner Author

pbatard commented Apr 25, 2017

Well, since the source for Process Explorer is not available, but the good people of Process Hacker are offering what looks like a powerful GPLv3 equivalent, I've been trying to see if I could reuse some of their code (though I'm not entirely sure about the idea of having to embed 32+64 bit Windows driver binaries in Rufus to be able to perform handle lookup)...

Only issue however is that the latest Process Hacker release does not seem to be able to list disk/volume handles. Damn!

Logged issue winsiderss/systeminformer#131 to try to get some input on that...

@pbatard
Copy link
Owner Author

pbatard commented Apr 26, 2017

Looks like I wasn't looking at the right place, which is good news!

It also appears that the kernel driver is not needed when looking for handles in PH, so I might be able to craft a handle lookup based of that code and integrate it into Rufus. Other nice thing about PH is that it can also tell whether the handle is opened for read/write, which may come handy...

@pbatard pbatard added this to the 2.15 milestone Apr 27, 2017
pbatard added a commit that referenced this issue Apr 27, 2017
* Using functionality from Process Hacker:
  https://github.com/processhacker2/processhacker2/
* Part of the #773 enhancement
* Also fix minor MinGW and WDK warnings
@pbatard pbatard removed the deferred label Apr 29, 2017
@dmex
Copy link
Contributor

dmex commented May 5, 2017

It may be able to detect a DOS prompt open against the logical drive, but it doesn't seem to detect something like HxD accessing the physical one.

It's possible HxD is using a custom driver that bypasses Windows... Unmounting and formatting the disk should still succeed since Windows won't know about those handles (and it's not your problem if other software is bypassing Windows and breaking stuff - that's their problem 😄).

I think Windows is using a different method than trying to lock the drive and seeing if it fails before producing that message.

Explorer uses NtQueryInformationFile with the FileProcessIdsUsingFileInformation class.

Try using this function instead of the handle enumeration code from Process Hacker:

/**
 * Query processes with open handles to a file, volume or disk.
 */
NTSTATUS QueryProcessesUsingVolumeOrFile(
    _In_ HANDLE VolumeOrFileHandle,
    _Out_ PFILE_PROCESS_IDS_USING_FILE_INFORMATION *Information
    )
{
    static ULONG initialBufferSize = 0x4000;
    NTSTATUS status;
    PVOID buffer;
    ULONG bufferSize;
    IO_STATUS_BLOCK isb;

    bufferSize = initialBufferSize;
    buffer = malloc(bufferSize);

    while ((status = NtQueryInformationFile(
        VolumeOrFileHandle,
        &isb,
        buffer,
        bufferSize,
        FileProcessIdsUsingFileInformation
        )) == STATUS_INFO_LENGTH_MISMATCH)
    {
        free(buffer);
        bufferSize *= 2;

        // Fail if we're resizing the buffer to something very large.
        if (bufferSize > SIZE_MAX) // SIZE_MAX from limits.h for maximum malloc.
            return STATUS_INSUFFICIENT_RESOURCES;

        buffer = malloc(bufferSize);
    }

    if (!NT_SUCCESS(status))
    {
        free(buffer);
        return status;
    }

    if (bufferSize <= 0x100000) initialBufferSize = bufferSize;
    *Information = (PFILE_PROCESS_IDS_USING_FILE_INFORMATION)buffer;

    return status;
}

You can use this function to identify which processes have open handles to the file, volume or disk.

PFILE_PROCESS_IDS_USING_FILE_INFORMATION info = NULL;

HANDLE rawHandle = CreateFile(
    L"\\\\.\\PhysicalDrive0",
    FILE_READ_ATTRIBUTES | SYNCHRONIZE,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
    );

if (NT_SUCCESS(QueryProcessesUsingVolumeOrFile(rawHandle, &info)))
{
    // Call OpenProcess for each returned PID to get the process name (e.g. GetModuleFileNameEx)...

    free(info);
}

HANDLE volumeHandle = CreateFile(
    L"\\\\.\\C:",
    FILE_READ_ATTRIBUTES | SYNCHRONIZE,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
    );

if (NT_SUCCESS(QueryProcessesUsingVolumeOrFile(volumeHandle, &info)))
{
    // Call OpenProcess for each returned PID to get the process name (e.g. GetModuleFileNameEx)...

    free(info);
}

HANDLE fileHandle = CreateFile(
    L"C:\\Windows\\Explorer.exe",
    FILE_READ_ATTRIBUTES | SYNCHRONIZE,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
    );

if (NT_SUCCESS(QueryProcessesUsingVolumeOrFile(fileHandle, &info)))
{
    // Call OpenProcess for each returned PID to get the process name (e.g. GetModuleFileNameEx)...

    free(info);
}

Note: The above code also works from standard user accounts that do not have administrative privileges 😃

@pbatard
Copy link
Owner Author

pbatard commented May 5, 2017

It's possible HxD is using a custom driver that bypasses Windows...

Nah. Accessing the disk doesn't require a driver (I'm doing that all the time in Rufus), especially as HxD requires Admin privileges to be able to do so. The most logical explanation is that Microsoft is simply checking against the volumes and not the disks, so they don't see all of the processes that may be accessing the device.

Try using this function instead of the handle enumeration code from Process Hacker:

That's a great tip. Many thanks for that!

As you probably guessed, I'm not familiar with the Nt... calls, so if NtQueryInformationFile() fits the purpose better I'll probably go with that. I have to say I'm already quite happy with the current lookup process, which I got from the handle search of Process Hacker, but what you are proposing looks even better. I'll give it a try when I have a chance and see how it goes.

@dmex
Copy link
Contributor

dmex commented May 5, 2017

I'm already quite happy with the current lookup process, which I got from the handle search of Process Hacker,

Something like this might also help 👍

// Check if we need to enumerate 
if (VolumeOpenHandleCount() > 0)
{
    PhEnumHandles(...)
}
ULONG VolumeOpenHandleCount(
    _In_ HANDLE VolumeOrFileHandle
    )
{
    NTSTATUS status;
    ULONG initialBufferSize = 0x4000;
    ULONG bufferSize;
    ULONG handleCount;
    PFILE_PROCESS_IDS_USING_FILE_INFORMATION info;
    IO_STATUS_BLOCK isb;

    bufferSize = initialBufferSize;
    info = malloc(bufferSize);

    while ((status = NtQueryInformationFile(
        VolumeOrFileHandle,
        &isb,
        info,
        bufferSize,
        FileProcessIdsUsingFileInformation
        )) == STATUS_INFO_LENGTH_MISMATCH)
    {
        free(info);
        bufferSize *= 2;

        if (bufferSize > SIZE_MAX)
            return 0;

        info = malloc(bufferSize);
    }

    if (!NT_SUCCESS(status))
    {
        free(info);
        return 0;
    }

    handleCount = info.NumberOfProcessIdsInList;

    free(info);
    return handleCount;
}

@pbatard
Copy link
Owner Author

pbatard commented May 5, 2017

Okay, I played with your code, and it seems to work just fine. Got no issue identifying processes like HxD accessing the disks/volumes (or even Rufus, when it turned out I forgot to close the handle I had opened for the search).

The only real issue I have with it, which doesn't have anything to do with the code is: Part of the reason I need a process search is to figure out which process(es) might be preventing Rufus' CreateFile() calls to succeed... and this new method does requires a successful call to CreateFile() to have taken place, to obtain the handle.

This is also the reason why I don't think I'll apply the VolumeOpenHandleCount() speedup either, because that too requires a handle to the target to have been obtained.

Granted, the only reason the CreateFile() calls placed by Rufus tend to fail is because we are trying to avoid using FILE_SHARE_WRITE on volumes and disks that other processes have opened, which isn't a constraint we have when opening the process in the new search (which is how I tested it). But as I said, I'm already happy enough with the current process search, even if it's a bit costly. The thing is, we only perform this search in Rufus when we're stuck trying to open a volume or disk, to let the users know what the cause of the holdup might be, so performance isn't exactly a big concern.

I have still integrated your alternate search suggestion, as unused code, since it does work. Depending on the feedback I get with regards to the process search, I may switch to using this new method in a future version.

pbatard pushed a commit that referenced this issue May 5, 2017
* As suggested in #773
* Don't switch to using this method though, as it requires a handle to the disk or volume
  to be obtained, and we use the process search in case there is an issue doing so.
@pbatard
Copy link
Owner Author

pbatard commented May 16, 2017

Moving the remainder of this ER (adding a UI report that lists the drive handle-hogging processes + running a sanity check to prompt the user to disable drive locking) to a future release.

Will see how the 2.15 log-only report of conflicting processes goes, and take it from there.

@pbatard pbatard removed this from the 2.15 milestone May 16, 2017
@pbatard pbatard added this to the 2.16 milestone Jul 15, 2017
@lock
Copy link

lock bot commented Apr 6, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue if you think you have a related problem or query.

@lock lock bot locked and limited conversation to collaborators Apr 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants