OSRLogo
OSRLogoOSRLogoOSRLogo x Subscribe to The NT Insider
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Wed, 13 Dec 2017     115635 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
Downloads
ListServer / Forum
Driver Jobs
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

Stand Up and be Recognized - File System Recognizers

 Click Here to Download: Code Associated With This Article Zip Archive, 5KB

A regular question asked in our file systems class is about the need to implement a "file system recognizer." Indeed, we’ve found that there is considerable confusion about the very purpose of a file system recognizer. Thus, in this article we will explain what a file system recognizer actually is, why you might write one, and how file system recognizers actually work. We will wrap up with a simple example of a file system recognizer you can use as the basis of your own recognizer.

First, a file system recognizer is a standard Windows NT kernel mode device driver which serves a single simple task – it looks at physical media devices and if it recognizes the media format it loads the full file system driver. Why not just load the entire file system? It turns out that the "footprint" of a typical file system driver is actually rather significant. Since the full file system driver might never be required on the system, using a little driver can save several hundred K of precious system memory. Indeed, the standard Windows NT physical media file systems all use a file system recognizer. For example, on a system where the CD-ROM is never accessed, the CDFS file system need never be loaded – saving that memory for more important operations.

How does a file system recognizer determine that its file system is present on a disk? Typically, this is done by examining a signature on the disk. This signature might include information within the partition table, a unique value located at a particular byte offset from the beginning of the partition, the serial number or whatever other clever mechanism the developer might have defined. Naturally, signatures must be sufficiently different between file systems so there is no possibility of a drive being incorrectly claimed by the wrong file system driver.

Some examples of disk signatures follow:

File System Name

File System Signature

HFS

0x4244

NTFS

‘NTFS ‘

FAT

0xe9 or 0xeb or 0x49

When the File System Driver (FSD) is loaded it is responsible for analyzing the disk and determining if the media contains a recognized file system. If it does, the FSD will then mount the drive. An FSR also analyzes the media to determine if it contains a recognized file system. However, instead of mounting the drive, the FSR loads the FSD. Once the FSD is loaded, the FSR can unload since its task is done.

Mount Processing

 

An FSR is tied up with the way Windows NT mounts volumes. Since the mount process defines how the FSR must operate it is worth reviewing how mounting works on Windows NT.

A volume on Windows NT is mounted when it is accessed. Some volumes (such as the fixed disks on the system) are accessed during system initialization. Newly created volumes created with Disk Administrator or removable media volumes would be examples of disk volumes that are mounted much later. Thus, when you create a new disk partition and assign it a new drive letter, until an application program accesses that volume, it won’t actually be mounted. Similarly, when you change a floppy, the new floppy is not mounted until an application accesses it.

A Win32 application accesses the volume by using the drive letter typically. Of course, by now everyone knows these are actually symbolic links in the Object Manager name space – you can view these using the winobj utility in the SDK. Drive letters for physical volumes point to the device object for the physical media, not the file system.

When the I/O Manager finds a device object for a physical media device (that is a device with the type FILE_DEVICE_DISK, FILE_DEVICE_TAPE, FILE_DEVICE_CD_ROM, or FILE_DEVICE_VIRTUAL_DISK) that device object will have a Volume Parameter Block (VPB) which will indicate if the volume has been mounted. If it has been mounted, the VPB will point to the device object belonging to the file system. If it has not been mounted, the I/O Manager will attempt to mount it.

Mounting consists of the I/O Manager calling each registered file system of the correct type (FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_TAPE_FILE_SYSTEM, or FILE_DEVICE_CD_ROM_FILE_SYSTEM) for the media volume. This is done by calling the file system’s IRP_MJ_FILE_SYSTEM_CONTROL dispatch entry point with the minor function code IRP_MN_MOUNT_VOLUME. The I/O Manager asks each file system in turn if the volume can be mounted by that particular file system. File systems are called in last registered first called order, so the most recently loaded file system is the first one provided an opportunity to mount a volume.

The very first file system registered on Windows NT is the RAW file system – which actually registers three separate file system device objects – one for each type of media it might handle. Whenever the RAW file system is asked to mount a volume it always does so. Volumes belonging to RAW can only be opened for "whole volume" access and this is typically done by utilities such as Disk Administrator.

An FSR masquerades as a regular file system for the sole purpose of handling mount requests. Thus, it creates a device object of the appropriate file system type, registers it as a file system with the I/O Manager, and then waits to be called to mount volumes. When an FSR does recognize a volume as belonging to its file system, rather than accepting the mount request it returns the special error code STATUS_FS_DRIVER_REQUIRED. The I/O Manager then calls the FSR asking it to load the full file system driver. This is done by calling the IRP_MJ_FILE_SYSTEM_CONTROL dispatch entry point of the FSR with a minor function code of IRP_MN_LOAD_FILE_SYSTEM.

The principal advantage of using an FSR rather than simply loading the complete file system is that it saves memory – but only when the full FSD is not loaded. For customers who will only use the FSD on an infrequent basis, saving that additional memory can ensure the performance of their system is not impacted when the FSD itself is not in use. Of course, even an FSR consumes some memory, but it will still be much smaller than the memory consumed by a complete FSD.

Implementation

 

It turns out that the implementation of a file system recognizer is very straightforward. We have provided an example which you can use to build your own file system recognizer.

//
// (C) Copyright 1997 OSR Open Systems Resources, Inc.
// All Rights Reserved
file://
#include
file://
// Definitions which should be changed for your file system
file://
#define FSD_SERVICE_PATH L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\MyFsd"
#define FSD_RECOGNIZER_NAME L"\\FileSystem\\MyFsdRecognizer"
#define DEVICE_LOGICAL_BLOCKSIZE 512 // This is the size of your File Systems sectors on disk.
file://
// External, non-confidential but unpublished NT entry points
file://

NTSYSAPI
NTSTATUS
NTAPI
ZwLoadDriver(
IN PUNICODE_STRING DriverServiceName
);

NTKERNELAPI
VOID
IoRegisterFileSystem(
    IN OUT PDEVICE_OBJECT DeviceObject
    );

NTKERNELAPI
VOID
IoUnregisterFileSystem(
    IN OUT PDEVICE_OBJECT DeviceObject
    );

file://
// Global data
file://

static PDRIVER_OBJECT RecognizerDriverObject;
static PDEVICE_OBJECT RecognizerDeviceObject;

file://
// Forward reference
file://

static VOID Unload(PDRIVER_OBJECT);
static NTSTATUS RecognizerFsControl(PDEVICE_OBJECT, PIRP);
static NTSTATUS RecognizerDetectFileSystem(PIRP Irp);
static NTSTATUS RecognizerIoControl( IN PDEVICE_OBJECT deviceObject,
                                     IN ULONG IoctlCode,
                                     IN PVOID InputBuffer,
                                     IN ULONG InputBufferSize,
                                     OUT PVOID OutputBuffer,
                                     OUT ULONG OutputBufferSize);

static BOOLEAN RecognizerReadDiskSector( IN PDEVICE_OBJECT pDeviceObject,
                                         IN ULONG DiskSector,
                                         IN UCHAR* Buffer);

// must be DEVICE_LOGICAL_BLOCKSIZE bytes long.

Figure 1 Def’s and External Structures for FSR

Figure 1 details the various definitions and external functions that are needed in our implementation of the FSR. Of particular interest are IoRegisterFileSystem(…), IoUnregisterFileSystem(…), and ZwLoadDriver(…). IoRegisterFileSystem(…) is used by the FSR to register with the I/O Manager as a File System. This in turn means that the I/O Manager will call it during mount processing whenver a new volume is being mounted. Once the FSR has loaded the full file system, it uses IoUnregisterFileSystem(…) to tell the I/O Manager it no longer wishes to be called during mount processing. The actual loading of the FSD is done using the ZwLoadDriver(…) call. None of these calls are included in the DDK and ZwLoadDriver(…) is not included in the current release of the IFS Kit.

//
// DriverEntry
file://
// This is the entry point for the driver.
file://
// Inputs:
// DriverObject - the driver object for this driver
// RegistryPath - the path to this driver's key
file://
// Outputs:
// None.
file://
// Returns:
// success
file://
// Notes:
// This is a trivial driver, with a trivial entry point.
file://
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    NTSTATUS code;
    UNICODE_STRING driverName;

// Save away the driver object pointer

    RecognizerDriverObject = DriverObject;

// Device object was created. Set up dispatch entry point for file system control.

    DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = RecognizerFsControl;

// Driver itself is unloadable

    DriverObject->DriverUnload = Unload;

// Initialize the name string for the file system recognizer device object.

    RtlInitUnicodeString(&driverName, FSD_RECOGNIZER_NAME);

// Create the named device object.

    code = IoCreateDevice(RecognizerDriverObject,
                                    0,
                                    &driverName,
                                    FILE_DEVICE_DISK_FILE_SYSTEM,
                                    0,
                                    FALSE,
                                    &RecognizerDeviceObject);

    if (!NT_SUCCESS(code)) {
        DbgPrint("Recognizer failed to load, failure in IoCreateDevice call returned 0x%x\n",              code);

        // Bail out.

        return (code);
    }

// Register the device object as a file system.

    IoRegisterFileSystem(RecognizerDeviceObject);

// Done!

    return STATUS_SUCCESS;
}

Figure 2 DriverEntry for FSR

Figure 2 contains the DriverEntry(…) for our FSR. When called the DriverEntry(…) routine initializes itself to respond to two calls from the I/O Manager – the dispatch entry point for IRP_MJ_FILE_SYSTEM_CONTROL and the driver Unload entry point. Nothing else has to be handled because all we are trying to do is recognize our file system format. When the real File System is loaded loaded, it will handle actual I/O operations on the file system.

In the FSR’s DriverEntry(…) routine it creates a Device Object which represents the type of file system media to recognize and calls IoRegisterFileSystem(…) which registers the Device Object as a file system. As we stated before, the entire process is actually quite simple.

One important note here: you could just as easily implement a recognizer which handled multiple different types of media in a single driver. Such a driver would create one device object for each type of media it wished to recognize.

Why might you wish to recognize more than one type of media? Well, there are two different possibilities here. Suppose that your file system supported different types of media, such as a UDF file system might do. Since Windows NT matches up the type of file system with the type of media, your recognizer might create one device object for recognizing disk file system – that is done by creating a device object of FILE_DEVICE_DISK_FILE_SYSTEM like we do in the sample code. A second device object would then be created of type FILE_DEVICE_CD_ROM_FILE_SYSTEM. Then, regardless of whether the UDF file system were present on a disk or a CD it would be detected by your recognizer.

Another possible reason for using multiple device objects would be to handle multiple file system media types. This approach is the one used by the Microsoft-provided file system recognizer – a single driver handles recognition of FAT, NTFS, and CDFS file systems. It distinguishes which file system needs to be loaded by using the device objects it creates. Specifically, it creates one device object for each of the file systems it recognizes.

//
// Unload
file://
// This is the function is called when the OS has determined
// that their are no more references to the device Object. It
// is our job to unregister ourselves as a File System and delete
// the Device Object we created. Typically this will be called after
// we have recognized our file systems and told the IO manager to load
// our file system and get rid of us.
file://
// Inputs:
// DeviceObject - presumably OUR device object
file://
// Outputs:
// None.
file://
// Returns:
// None.
//
// Notes:
// None.
//
static VOID Unload(PDRIVER_OBJECT DriverObject)
{
    file://
    // Delete our device object, if there is one, and that's about it.
    file://

    if (RecognizerDeviceObject) {
        IoUnregisterFileSystem(RecognizerDeviceObject);
        IoDeleteDevice(RecognizerDeviceObject);
        RecognizerDeviceObject = 0;
    }
    file://
    // Done!
    file://
}

Figure 3 – Unload

Figure 3 shows the Unload handler for the FSR. This function is called either when an explicit call is made to halt the driver (e.g., the "net stop" command) or when there are no more references to the Driver Object which occurs when the full file system is loaded.

If this is an explicit call from a user to halt the recognizer driver, we note this fact because the RecognizerDeviceObject is not NULL. Thus, we unregister the device object, delete it, and return to the caller. If this is a call by the I/O Manager because the recognizer device object was deleted, and hence the last reference is gone, then the RecognizerDeviceObject is NULL and we need do nothing else. In either case, the recognizer driver will be unloaded shortly thereafter.

//
// RecognizerFsControl
file://
// This is the ONLY function that is implemented in this driver and
// it does two things: handles mount and load requests. If the mount
// request is intercepted, it looks at the media. If the media is
// recognized, this routine should return LOAD_DRIVER. When the call
// comes in from the I/O Manager to load the FSD, it does it. Simple!
file://
// Inputs:
// DeviceObject - presumably OUR device object
// Irp - the mount/load request
file://
// Outputs:
// None.
file://
// Returns:
// STATUS_NOT_IMPLEMENTED - something other than mount/load
// STATUS_UNRECOGNIZED_VOLUME - not ours
// STATUS_FS_DRIVER_REQUIRED - ours, load us
// SUCCESS - load was OK
//
// Notes:
// None.
//
static NTSTATUS RecognizerFsControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    UNICODE_STRING driverName;
    NTSTATUS code;

    // Paranoid, right?

    if (DeviceObject != RecognizerDeviceObject) {

        // Not ours.

        Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_NOT_IMPLEMENTED;
    }

    // Verify this is mount/load. If not, we don't do this.

    if ((irpSp->MinorFunction != IRP_MN_MOUNT_VOLUME) &&
        (irpSp->MinorFunction != IRP_MN_LOAD_FILE_SYSTEM)) {

        // We don't do that (whatever "that" is!)

        Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_NOT_IMPLEMENTED;
    }

    // Handle load case

    if (irpSp->MinorFunction == IRP_MN_LOAD_FILE_SYSTEM) {

        // Load the file system itself

        RtlInitUnicodeString(&driverName, FSD_SERVICE_PATH);
        code = ZwLoadDriver(&driverName);

        // Irp completes with the results of the load(s)

        Irp->IoStatus.Status = code;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        // In the case of a successful load, we no longer need our device
        // object.

        if (NT_SUCCESS(code)) {
            IoUnregisterFileSystem(RecognizerDeviceObject);
            IoDeleteDevice(RecognizerDeviceObject);
            RecognizerDeviceObject = 0;

            // We're ready to be unloaded!

        }
        return (code);
    }

    // Handle mount case - that's all that's left at this point.
    // XXX: ADD CODE in the RecognizerDetectFileSystem routine TO RECOGNIZE
    // THE VOLUME. Use the routines RecognizerXXXXX routines included
    // to issue I/O control requests to the underlying media device.

    code = RecognizerDetectFileSystem(Irp);

    // Look at the return code. If this is success, then this is our file system,
    // and we will tell the caller that they should call us back to load our file
    // system drivers.

    if (NT_SUCCESS(code)) {

        // This is the case where the volume is recognized

        Irp->IoStatus.Status = STATUS_FS_DRIVER_REQUIRED;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_FS_DRIVER_REQUIRED;
    }

    // Volume was not recognized.

    Irp->IoStatus.Status = STATUS_UNRECOGNIZED_VOLUME;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_UNRECOGNIZED_VOLUME;
}

Figure 4 RecognizerFSControl

Figure 4 contains the heart of the FSR "RecognizerFSControl", which is the handler for IRP_MJ_FILE_SYSTEM_CONTROL requests. This entry point is always called at IRQL = PASSIVE_LEVEL. This function then handles two minor functions: IRP_MN_MOUNT_VOLUME which is called when there is a media volume being mounted, and IRP_MN_LOAD_FILE_SYSTEM when the file system driver must be loaded.

Earlier in the article we described how the I/O Manager calls the registered file systems when an unmounted physical media volume is accessed. Each of the file systems is provided with an opportunity to analyze the media volume until one of them claims it. Thus, the FSR receives a mount request and that request is dispatched here. The FSR then calls out to the routine RecognizerDetectFileSystem(…) which we will describe later in this article. If this routine returns STATUS_SUCCESS, presumably because the media belongs to this FSR’s FSD, it in turn returns STATUS_FS_DRIVER_REQUIRED to the I/O Manager. This error code is specially detected by the I/O Manager because the support for file system recognizers is built into the I/O Manager’s mount processing.

Upon receiving this error value, the I/O Manager then sends a new I/O request down to the FSR. This time the request will use the IRP_MN_LOAD_FILE_SYSTEM minor function code. The FSR then implements this request by loading the driver using ZwLoadDriver(…). The only parameter to ZwLoadDriver(…) is the name of the registry key that points to the driver to be loaded. Windows NT then attempts to load the specified driver. If the driver can be successfully loaded, ZwLoadDriver(…) returns STATUS_SUCCESS and the file system recognizer can deregister and delete the recognizer device object. This will then, in turn, trigger unloading of the recognizer driver itself.

With the full file system driver loaded, the I/O Manager will then start processing the mount request all over again. However, since the full FSD just registered, it will be the first file system driver called. Also, because the full file system is now loaded, the FSR is no longer needed. Subsequent mount requests will be handled by the FSD.

//
// RecognizerDetectFileSystem
file://
// This is the routine which the user would modify to detect the presence of their
// File System on the underlying media.
file://
// Inputs:
// Irp - the Irp that requested that the volume be mounted.
file://
// Ouputs:
// None.
file://
// Returns:
// STATUS_SUCCESS - This is our file system and will indicated to the caller to that
// our real file system driver needs to be loaded.
// Other - I/O error
file://
// Notes:
// None.
file://
static NTSTATUS RecognizerDetectFileSystem(PIRP Irp)
{
    NTSTATUS                       code =STATUS_SUCCESS;
    DISK_GEOMETRY                   diskGeometry;
    PARTITION_INFORMATION           partitionInfo;
    PIO_STACK_LOCATION              irpSp = IoGetCurrentIrpStackLocation(Irp);
    PVPB                           vpb = irpSp->Parameters.MountVolume.Vpb;
    PDEVICE_OBJECT                 mediaDeviceObject = irpSp->Parameters.MountVolume.DeviceObject;
    unsigned char*                 pBuffer = NULL;

    file://    First build IRP and get Geometry and Partition information for this volume.
    file://          use routine RecognizerIoControl to issue these requests
    file://    IF error
    file://        return error
    file://    else
    file://        Read the volume master block. Use RecognizerReadDiskSector to issue this request.
    file://        If read error
    //             return error
    file://        else
    //             unpack the master block and check signature.
    //             if signature check fails (i.e. this is not your file system)
    //                 return error
    //             else
    //                     return code STATUS_SUCCESS;
    //             endif
    //        endif
    file://    endif
    file://
    file://    For now return an error, but if successful return STATUS_SUCCESS

    code = STATUS_UNRECOGNIZED_VOLUME;
    return code;
}

Figure 5 – RecognizerDetectFileSystem

The routine RecognizerDetectFileSystem(…) shown in Figure 5, is responsible for the actual recognition of its particular type of disk. Since this will depend entirely upon your particular physical file system layout, we will only describe a general approach – it is up to you to customize this section to fit your particular media.

There are two key functions required by this routine, one is to read data from the underlying media, and the other is to retrieve characteristics information about that media. This functionality is encapsulated in two routines. The first, RecognizerReadDiskSector(…), handles actually reading the media volume in order to analyze its contents. The second, RecognizerIoControl(…), is used to send device control operations to the underlying media device. These device control operations obtain information about the physical media device. For example, IOCTL_DISK_GET_DRIVE_GEOMETRY returns information about the media, such as the number of bytes per sector. IOCTL_DISK_GET_PARTITION_INFORMATION returns information about the disk drive on which the volume is located. It is common for NT file systems to use the disk partition information as part of their signature matching algorithm.

Keep in mind that the routine RecognizerReadDiskSector(…) utilizes partition relative sector numbers, not disk relative partition numbers. Thus, if you use the value zero as the partition number you will get the first sector of the partition, not of the disk. In other words, if your partition starts on sector 540, a read of that partition beginning at sector zero (relative to the partition) will be sector 540 (relative to the beginning of the disk drive.) 

//
// RecognizerIoControl
file://
// This routine passes the specified I/O control requested to the underlying device.
file://
// Inputs:
// MediaHandle - the volume handle for the specified device
// Offset - the logical offset where read starts
// Length - the length of the read
// MDL - the mdl chain describing the buffer where the data itself is to be copied
file://
// Ouputs:
// None.
file://
// Returns:
// STATUS_SUCCESS - I/O completed successfully
// Other - I/O error
file://
// Notes:
// None.
file://
static NTSTATUS
RecognizerIoControl( IN PDEVICE_OBJECT deviceObject,
                    IN ULONG IoctlCode,
                    IN PVOID InputBuffer,
                    IN ULONG InputBufferSize,
                    OUT PVOID OutputBuffer,
                    OUT ULONG OutputBufferSize)
{

    PIRP irp;
    NTSTATUS code;
    KEVENT event;
    IO_STATUS_BLOCK iosb;

    // Init the event we'll use for waiting for the request to complete

    KeInitializeEvent(&event, SynchronizationEvent, FALSE);

    // Build the request
    file://

    irp = IoBuildDeviceIoControlRequest(IoctlCode,
                                    deviceObject,
                                    InputBuffer,
                                    InputBufferSize,
                                    OutputBuffer,
                                    OutputBufferSize,
                                    FALSE,
                                    &event,
                                    &iosb);

    // Send the request to the lower layer driver

    code = IoCallDriver(deviceObject, irp);

    // Wait if we must. Note that we do not accept APCs - we cannot
    // return out of this path until the I/O is done, since the event
    // and IOSB are on the stack.
    file://

    if (code == STATUS_PENDING) {
        (void) KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
        code = iosb.Status;
    }

    file://
    // Set the final size of the output buffer.
    file://

    OutputBufferSize = iosb.Information;

    file://
    // Done!
    file://

    return(code);
}

Figure 6 – RecognizerIoControl

//
file://
// RecognizerReadDiskSector
file://
// Reads a disk sector off the specified input device.
file://
// Inputs:
// pDeviceObject - Supplies a pointer to the disk device object.
// DiskSector - number of the sector on the disk to be read.
file://
// Outputs:
// Buffer - address of the buffer to receive the read sector. It must be sectorSize bytes.
file://
// Returns:
// Returns TRUE if the disk sector was read.
file://
// Notes:
// None.
file://
static BOOLEAN RecognizerReadDiskSector(
    IN PDEVICE_OBJECT pDeviceObject,
    IN ULONG
DiskSector,
   
IN
UCHAR* Buffer // must be DEVICE_LOGICAL_BLOCKSIZE bytes long.)
{

    LARGE_INTEGER sectorNumber;
    PIRP irp;
    IO_STATUS_BLOCK ioStatus;
    KEVENT event;
    NTSTATUS status;
    ULONG sectorSize;
    PULONG mbr;

    PAGED_CODE();

    sectorNumber.QuadPart = (LONGLONG) DiskSector * DEVICE_LOGICAL_BLOCKSIZE;

    // Create notification event object to be used to signal the inquiry
    // request completion.

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    // Get sector size.

    sectorSize = DEVICE_LOGICAL_BLOCKSIZE;

    // Allocate buffer for sector read.

    mbr = ExAllocatePool(NonPagedPoolCacheAligned, sectorSize);

    if (!mbr) {
        return FALSE;
    }

    // Build IRP to read MBR.

    irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
                                            pDeviceObject,
                                            mbr,
                                            sectorSize,
                                            §orNumber,
                                            &event,
                                            &ioStatus );
    if (!irp) {
        ExFreePool(mbr);

return FALSE;
    }

    // Pass request to port driver and wait for request to complete.

    status = IoCallDriver(pDeviceObject, irp);

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event,
                        Suspended,
                        KernelMode,
                        FALSE,
                        NULL);

        status = ioStatus.Status;
    }

    if (!NT_SUCCESS(status)) {
    ExFreePool(mbr);

    return FALSE;
    }

    file://
    // return the read partition information
    file://

    RtlCopyMemory(Buffer,mbr,sectorSize);
    ExFreePool(mbr);

    return TRUE;

}

Figure 7 – RecognizerReadDiskSector

Finally Figure 6 and Figure 7 contain the previously mentioned RecognizerIoControl(…) and RecognizerReadDiskSector(…) routines. As you can see, they are, like all code, self-documenting. They take advantage of the fact that they are going to be called at PASSIVE_LEVEL and can use KeWaitForSingleObject(…) to wait for requests that they issue to complete.

Getting It All to Run

 

Now that we’ve told you all about a FSR, it’s now time to tell you how to register it and your File System Driver (FSD) so that it all works. To do this, you need to configure your FSR and FSD as follows:

Key Name:          SYSTEM\CurrentControlSet\Services\MyFsdRecognizer
Class Name:        
Last Write Time:   
10/10/96 - 4:09 AM

Value 0
Name:              ErrorControl
Type:              REG_DWORD
Data:              0x1

Value 1
Name:              Group
Type:              REG_SZ
Data:              File system

Value 2
Name:              Start
Type:              REG_DWORD
Data:              0x1

Value 3
Name:              Type
Type:              REG_DWORD
Data:              0x8

Key Name:          SYSTEM\CurrentControlSet\Services\MyFsd
Class Name:        
Last Write Time:   
10/10/96 - 4:09 AM

Value 0
Name:              ErrorControl
Type:              REG_DWORD
Data:              0x1

Value 1
Name:              Group
Type:              REG_SZ
Data:              File system

Value 2
Name:              Start
Type:              REG_DWORD
Data:              0x3

Value 3
Name:              Type
Type:              REG_DWORD
Data:              0x2

Figure 8 Registry Keys

You will notice from the registry information that we configure the FSR to load at system start and make it a member of the "File System" load group. The FSD is configured so that it is started manually which might seem confusing. Of course, since the FSR’s whole raison d’etre is to load the FSD it is the FSR which performs the "manual" loading of your file system. Configuring your FSD for manual start ensures that the driver will not be automatically started by Windows NT but can instead be started by your FSR using ZwLoadDriver(…).

Conclusion

 

An FSR is a simple driver to write and use with your complete file system. While it isn’t essential, it does allow for your file system driver to be loaded "on demand" and minimize its memory requirements when it isn’t needed.

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"Thanks!"
I searched a lot, and this is the only place that explains how an IFS driver is actually loaded. Thanks a lot!

Rating:
17-Apr-11, Humble Bumble


"nice articles"
when i search fs_rec, only this place can give you detailed information about it.

thanks

Rating:
07-Dec-09, zhen hua yang


Post Your Comments.
Print this article.
Email this article.

Writing WDF Drivers I: Core Concepts
LAB

Nashua (Amherst), NH
15-19 May 2017

Writing WDF Drivers II: Advanced Implementation Techniques
LAB

Nashua (Amherst), NH
23-26 May 2017

Kernel Debugging and Crash Analysis
LAB

Dulles (Sterling), VA
26-30 Jun 2017

Windows Internals and Software Driver Development
LAB

Nashua (Amherst), NH
24-28 Jul 2017

 
 
 
 
x
LetUsHelp
 

Need to develop a Windows file system solution?

We've got a kit for that.

Need Windows internals or kernel driver expertise?

Bring us your most challenging project - we can help!

System hangs/crashes?

We've got a special diagnostic team that's standing by.

Visit the OSR Corporate Web site for more information about how OSR can help!

 
bottom nav links