STATUS_OBJECT_NAME_INVALID in Virtual Storport Miniport Driver

I am writing a virtual storport miniport driver to mount a Windows software raid as a virtual disk so that it will be available when write blocked. I have modified the storport miniport driver example from the NT Insider at http://www.osronline.com/article.cfm?article=538 but I am having an error that I cannot solve in the OsrVmUserUtil.cpp file and the NTSTATUS CreateConnection function. When I try to get a handle to the disk I am getting the STATUS_OBJECT_NAME_INVALID error and I cannot figure it out. I am passing in \.\PhysicalDrive1 which I know is a good path as my application reads it to get all of the raid information before passing that info onto the driver. I have a few years experience with C++ but this is my first driver as I am writing it for my Masters thesis.

Anyway here is the CreateConnection function code:

///////////////////////////////////////////////////////////////////////////////
//
// CreateConnection
//
// Creates a connection to the specified volume, if it does not already
// exists.
//
// INPUTS:
//
// PGInfo - Pointer to the Global Information BLock.
//
// PConnectInfo - Pointer to the connection information to create
//
// OUTPUTS:
//
// None.
//
// RETURNS:
//
// STATUS_SUCCESS if okay, an error otherwise.
//
// IRQL:
//
// This routine is called at any IRQL PASSIVE_LEVEL.
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS CreateConnection(PUSER_GLOBAL_INFORMATION PGInfo, PCONNECT_IN PConnectInfo)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK ioStatus;
BOOLEAN bInserted = FALSE;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING uString;
KIRQL oldIrql;
GUID tmpGuid;
ULONG bytesReturned;

OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRVMINIPT_DEBUG_FUNCTRACE,(FUNCTION": Enter\n"));

//
// See if we already have a connection that matches this.
//
if(FindConnectionMatch(PGInfo,PConnectInfo,NULL)) {
return STATUS_OBJECT_NAME_COLLISION;
}

RtlZeroMemory(&tmpGuid,sizeof(GUID));

//
// Add the connection to the list.
//
PCONNECTION_LIST_ENTRY pEntry = (PCONNECTION_LIST_ENTRY)
ExAllocatePoolWithTag(NonPagedPool,sizeof(CONNECTION_LIST_ENTRY),‘pCLE’);

if(!pEntry) {
return STATUS_INSUFFICIENT_RESOURCES;
}

RtlZeroMemory(pEntry,sizeof(CONNECTION_LIST_ENTRY));

RtlCopyMemory(&pEntry->ConnectionInfo,PConnectInfo,sizeof(CONNECT_IN));

OsrAcquireSpinLock(&PGInfo->ConnectionListLock,&oldIrql);

InsertTailList(&PGInfo->ConnectionList,&pEntry->ListEntry);

OsrReleaseSpinLock(&PGInfo->ConnectionListLock,oldIrql);
bInserted = TRUE;

pEntry->DiskSize = (ULONG) ((ULONG) PConnectInfo->Disk0SizeMB + (ULONG) PConnectInfo->Disk1SizeMB + (ULONG) PConnectInfo->Disk2SizeMB + (ULONG) PConnectInfo->Disk3SizeMB + (ULONG) PConnectInfo->Disk4SizeMB + (ULONG) PConnectInfo->Disk5SizeMB) * 1024 * 1024;

OBJECT_ATTRIBUTES Disk0, Disk1, Disk2, Disk3, Disk4, Disk5;
IO_STATUS_BLOCK Disk0Comp, Disk1Comp, Disk2Comp, Disk3Comp, Disk4Comp, Disk5Comp;
UNICODE_STRING Disk0Path, Disk1Path, Disk2Path, Disk3Path, Disk4Path, Disk5Path;
RtlInitUnicodeString(&Disk0Path, PConnectInfo->Disk0Path);
//CODE CUT FOR EXAMPLE
InitializeObjectAttributes(&Disk0, &Disk0Path, OBJ_OPENIF, NULL, NULL);
//CODE CUT FOR EXAMPLE
if(PConnectInfo->Disk0SizeMB != 0)
{
status = ZwCreateFile(
&(pEntry->Disk0BaseAddress), //File HANDLE
FILE_READ_DATA, //Desired Access
&Disk0, //Object Attributes
&Disk0Comp, //IO_STATUS_BLOCK
NULL, //Allocation Size
FILE_ATTRIBUTE_NORMAL, //File Attributes
FILE_SHARE_READ, //Share Access
FILE_OPEN, //Create Disposition
FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS, //Create Options
NULL, //EABuffer
0 //EALength
);
if(!NT_SUCCESS(status)) {
switch(status){
case STATUS_OBJECT_NAME_INVALID:
default:
goto cleanupAfterError;
}
}
else {
switch(Disk0Comp.Status){
case FILE_CREATED:
goto cleanupAfterError;
case FILE_OPENED:
break;
case FILE_OVERWRITTEN:
goto cleanupAfterError;
case FILE_SUPERSEDED:
goto cleanupAfterError;
case FILE_EXISTS:
goto cleanupAfterError;
case FILE_DOES_NOT_EXIST:
goto cleanupAfterError;
default:
goto cleanupAfterError;
}
}
}
//CODE CUT FOR EXAMPLE
//
// For our Virtual Disks, it comes from the Disk Header.
//
status = ExUuidCreate(&tmpGuid);

if(!NT_SUCCESS(status)) {
goto cleanupAfterError;
}

//
// We now have the information about the file that this disk, which we are about to
// create, represents. We need to build some SCSI inquiry information about the
// disk, so that the Disk Class Driver knows about us.
//
#pragma prefast(suppress:28197,“This memory is not leaked”)
PINQUIRYDATA pInquiryData = (PINQUIRYDATA) ExAllocatePoolWithTag(NonPagedPool,
sizeof(INQUIRYDATA),
‘diSO’);

if(pInquiryData) {

// typedef struct _INQUIRYDATA {
// UCHAR DeviceType : 5;
// UCHAR DeviceTypeQualifier : 3;
// UCHAR DeviceTypeModifier : 7;
// UCHAR RemovableMedia : 1;
// UCHAR Versions;
// UCHAR ResponseDataFormat : 4;
// UCHAR HiSupport : 1;
// UCHAR NormACA : 1;
// UCHAR ReservedBit : 1;
// UCHAR AERC : 1;
// UCHAR AdditionalLength;
// UCHAR Reserved[2];
// UCHAR SoftReset : 1;
// UCHAR CommandQueue : 1;
// UCHAR Reserved2 : 1;
// UCHAR LinkedCommands : 1;
// UCHAR Synchronous : 1;
// UCHAR Wide16Bit : 1;
// UCHAR Wide32Bit : 1;
// UCHAR RelativeAddressing : 1;
// UCHAR VendorId[8];
// UCHAR ProductId[16];
// UCHAR ProductRevisionLevel[4];
// UCHAR VendorSpecific[20];
// UCHAR Reserved3[40];
// } INQUIRYDATA, *PINQUIRYDATA;

RtlZeroMemory(pInquiryData,sizeof(INQUIRYDATA));

//
// The media is now either an OSR Disk or a regular disk, either way
// we return the same information.
//
pInquiryData->DeviceType = DIRECT_ACCESS_DEVICE;
pInquiryData->DeviceTypeQualifier = DEVICE_CONNECTED;
pInquiryData->DeviceTypeModifier = 0;
pInquiryData->RemovableMedia = TRUE;
pInquiryData->Versions = 2; // SCSI-2 support
pInquiryData->ResponseDataFormat = 2; // Same as Version?? according to SCSI book
pInquiryData->Wide32Bit = TRUE; // 32 bit wide transfers
pInquiryData->Synchronous = TRUE; // Synchronous commands
pInquiryData->CommandQueue = FALSE; // Does not support tagged commands
pInquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE-5; // Amount of data we are returning
pInquiryData->LinkedCommands = FALSE; // No Linked Commands
RtlCopyMemory((PUCHAR) &pInquiryData->VendorId[0],OSR_INQUIRY_VENDOR_ID,
strlen(OSR_INQUIRY_VENDOR_ID));
RtlCopyMemory((PUCHAR) &pInquiryData->ProductId[0],OSR_INQUIRY_PRODUCT_ID,
strlen(OSR_INQUIRY_PRODUCT_ID));
RtlCopyMemory((PUCHAR) &pInquiryData->ProductRevisionLevel[0],OSR_INQUIRY_PRODUCT_REVISION,
strlen(OSR_INQUIRY_PRODUCT_REVISION));
RtlCopyMemory((PUCHAR) &pInquiryData->VendorSpecific[0],OSR_INQUIRY_VENDOR_SPECIFIC,
strlen(OSR_INQUIRY_VENDOR_SPECIFIC));

ULONG bitNumber = RtlFindClearBitsAndSet(&ScsiBitMapHeader,1,0);

if(bitNumber == 0xFFFFFFFF) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoClose(PGInfo,pEntry);
goto cleanupAfterError;
}

ULONG targetId = bitNumber % SCSI_MAXIMUM_TARGETS_PER_BUS;
ULONG BusId = bitNumber / SCSI_MAXIMUM_BUSES;

#pragma prefast(suppress:28197,“This memory is not leaked”)
PUSER_INSTANCE_INFORMATION pLocalInfo = (PUSER_INSTANCE_INFORMATION)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(USER_INSTANCE_INFORMATION),
‘DLUp’);

if(!pLocalInfo) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoClose(PGInfo,pEntry);
goto cleanupAfterError;
}

RtlZeroMemory(pLocalInfo,sizeof(USER_INSTANCE_INFORMATION));
pLocalInfo->MagicNumber = USER_INSTANCE_INFORMATION_MAGIC_NUMBER;
pLocalInfo->PInquiryData = pInquiryData;

//
// Create a PDO for this new disk.
//

pLocalInfo->OsrSPLocalHandle = OsrSPCreateScsiDevice(PGInfo->OsrSPHandle,
BusId /*IN ULONG BusIndex*/,
targetId /*IN ULONG TargetIndex*/,
LunId /*IN ULONG LunIndex*/,
pLocalInfo, /* Our local Data for Device */
FALSE,
pInquiryData,
1);

//
// Okay, we’ve got a PDO, we can now invalidate relations and see what happens.
//

if(pLocalInfo) {

static ULONG indexNumber = 0x08051958;

pLocalInfo->PGInfo = PGInfo;

//
// Get the infor for the unique ID.
//
GUID* pUniqueId = &tmpGuid;

RtlCopyMemory(&pLocalInfo->UniqueID.UniqueID,pUniqueId,sizeof(GUID));
pLocalInfo->UniqueID.FileId = (ULONGLONG) InterlockedIncrement((volatile LONG*) &indexNumber);

//
// Store away some other useful information.
//
pLocalInfo->ConnectionInformation = pEntry;
pLocalInfo->TargetIndex = targetId;
pLocalInfo->BusIndex = BusId;
pLocalInfo->LunIndex = LunId;
if(STATUS_SUCCESS != RtlStringCbPrintfA(&pLocalInfo->AsciiSignature[0],
sizeof(pLocalInfo->AsciiSignature),
“%08x%04x%04x%2x%2x%02x%02x%02x%02x%02x%02x%0I64x”,
pUniqueId->Data1,pUniqueId->Data2,pUniqueId->Data3,
pUniqueId->Data4[0],pUniqueId->Data4[1],pUniqueId->Data4[2],pUniqueId->Data4[3],
pUniqueId->Data4[5],pUniqueId->Data4[5],pUniqueId->Data4[6],pUniqueId->Data4[7],
pLocalInfo->UniqueID.FileId)) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoClose(PGInfo,pEntry);
goto cleanupAfterError;
}

pEntry->PIInfo = pLocalInfo;
pEntry->BusIndex = BusId;
pEntry->TargetIndex = targetId;
pEntry->LunIndex = LunId;

InterlockedIncrement(&PGInfo->ConnectionCount);

targetId++;

//
// Tell the OSR SP that our bus has changed.
//
OsrSPAnnounceArrival(PGInfo->OsrSPHandle);

pEntry->Connected = TRUE;

status = STATUS_SUCCESS;

}

OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRVMINIPT_DEBUG_FUNCTRACE,(FUNCTION": Exit\n"));

return status;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}

cleanupAfterError:

if(bInserted) {

DeleteConnectionEntry(PGInfo,pEntry,PConnectInfo);

}

if(pEntry) {

ExFreePool(pEntry);

}

OsrTracePrint(TRACE_LEVEL_ERROR,OSRVMINIPT_DEBUG_FUNCTRACE,(FUNCTION": Exit\n"));

return status;
}

Can nobody help with this error? My thesis is currently at a standstill trying to figure out this problem. In case I posted too much code above here is the specific call that is giving me the error:

status = ZwCreateFile( &(pEntry->Disk0BaseAddress), //File HANDLE FILE_READ_DATA, //Desired Access &Disk0, //Object Attributes &Disk0Comp, //IO_STATUS_BLOCK NULL, //Allocation Size FILE_ATTRIBUTE_NORMAL, //File Attributes FILE_SHARE_READ, //Share Access FILE_OPEN, //Create Disposition FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS, //Create Options NULL, //EABuffer 0 //EALength ); if(!NT_SUCCESS(status)) { switch(status){ case STATUS_OBJECT_NAME_INVALID: default: goto cleanupAfterError; } } else { switch(Disk0Comp.Status){ case FILE_CREATED: goto cleanupAfterError; case FILE_OPENED: break; case FILE_OVERWRITTEN: goto cleanupAfterError; case FILE_SUPERSEDED: goto cleanupAfterError; case FILE_EXISTS: goto cleanupAfterError; case FILE_DOES_NOT_EXIST: goto cleanupAfterError; default: goto cleanupAfterError; } } }

dducharme@my.uri.edu wrote:

Can nobody help with this error? My thesis is currently at a standstill trying to figure out this problem. In case I posted too much code above here is the specific call that is giving me the error:

Yes, but it’s the parameters to the call that are causing the trouble.

STATUS_OBJECT_NAME_INVALID error and I cannot figure it out. I am passing in \.\PhysicalDrive1 which I know is a good path as my application reads it to get all of the raid information before passing that info onto the driver.

\.\PhysicalDrive1 is a good path FROM USER MODE. The kernel and user
object namespaces are quite different. The \. prefix is a user-mode
invention that points to ?? in the kernel namespace. In the kernel,
Global??\PhysicalDrive1 points to something like
\Device\Harddisk0\Partition1.

What are you actually trying to do here? Accessing a disk volume
directly like this doesn’t seem like right answer for very many questions.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

>Can nobody help with this error? My thesis is currently at a standstill trying to figure out this >problem.

Could you see a volume or a disk in “Disk Management Panel”?

I am passing in \.\PhysicalDrive1 which I know is a good path as my application reads it to get >all of the raid information before passing that info onto the driver.

Why do you know that your disk have such name?
If you could see at least a disk in “Disk Management Panel” I would suggest to you use either VDS (prefered) or Volume Management Functions to locate a disk/volume.

Igor Sharovar

> \.\PhysicalDrive1 is a good path FROM USER MODE. The kernel and user
object namespaces are quite different. The \. prefix is a user-mode
invention that points to ?? in the kernel namespace. In the kernel,
Global??\PhysicalDrive1 points to something like
\Device\Harddisk0\Partition1.

The driver is being designed to mount a Windows Software Raid as a virtual drive when Windows will not such as when the disk is write blocked and for that reason VDS will not work. Since this is in a drive should I change \.\PhysicalDrive1 to ??\PhysicalDrive1 or would it be \Device\Harddisk1?

dducharme@my.uri.edu wrote:

The driver is being designed to mount a Windows Software Raid as a virtual drive when Windows will not such as when the disk is write blocked and for that reason VDS will not work. Since this is in a drive should I change \.\PhysicalDrive1 to ??\PhysicalDrive1 or would it be \Device\Harddisk1?

Since disks are not my thing, I’m hoping someone else with real-world
experience will pipe up.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

>The driver is being designed to mount a Windows Software Raid as a virtual drive when Windows >will not such as when the disk is write blocked and for that reason VDS will not work.

Could you elaborate this sentence? It is not clear what you mean.

Igor Sharovar

Ignoring the underlying architectural question (which is “I still don’t understand what you’re doing or how you could a-priori know the volume name”), and restricting my answer to just what’s quoted above… You could always download the GUI Object Viewer (http://www.osronline.com/article.cfm?article=42) and see what the device you’re trying to open corresponds to.

You COULD simply substitute \Global??\XXXX for \.\XXXX – that should allow you to access the user-mode name. There are edge-conditions, but let’s not worry about those. Note that there is no definitive mapping from a name such as “\.\PhysicalDrive1” to “\Device\HardDisk1\DR1” (or whatever)

Peter
OSR

> Could you elaborate this sentence? It is not clear what you mean.

This driver is being designed for the field of Digital Forensics. In order to ensure that no data has been changed the disk must be fully write blocked so that even the OS cannot write to it. The problem is that when Windows discovers that even it cannot write to it, it breaks the software raid and makes it inaccessible to read. The driver I am writing is to mount the Software Raid even when Windows will not mount it so that any program that runs in Windows, including explorer, can be utilized.

I find out in my user mode application that the disks are at \.\PhysicalDrive1 for instance and that is what is passed into the driver. Is there a function that I should be calling in my user mode application in order to get the naming for the driver because right now I am just passing in the name I was using to access the drive with CreateFile in the application.

Interesting. I think that for U.S.A. courts this might not work. Maybe if
combined with a certified hardware write protect device, it could be
acceptable to the courts. That means your driver will need to store changed
sectors in a place not on any of the disks in question for each boot
session. Might be some fun software to write.

wrote in message news:xxxxx@ntdev…

Could you elaborate this sentence? It is not clear what you mean.

This driver is being designed for the field of Digital Forensics. In order
to ensure that no data has been changed the disk must be fully write blocked
so that even the OS cannot write to it. The problem is that when Windows
discovers that even it cannot write to it, it breaks the software raid and
makes it inaccessible to read. The driver I am writing is to mount the
Software Raid even when Windows will not mount it so that any program that
runs in Windows, including explorer, can be utilized.

I find out in my user mode application that the disks are at
\.\PhysicalDrive1 for instance and that is what is passed into the driver.
Is there a function that I should be calling in my user mode application in
order to get the naming for the driver because right now I am just passing
in the name I was using to access the drive with CreateFile in the
application.

I suggest instead you copy the drives images to an iSCSI disk array, and mount those images as iSCSI targets; then you can connect to them from another host and build a soft RAID from them. The original disks should not be messed around with.

This software is not designed as the write blocker but is instead used with a certified write blocker such as a hardware write blocker or SAFE Block a certified software write blocker designed at URI. The problem right now is when you attach a software raid with any write blocker Windows will not mount it since it cannot write to it. All my driver is doing is allowing you to mount the RAID without needing to copy all of the data off the original disks and run a raid rebuilder to obtain access to it. With hard drive sizes growing so fast it is inconvenient to have to copy a several terabyte RAID just to be able to look through it and find out if there is even anything illegal on it.

Now back to the question at hand:
I find out in my user mode application that the disks are at \.\PhysicalDrive1 and \.\PhysicalDrive2
for instance and that is what is passed into the driver. Is there a function
that I should be calling in my user mode application in order to get the naming
for the driver because right now I am just passing in the name I was using to
access the drive with CreateFile in the application or is it safe to just assume that these are \Global??\PhysicalDrive1 and \Global??\PhysicalDrive2. Thanks for all the responses so far.

About the only way you can ensure that the storage stack and file system
will never ever contaminate the disk is to provide a native device driver
that completely isolates the controller and the media from the OS. Serious
forensic or diagnostics are about the only apps that justify writing native
drivers for drive HBAs. If you never implement the ATA/SCSI write commands,
then the disk media will never be written. The OS won’t write to it because
the OS knows squat about writing to such media since the controller and
media are NOT in it’s storage stack or filesystem, and since you don’t
support write functionality in your native driver, then nothing can write to
the drive. But, using the protocol input functions you can get anything you
want off the media, even diagnostic information drive manufacturers write in
hidden potions of the disk.

A lot of work? You betch’m Kimosabe. For a single developer targeting your
first controller I’d estimate 3 to 6 man-months, with a new controller
available every 2 or 3 months after that. Your experience in the kernel and
with low level storage protocols can reduce that, but that’s a good
ballpark. Of course, if you or your institution has a good working liaison
with a drive OEM, ask’m about their internal diagnostic software. I can
guarantee they have exactly what you are needing.

Gary G. Little
H (952) 223-1349
C (952) 454-4629
xxxxx@comcast.net

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of dducharme@my.uri.edu
Sent: Wednesday, November 10, 2010 9:13 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] STATUS_OBJECT_NAME_INVALID in Virtual Storport Miniport
Driver

Could you elaborate this sentence? It is not clear what you mean.

This driver is being designed for the field of Digital Forensics. In order
to ensure that no data has been changed the disk must be fully write blocked
so that even the OS cannot write to it. The problem is that when Windows
discovers that even it cannot write to it, it breaks the software raid and
makes it inaccessible to read. The driver I am writing is to mount the
Software Raid even when Windows will not mount it so that any program that
runs in Windows, including explorer, can be utilized.

I find out in my user mode application that the disks are at
\.\PhysicalDrive1 for instance and that is what is passed into the driver.
Is there a function that I should be calling in my user mode application in
order to get the naming for the driver because right now I am just passing
in the name I was using to access the drive with CreateFile in the
application.


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

__________ Information from ESET Smart Security, version of virus signature
database 5609 (20101111) __________

The message was checked by ESET Smart Security.

http://www.eset.com

Ok I think people are getting confused, I am not writing a write blocker, my driver is being run AFTER a write blocker has already been utilized thus isolating the disk and causing Windows to break the software raid. The write blocker I will be using in my testing is a software write blocker called SAFE Block designed at URI and marketed through ForensicSOFT Inc which has been certified to work across hard drives. All my driver is doing is allowing a user to interact with the software raid as though it was still mounted so that they can browse and use any of their tools without having to rely on a tool such as Encase.

That being said I have already parsed out the raids control information from the raw disk and I pass the relevent information into my driver but I can’t test if that works yet because I have been unable to get a read handle on the raw disk with ZwCreateFile. Now my question right now is Is there a function that I should be calling in my user mode application in order to get the naming
for the driver because right now I am just passing in the name I was using to access the drive with CreateFile in the application or is it safe to just assume that these are \Global??\PhysicalDrive1 and \Global??\PhysicalDrive2. Peter Viscarola above mentioned that in most cases I can probably just replace \.\ with \Global??\ but that there are edge-conditions. What are these edge conditions and how prevelent are they?