Hello all,
I’ve set up a \DosDevices namespace for a hardware device driver I’m writing. Before getting into the gory details, I’d mention that it actually works properly and cleanly when I run a plain test application. The thing is that I want users to be able to access the device driver from the DOS command prompt. Let’s try to remember that behind the massed of info below, that’s the problem I want solved, even though general insights are mostly welcome.
The platform is Windows 7 version 6.1.7600.
First, the setting: Following the classic convention, the driver sets up an UNNAMED device (with IoCreateDevice()'s DeviceName parameter set to NULL). The interface’s name is later acquired from IoRegisterDeviceInterface()'s SymbolicLinkName. The driver’s namespace is then created by virtue of several symbolic links under \DosDevices, each pointing at the interface name given by IoRegisterDeviceInterface() appended with some “\myname” string. So when the application creates a file with the symbolic link (say, \.\hello), the symbolic link redirects the object manager to something link {…Long blob…}\hello, which in turn redirects to the actual device (\Device\NTPNP_PCI0015). As I said, this works like a charm with a test application.
I’ll also mention that in the dispatch method for Create, the driver checks how the file was created (irpstack->Parameters.Create.SecurityContext->DesiredAccess), and goes something like
if ((DesiredAccess & (FILE_WRITE_DATA | FILE_READ_DATA)) == 0)
{ … complete IRP with STATUS_INVALID_DEVICE_REQUEST … }
In other words, it rejects attempts to create a file if it’s not for either reading or writing data. When I wrote this sanity check I thought it was silly, but what did I know…
So what happens in reality? If it’s a plain application calling
CreateFile(TEXT(“\\.\hello”),
GENERIC_READ, // open for reading
0, // do not share
NULL, // no security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
The logs show a single successful attempt to open the device with DesiredAccess = 0x120089 (matching GENERIC_READ), data is read properly and all is well.
But the thing is that I want access from the DOS prompt. Linux style. This is a real use scenario for this driver. I want users to be able to get a chunk of data without having a special application for that.
So this is what happens with a DOS prompt:
C:\Users\eli>type \.\hello
The filename, directory name, or volume label syntax is incorrect.
And indeed, no access was made to the device. Why “type” refuses to access device files is beyond me. So I tried something else:
C:\Users\eli>copy \.\hello delme
Incorrect function.
DesiredAccess was 0x0080 (FILE_CREATE_TREE_CONNECTION, I believe) so it failed.
How about redirection?
C:\Users\eli>type > \.\hello
Incorrect function.
This was actually correct behavior, because the device is read only. And now prepare for the really weird part:
C:\Users\eli>type < \.\hello
Incorrect function.
It’s weird, because the logs show that the driver returned SUCCESS on the Create, and that the file remained open. As a matter of fact, no IRPs were sent to the driver after the IRP_MJ_CREATE.
The DOS shell merely repeats the status from the last command it got: Other tests show that if exactly the same command is given on a fresh DOS window, the prompt is given immediately as if all was OK, but in fact the file was opened, not read from at all, and left open.
The thing about repeating the status from the last command is consistent.
If I try to repeat this command,
C:\Users\eli>type < \.\hello
The requested resource is in use.
This error came from the driver itself, which detected that the specific “file” is already open, so it failed with a STATUS_DEVICE_BUSY, which was passed on to user correctly by the DOS prompt.
So basically, the DOS prompt does weird things only when the driver is cooperative.
So what’s the conclusion? That the DOS prompt gets crazy when faced with one of Window’s basic internals? And suppose that a user has a bare system (no applications), is there any way he or she could grab data from the device, using only what comes with the system? The DOS prompt doesn’t look very promising.
And of course, the first thing I looked at was how the IRP is completed, in case I forgot to set IoStatus.Status or something. But it goes:
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
which is, as far as I know, the official way.
And for the sake of curiosity I also tried it with Cygwin. This works properly (file is opened, data is read):
$ cat \\.\hello
But surprisingly, there are two failed attempts on CreateFile before the successful one, having DesiredAccess set to 0x00020088 and 0x00020000 (both failing on the sanity check showed above, as they don’t require FILE_READ_DATA nor FILE_WRITE_DATA). It’s the third call to CreateFile, with access flags 0x00120089 (generic read) that does the work.
What is even more weird, is that AFTER the successful open (and a couple of failed QUERY_INFO requests, who cares), the device gets another CreateFile with a zero-length file name and DesiredAccess=0x100001 (I suppose FILE_RESERVE_OPFILTER | FILE_DIRECTORY_FILE). The thing is that all symbolic links the driver creates have this “\something” tail, so the last CreateFile didn’t go through one of these. According to IrpTracker it was cat.exe initiating this after it had finished reading and reached end of file. How did it get around the symbolic links, and why did it bother doing that after the data had been read?
The Cygwin issue is of less importance, of course, since things work really well with it. These extra file accesses could be a Cygwin implementation issue.
And for those who got reading this far, thanks for showing interest.