Link to home
Start Free TrialLog in
Avatar of Dennis Miller
Dennis Miller

asked on

Locking a volume on Windows XP using FSCTL_LOCK_VOLUME

I'm writing a disk utility that will need to directly write to a volume; thus, for compatibility with Vista, I'd like to lock that volume before writing to it.  Whenever I try issuing a FSCTL_LOCK_VOLUME or FSCTL_DISMOUNT_VOLUME command, the call always fails with GetLastError() == 5, error ACCESS_DENIED.  I read that this can be due to anti-virus software that locks up the volume, so I tried disabling McAfee anti-virus; no go.  I opened ProcessExplorer and found that Windows Security Center may have had a handle to the volume, so I disabled it; still didn't work.  I did a search for all handles with the word "volume" in it and found one svchost.exe instance that had a handle to \Device\HarddiskVolume1, so I killed that process; still didn't work.  I even confirmed with another search that there were no more handles with the word "volume" in them anywhere (although there was a handle to \Device\Harddisk0 open in the System process).

I'm not sure why I keep getting this access denied error; I can't locate any program that may have a handle open to the volume, and I've confirmed that I'm running on an account with Administrator privileges.  To confirm that it wasn't just my code, I tried running CHKDSK, once with the /f flag and once with the /x flag, and both times it reported that it couldn't lock the volume.  I've replicated these conditions on two different machines running XP SP 2, and I just can't figure out what's wrong.

Here's how my code opens the volume handle:

HANDLE volume_handle;
volume_handle = CreateFile(
            "\\\\?\\C:",
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            NULL,
            NULL);
if (volume_handle == INVALID_HANDLE_VALUE)
      {
            printf("Failed to open handle to drive %c: - error code %u", drive, GetLastError());
            return 0;
      }

The program always passes that point, so I assume I have a good handle.  Here's how I attempt to lock the volume:

if (!DeviceIoControl(
            volume_handle,
            FSCTL_LOCK_VOLUME,
            NULL,
            0,
            NULL,
            0,
            &bytes_returned,
            NULL))
      {
            if (force) //User chose to force dismount, but even so, I always see the error message.
            {
                  if (!DeviceIoControl(
                        volume_handle,
                        FSCTL_DISMOUNT_VOLUME,
                        NULL,
                        0,
                        NULL,
                        0,
                        &bytes_returned,
                        NULL))
                  {
                        printf("Failed to force dismount - error code %u.\n", GetLastError());
                        return 0;
                  }
            }
        }

I'm inclined to think it's an environment problem rather than something specific to my code, since CHKDSK fails as well.  Did XP simply disable this feature or something?

Any help would be greatly appreciated.
Avatar of jkr
jkr
Flag of Germany image

See the docs on that control code (http://msdn.microsoft.com/en-us/library/aa364575(VS.85).aspx), they clearly state that "This operation fails if there are any open files on the volume".
Avatar of Dennis Miller
Dennis Miller

ASKER

I had read that before, and ensured that there were no programs running in the task bar.  However, I never thought to check that all file handles were closed via Process Explorer, so I'll try doing that next.
Are you booting from 'c:'?
Yes, I am.  I imagine this makes the operation a bit unfeasible; there are numerous system processes that have handles to various files on the volume.  So, what are my options?  Do I have to figure out another way to run the utility, or is there a way to truly force the volume to lock, despite open handles?  Given the number of them, it would seem like a hassle to close all the handles manually.
You cannot even close all the handles manually - that would not work either, since yozu cannot close kernel handles. However, you could run that the same way Windows runs 'Autochk' using 'HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute' - see http://technet.microsoft.com/en-us/sysinternals/bb897447.aspx ("Inside Native Applications")
Sorry, forgot that MS has removed a lot from the original article, which you can find here: http://web.archive.org/web/20060427071811/www.sysinternals.com/Information/NativeApplications.html

The source code is
//======================================================================
//
// Native.h
//
// Mark Russinovich
// http://www.ntinternals.com
//
// This file includes the definitions required by the Native.exe sample
// NT native program to do what it does. 
//
//======================================================================
 
//
// Environment information, which includes command line and
// image file name
//
typedef struct {
       ULONG            Unknown[21];     
       UNICODE_STRING   CommandLine;
       UNICODE_STRING   ImageFile;
} ENVIRONMENT_INFORMATION, *PENVIRONMENT_INFORMATION;
 
//
// This structure is passed as NtProcessStartup's parameter
//
typedef struct {
       ULONG                     Unknown[3];
       PENVIRONMENT_INFORMATION  Environment;
} STARTUP_ARGUMENT, *PSTARTUP_ARGUMENT;
 
//
// Data structure for heap definition. This includes various
// sizing parameters and callback routines, which, if left NULL,
// result in default behavior
//
typedef struct {
	ULONG    	Length;
	ULONG    	Unknown[11];
} RTL_HEAP_DEFINITION, *PRTL_HEAP_DEFINITION;
 
//
// Native NT api function to write something to the boot-time
// blue screen
//
NTSTATUS 
NTAPI 
NtDisplayString(
		PUNICODE_STRING String 
		);
 
//
// Native applications must kill themselves when done - the job
// of this native API
//
NTSTATUS 
NTAPI 
NtTerminateProcess(
		   HANDLE ProcessHandle, 
		   LONG ExitStatus 
		   );
 
//
// Definition to represent current process
//
#define NtCurrentProcess() ( (HANDLE) -1 )
 
//
// Heap creation routine
//
HANDLE 
NTAPI 
RtlCreateHeap(
	      ULONG Flags, 
	      PVOID BaseAddress, 
	      ULONG SizeToReserve, 
	      ULONG SizeToCommit, 
	      PVOID Unknown,
	      PRTL_HEAP_DEFINITION Definition
	      );
 
//
// Heap allocation function (ala "malloc")
//
PVOID 
NTAPI 
RtlAllocateHeap(
		HANDLE Heap, 
		ULONG Flags, 
		ULONG Size 
		);
 
//
// Heap free function (ala "free")
//
BOOLEAN 
NTAPI 
RtlFreeHeap(
	    HANDLE Heap, 
	    ULONG Flags, 
	    PVOID Address 
	    );
 
 
//======================================================================
//
// Native.c
//
// Mark Russinovich
// http://www.ntinternals.com
//
// This is a demonstration of a Native NT program. These programs
// run outside of the Win32 environment and must rely on the raw
// services provided by NTDLL.DLL. AUTOCHK (the program that executes
// a chkdsk activity during the system boot) is an example of a
// native NT application.
//
// This example is a native 'hello world' program. When installed with
// the regedit file associated with it, you will see it print 
// "hello world" on the initialization blue screen during the system
// boot. This program cannot be run from inside the Win32 environment.
//
//======================================================================
#include "ntddk.h" // include this for its native functions and defn's
#include "stdio.h"
#include "native.h"
 
//
// Our heap
//
HANDLE Heap;
 
//----------------------------------------------------------------------
//
// NtProcessStartup
//
// Instead of a 'main' or 'winmain', NT applications are entered via
// this entry point.  
//
//----------------------------------------------------------------------
void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
{
    PUNICODE_STRING commandLine;
    PWCHAR stringBuffer, argPtr;
    UNICODE_STRING helloWorld;
    RTL_HEAP_DEFINITION  heapParams;
 
    //
    // Initialize some heap
    //
    memset( &heapParams, 0, sizeof( RTL_HEAP_DEFINITION ));
    heapParams.Length = sizeof( RTL_HEAP_DEFINITION );
    Heap = RtlCreateHeap( 2, 0, 0x100000, 0x1000, 0, &heapParams );
 
    //
    // Point at command line
    //
    commandLine = &Argument->Environment->CommandLine;
 
    //
    // Locate the argument
    //
    argPtr = commandLine->Buffer;
    while( *argPtr != L' ' ) argPtr++;
    argPtr++;
 
    //
    // Print out the argument
    //
    stringBuffer = RtlAllocateHeap( Heap, 0, 256 );
    swprintf( stringBuffer, L"\n%s", argPtr );
    helloWorld.Buffer = stringBuffer;
    helloWorld.Length = wcslen( stringBuffer ) * sizeof(WCHAR);
    helloWorld.MaximumLength = helloWorld.Length + sizeof(WCHAR);
    NtDisplayString( &helloWorld );
 
    //
    // Free heap
    //
    RtlFreeHeap( Heap, 0, stringBuffer );
 
    //
    // Terminate
    //
    NtTerminateProcess( NtCurrentProcess(), 0 );
}

Open in new window

So, it's going to be impossible for me to write directly to the volume that I boot from?  How do programs like Partition Magic work then; Device Driver Kit?

Although the native NT applications look interesting as well.  Since they run outside the OS, would I be able to use the Assembly-level int 13h disk access functions that Win32 typically blocks?  If that's the case, I have an old DOS version of the utility that might be a good candidate for running in this manner.
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Well, thanks for all your help.  I may just finish the DOS version of the utility, but I'm also going to look into a few more methods of achieving the disk I/O I need on Windows.