FSCTL_DISMOUNT_VOLUME fails on first execution (Win2K/NTFS)

I am perplexed by the behavior of FSCTL_DISMOUNT_VOLUME. The first time I use it to dismount an NTFS volume on my Win2000 system (assuming the system has just recently been rebooted), I receive a user mode error code of 21, ERROR_NOT_READY, which is “The device is not ready.” This comes from the kernel mode error code of 0xC000026E, STATUS_VOLUME_DISMOUNTED, for status of the FSCTL_DISMOUNT_VOLUME IRP. After this fails once, all subsequent calls to FSCTL_DISMOUNT_VOLUME for that volume succeed. Why does this first call to FSCTL_DISMOUNT_VOLUME for each volume fail? The volume is mounted and accessible, but fails to dismount.

Here is what I have tried…
Upon booting my system, I run a simple utility I created, DismountVolume.exe (see code below), to dismount a volume. All this utility does is open a handle to a volume based on its volume GUID, locks the volume, dismounts the volume, unlocks the volume, then closes the handle to it. The first execution of this utility on a given volume fails, in spite of I/O to the file system on the volume prior to running it. Each subsequent execution completes successfully (provided there are no open file handles).

If anyone knows why the FSCTL_DISMOUNT_VOLUME fails the first time around, please let me know.

Kevin

//
// DismountTest.cpp
//

#include <windows.h>
#include <iostream.h>

#define SUCCESS 0
#define CANNOT_GET_VOLUME_HANDLE 1
#define VOLUME_LOCK_FAIL 2
#define DISMOUNT_VOLUME_FAIL 3
#define VOLUME_NAME_INVALID 4
#define VOLUME_NOT_FOUND 5

#define MaxNameStrSize 256

int DismountVolume( char* VolumeId );

int main(int argc, char* argv)
{
if( argc != 3 )
{
cerr << “\nError – invalid parameters\n\n”;
cerr << “Usage:\n”
" UnmountTest.exe \n"
“\n”
" Ex: UnmountTest.exe -l VolumeX\n"
“\n”
" Meaning: Unmount the volume with the lable "VolumeX"\n"
“\n”
" Flages: -i unmounts the volume with the unique volume id specified\n"
" volume id must be in the form \\.\Volume{12345678-1234-1234-1234-123456789ABC}\n"
" -d unmounts the volume with the drive letter specified\n"
" driver letter must be a single letter between A and Z inclusive\n"
" -l unmounts the volume with the lable specified\n"
" volume lable must be at least one character long\n";

return -1;
}

char tempString[MaxNameStrSize];
strncpy( tempString, argv[2], 11 );
tempString[11] = ‘\0’;
if( strcmp( argv[1], “-i” ) == 0 &&
strlen( argv[2] ) == 48 &&
strcmp( tempString, “\\.\Volume{” ) == 0
)
{
// Make sure that if “-i” is specified as the flag that
// the volume id parameter is long enough and
// the volume id starts with “\.\Volume{”
}
else if( strcmp( argv[1], “-d” ) == 0 &&
strlen( argv[2] ) == 1 &&
strcmp( argv[2], “A” ) >= 0 &&
strcmp( argv[2], “Z” ) <= 0
)
{
// Make sure that if “-d” is specified as the flag that
// the volume drive letter is only one characte long
// and that it is between “A” and “Z” inclusive
}
else if( strcmp( argv[1], “-l” ) == 0 &&
strlen( argv[2] ) >= 1
)
{
// Make sure that if “-l” is specified as the flag that
// the volume name is at least one characte long
}
else
{
cerr << “\nError – invalid parameters\n\n”;
cerr << “Usage:\n”
" UnmountTest.exe \n"
“\n”
" Ex: UnmountTest.exe -l VolumeX\n"
“\n”
" Meaning: Unmount the volume with the lable "VolumeX"\n"
“\n”
" Flages: -i unmounts the volume with the unique volume id specified\n"
" volume id must be in the form \\.\Volume{12345678-1234-1234-1234-123456789ABC}\n"
" -d unmounts the volume with the drive letter specified\n"
" driver letter must be a single letter between A and Z inclusive\n"
" -l unmounts the volume with the lable specified\n"
" volume lable must be at least one character long\n";

return -1;
}

if( strcmp( argv[1], “-i” ) == 0 )
{
int returnValue = DismountVolume( argv[2] );

switch( returnValue )
{
case SUCCESS:
{
cout << “SUCCESS”;
cout << “\n”;
return returnValue;
break;
}
case CANNOT_GET_VOLUME_HANDLE:
{
cout << “CANNOT_GET_VOLUME_HANDLE”;
cout << “\n”;
return returnValue;
break;
}
case VOLUME_LOCK_FAIL:
{
cout << “VOLUME_LOCK_FAIL”;
cout << “\n”;
return returnValue;
break;
}
case DISMOUNT_VOLUME_FAIL:
{
cout << “DISMOUNT_VOLUME_FAIL”;
cout << “\n”;
return returnValue;
break;
}
case VOLUME_NAME_INVALID:
{
cout << “VOLUME_NAME_INVALID”;
cout << “\n”;
return returnValue;
break;
}
case VOLUME_NOT_FOUND:
{
cout << “VOLUME_NOT_FOUND”;
cout << “\n”;
return returnValue;
break;
}
default:
cout << “Undefined Error”;
cout << “\n”;
return -1;
break;
}
}
else
{
cout << “Sorry, the -i flag is the only method implemented right now.\n”;
return -1;
}
}

int DismountVolume( char* VolumeId )
{
if( strlen( VolumeId ) == 0 )
{
return( VOLUME_NAME_INVALID );
}

HANDLE volumeHandle; // handle for the volume scan
BOOL lockFlag = FALSE; // lock volume results flag
BOOL dismountFlag = FALSE; // dismount volume results flag
BOOL unlockFlag = FALSE; // unlock volume results flag
DWORD returnedByteCount;
DWORD testValue;
char tempBuffer[MaxNameStrSize];

strcpy( tempBuffer, VolumeId );

volumeHandle = CreateFile( tempBuffer,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL
);

if( volumeHandle == INVALID_HANDLE_VALUE )
{
testValue = GetLastError( );
cout << "Error: " << testValue << “\n”;
cout << “\n”;

return( CANNOT_GET_VOLUME_HANDLE );
}

lockFlag = DeviceIoControl( volumeHandle,
FSCTL_LOCK_VOLUME,
NULL,
0,
NULL,
0,
&returnedByteCount,
NULL
);
if( lockFlag == FALSE )
{
testValue = GetLastError( );
cout << "Error: " << testValue << “\n”;
cout << “\n”;

CloseHandle( volumeHandle );

return( VOLUME_LOCK_FAIL );
}

dismountFlag = DeviceIoControl( volumeHandle,
FSCTL_DISMOUNT_VOLUME,
NULL,
0,
NULL,
0,
&returnedByteCount,
NULL
);
if( dismountFlag == FALSE )
{
testValue = GetLastError( );
cout << "Error: " << testValue << “\n”;
cout << “\n”;

unlockFlag = DeviceIoControl( volumeHandle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &returnedByteCount, NULL );
CloseHandle( volumeHandle );
return( DISMOUNT_VOLUME_FAIL );
}

unlockFlag = DeviceIoControl( volumeHandle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &returnedByteCount, NULL );

CloseHandle( volumeHandle );
return( SUCCESS );
}

You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com</iostream.h></windows.h>

Interesting…

  • You are opening the volume with read and write attributes. If I am
    correct, the fact that you are doing this will force the volume to mount
    if it is not already mounted. You should open the volume for attribute
    access to prevent this mount.

  • Is it possible that the first time around, soon after boot, you are
    causing a mount to occur on your IO path and that the mount either has
    not completed yet and/or is synchronized with the close after the first
    create that triggers a mount?

Just some thoughts…

Jamey

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Kevin Goodwin
Sent: Wednesday, January 09, 2002 5:22 PM
To: File Systems Developers
Subject: [ntfsd] FSCTL_DISMOUNT_VOLUME fails on first execution
(Win2K/NTFS)

I am perplexed by the behavior of FSCTL_DISMOUNT_VOLUME. The first time
I use it to dismount an NTFS volume on my Win2000 system (assuming the
system has just recently been rebooted), I receive a user mode error
code of 21, ERROR_NOT_READY, which is “The device is not ready.” This
comes from the kernel mode error code of 0xC000026E,
STATUS_VOLUME_DISMOUNTED, for status of the FSCTL_DISMOUNT_VOLUME IRP.
After this fails once, all subsequent calls to FSCTL_DISMOUNT_VOLUME for
that volume succeed. Why does this first call to FSCTL_DISMOUNT_VOLUME
for each volume fail? The volume is mounted and accessible, but fails
to dismount.

Here is what I have tried…
Upon booting my system, I run a simple utility I created,
DismountVolume.exe (see code below), to dismount a volume. All this
utility does is open a handle to a volume based on its volume GUID,
locks the volume, dismounts the volume, unlocks the volume, then closes
the handle to it. The first execution of this utility on a given volume
fails, in spite of I/O to the file system on the volume prior to running
it. Each subsequent execution completes successfully (provided there
are no open file handles).

If anyone knows why the FSCTL_DISMOUNT_VOLUME fails the first time
around, please let me know.

Kevin

//
// DismountTest.cpp
//

#include <windows.h>
#include <iostream.h>

#define SUCCESS 0
#define CANNOT_GET_VOLUME_HANDLE 1
#define VOLUME_LOCK_FAIL 2
#define DISMOUNT_VOLUME_FAIL 3
#define VOLUME_NAME_INVALID 4
#define VOLUME_NOT_FOUND 5

#define MaxNameStrSize 256

int DismountVolume( char* VolumeId );

int main(int argc, char* argv)
{
if( argc != 3 )
{
cerr << “\nError – invalid parameters\n\n”;
cerr << “Usage:\n”
" UnmountTest.exe \n"
“\n”
" Ex: UnmountTest.exe -l VolumeX\n"
“\n”
" Meaning: Unmount the volume with the
lable "VolumeX"\n"
“\n”
" Flages: -i unmounts the volume
with the unique volume id specified\n"
" volume id must be in
the form \\.\Volume{12345678-1234-1234-1234-123456789ABC}\n"
" -d unmounts the volume
with the drive letter specified\n"
" driver letter must be a
single letter between A and Z inclusive\n"
" -l unmounts the volume
with the lable specified\n"
" volume lable must be at
least one character long\n";

return -1;
}

char tempString[MaxNameStrSize];
strncpy( tempString, argv[2], 11 );
tempString[11] = ‘\0’;
if( strcmp( argv[1], “-i” ) == 0 &&
strlen( argv[2] ) == 48 &&
strcmp( tempString, “\\.\Volume{” ) == 0
)
{
// Make sure that if “-i” is specified as the flag that
// the volume id parameter is long enough and
// the volume id starts with “\.\Volume{”
}
else if( strcmp( argv[1], “-d” ) == 0 &&
strlen( argv[2] ) == 1 &&
strcmp( argv[2], “A” ) >= 0 &&
strcmp( argv[2], “Z” ) <= 0
)
{
// Make sure that if “-d” is specified as the flag that
// the volume drive letter is only one characte long
// and that it is between “A” and “Z” inclusive
}
else if( strcmp( argv[1], “-l” ) == 0 &&
strlen( argv[2] ) >= 1
)
{
// Make sure that if “-l” is specified as the flag that
// the volume name is at least one characte long
}
else
{
cerr << “\nError – invalid parameters\n\n”;
cerr << “Usage:\n”
" UnmountTest.exe \n"
“\n”
" Ex: UnmountTest.exe -l VolumeX\n"
“\n”
" Meaning: Unmount the volume with the
lable "VolumeX"\n"
“\n”
" Flages: -i unmounts the volume
with the unique volume id specified\n"
" volume id must be in
the form \\.\Volume{12345678-1234-1234-1234-123456789ABC}\n"
" -d unmounts the volume
with the drive letter specified\n"
" driver letter must be a
single letter between A and Z inclusive\n"
" -l unmounts the volume
with the lable specified\n"
" volume lable must be at
least one character long\n";

return -1;
}

if( strcmp( argv[1], “-i” ) == 0 )
{
int returnValue = DismountVolume( argv[2] );

switch( returnValue )
{
case SUCCESS:
{
cout << “SUCCESS”;
cout << “\n”;
return returnValue;
break;
}
case CANNOT_GET_VOLUME_HANDLE:
{
cout << “CANNOT_GET_VOLUME_HANDLE”;
cout << “\n”;
return returnValue;
break;
}
case VOLUME_LOCK_FAIL:
{
cout << “VOLUME_LOCK_FAIL”;
cout << “\n”;
return returnValue;
break;
}
case DISMOUNT_VOLUME_FAIL:
{
cout << “DISMOUNT_VOLUME_FAIL”;
cout << “\n”;
return returnValue;
break;
}
case VOLUME_NAME_INVALID:
{
cout << “VOLUME_NAME_INVALID”;
cout << “\n”;
return returnValue;
break;
}
case VOLUME_NOT_FOUND:
{
cout << “VOLUME_NOT_FOUND”;
cout << “\n”;
return returnValue;
break;
}
default:
cout << “Undefined Error”;
cout << “\n”;
return -1;
break;
}
}
else
{
cout << “Sorry, the -i flag is the only method
implemented right now.\n”;
return -1;
}
}

int DismountVolume( char* VolumeId )
{
if( strlen( VolumeId ) == 0 )
{
return( VOLUME_NAME_INVALID );
}

HANDLE volumeHandle; // handle for the volume
scan
BOOL lockFlag = FALSE; // lock volume
results flag
BOOL dismountFlag = FALSE; // dismount volume
results flag
BOOL unlockFlag = FALSE; // unlock volume results
flag
DWORD returnedByteCount;
DWORD testValue;
char tempBuffer[MaxNameStrSize];

strcpy( tempBuffer, VolumeId );

volumeHandle = CreateFile( tempBuffer,
GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL
);

if( volumeHandle == INVALID_HANDLE_VALUE )
{
testValue = GetLastError( );
cout << "Error: " << testValue << “\n”;
cout << “\n”;

return( CANNOT_GET_VOLUME_HANDLE );
}

lockFlag = DeviceIoControl( volumeHandle,

FSCTL_LOCK_VOLUME,
NULL,
0,
NULL,
0,

&returnedByteCount,
NULL
);
if( lockFlag == FALSE )
{
testValue = GetLastError( );
cout << "Error: " << testValue << “\n”;
cout << “\n”;

CloseHandle( volumeHandle );

return( VOLUME_LOCK_FAIL );
}

dismountFlag = DeviceIoControl( volumeHandle,

FSCTL_DISMOUNT_VOLUME,

NULL,

0,

NULL,

0,

&returnedByteCount,

NULL

);
if( dismountFlag == FALSE )
{
testValue = GetLastError( );
cout << "Error: " << testValue << “\n”;
cout << “\n”;

unlockFlag = DeviceIoControl( volumeHandle,
FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &returnedByteCount, NULL );
CloseHandle( volumeHandle );
return( DISMOUNT_VOLUME_FAIL );
}

unlockFlag = DeviceIoControl( volumeHandle, FSCTL_UNLOCK_VOLUME,
NULL, 0, NULL, 0, &returnedByteCount, NULL );

CloseHandle( volumeHandle );
return( SUCCESS );
}

You are currently subscribed to ntfsd as: xxxxx@storagecraft.com To
unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com</iostream.h></windows.h>