The NT Insider

Maybe I Should Drive - Drive Letter Assignment & The Mount Manager
(By: The NT Insider, Vol 9, Issue 4, Jul-Aug 2002 | Published: 15-Aug-02| Modified: 03-Oct-02)

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

 

A perennial problem in Windows NT has been the issue of how to assign drive letters in such a manner that they were persistent ? in other words, so that they remained constant even when you modified the configuration of the system.  Adding a new drive to the system should not cause your existing drives to change identities.

 

Windows NT 4.0 and earlier versions simply made these assignments based upon information stored in the registry (the DISK key) during system initialization.  The actual assignment of drive letters was done by an I/O Manager function, but could be overridden within custom HALs so that vendors could control the layout of partitions on their systems.

 

The proliferation of storage devices stressed this system ? we would periodically hear ?war stories? of people who had installed Iomega ZIP drives only to find out that their drive letter assignments had been shuffled around and the corresponding pain that this would bring people.  File systems developers for network file systems or file systems that were not associated with a physical volume would struggle with the issues of drive letter assignment.  Finally, the addition of plug-and-play in Windows 2000 really required a more flexible model that could work with all of these scenarios.

 

In all versions of Windows NT as well as Windows 2000 and Windows XP, drive letters are nothing more than ?symbolic links? in the object manager name space.  In Windows NT 4.0, these symbolic links were displayed in the ?\??? directory of the object manager name space.  Thus, to create a new drive letter all one would have to do is create a symbolic link in the object manager name space.  Even during the NT 4.0 life cycle some problems arose with this approach: 

  • Existing applications that already ?knew? about drive letters would not update their view of the drive letter namespace automatically.
  • Shared sessions (using Terminal Server) required disjoint drive letters for (at least) the network shares.
  • Dynamic devices ? of any type ? would not receive drive letters because the assignment was done during initialization.

These issues led to the development of several new mechanisms for tracking drive letters.  In Windows 2000 the Mount Manager was introduced in order to provide a new mechanism for associating drive letters with their devices in such a way that configuration changes and dynamic media arrival and removal events would be handled gracefully.

 

The Mount Manager is, at its heart, a driver that maintains a simple registry-based database of device-to-drive mappings.  It does not resolve the network drive letter issues, leaving those to the network redirectors.  It does, however, handle the dynamic arrival of a new media device, associating it with a drive letter so that a subsequent use of the same media will attempt to use the same drive letter and the introduction of a new media device will attempt to use a different drive letter!

 

For application programs, the Mount Manager is controlled using several different Win32 APIs relating to volume mountpoint management, such as SetVolumeMountPoint and DeleteVolumeMountPoint which work for both mount points on NTFS directories (which are implemented as reparse points) as well as drive letters (which are implemented as symbolic links in the object manager name space).

 

Of course, for those building kernel mode components, those user-mode APIs are not available directly.  We either have to utilize some sort of captive service (which we?ve certainly discussed in The NT Insider in the past REF?????) or we can utilize the IOCTL interface underlying this API as implemented within the Mount Manager.

 

As we noted previously, the basic function of the Mount Manager is to associate volumes with drive letters.  It does this by collecting unique identifying information from the volumes directly.  Thus, any device that exports storage volumes must support special IOCTL operations needed by the Mount Manager to associate this information.  These operations are defined in the DDK header files mountmgr.h and mountdev.h, which are part of the standard Windows DDK.

 

The following operations are required by the Mount Manager to be supported by any device that exports volumes (that is, it calls IoRegisterDeviceInterface using the MOUNTDEV_MOUNTED_DEVICE_GUID identifier for its interface):

 

IOCTL_MOUNTDEV_QUERY_UNIQUE_ID ? this operation returns an arbitrary string of information that will be used to track the given volume.  The actual format and data contents of this are determined by the driver and have no special meaning to the Mount Manager.  This ID should be considered static by the driver.  Should this identifier change, the driver must indicate that to the Mount Manager by calling IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY).

 

IOCTL_MOUNTDEV_QUERY_DEVICE_NAME ? this operation returns the name of the device object, which must not be empty, because this is used by the Mount Manager in order to create the symbolic link.

 

The following operations are not required, but may be supported by volume managers that support specific features:

 

IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY ? this operation is used by a driver to indicate a change in the unique identifier of a volume; this allows the Mount Manager to update its database with the new unique identifier.

 

IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME ? this operation may be used by a driver with a persistent identifier that also wishes to suggest a specific name to the Mount Manager.  It is a hint only and the Mount Manager will not use it if the configuration does not support the suggested name.

 

IOCTL_MOUNTDEV_LINK_CREATED ? this operation allows a driver to be advised when a persistent name has been assigned to the volume.

 

IOCTL_MOUNTDEV_LINK_DELETED ? this operation allows a driver to be advised when a persistent name has been deleted from the volume.

 

IOCTL_MOUNTDEV_QUERY_STABLE_GUID ? this operation allows a driver to associate a persistent GUID with the device.

 

The Mount Manager interface also provides an API that allows the Mount Manager database to be queried.  These include:

 

IOCTL_MOUNTMGR_CREATE_POINT ? used to create a new persistent name for a volume. It takes (as input) the new name and a valid name for the volume (the Mount Manager will use this name to communicate with the specific volume).

 

IOCTL_MOUNTMGR_DELETE_POINTS ? used to delete an existing mount point.  If the input parameters to this call are the name of the current symbolic link, and then a null name and offset, the Mount Manager will note that the given device does not wish to be assigned any drive letter and it will suppress assignment of a drive letter in the future (including across reboot operations).

 

IOCTL_MOUNTMGR_QUERY_POINTS ? used to query existing information about volumes.  The input parameters are encoded versions of three possible strings: the persistent volume name, the unique id value, and the volume name.  This call returns all volumes matching the names provided by the caller (if any).

 

IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY ? similar in function to IOCTL_MOUNTMGR_DELETE_POINTS, but it only updates the Mount Manager database ? it does not delete the in-memory information and thus the mount points remain visible, but will not persist across a reboot cycle.      

 

IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER ? this operation is used by the I/O Manager when assigning drive letters during system initialization to obtain existing drive letter assignments (if any) and assign a new drive letter as needed.

 

IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS ? this operation is used by the I/O Manager as part of its drive letter assignment.

 

IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED ? this operation indicates the creation of a new NTFS junction (a ?mount point? stored within the NTFS file system) so that the Mount Manager can update its own database.

 

IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED ? this operation indicates the removal of an NTFS junction (a ?mount point? stored within the NTFS file system) so that the Mount Manager can update its own database.

 

IOCTL_MOUNTMGR_CHANGE_NOTIFY ? this is a hanging IRP operation that pends until such time as a change to the Mount Manager database occurs.  Thus, this can be used to monitor updates to the database.

 

IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE ? this operation modifies the way that the Mount Manager cleans up after a volume becomes unavailable.  Normally, the symbolic link to the volume is deleted but when this option is enabled, the symbolic links remain, even though the volume to which it refers is not presently available on the system.  Should the volume become available again, the Mount Manager would adjust the symbolic link as necessary.  This functionality would be useful for a cluster manager, for example, to ensure that the configuration can be ?locked down? even though the clustered storage device is not presently available.

 

IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES ? this operation causes the mount manager to rescan the volumes that did not respond to previous interrogation from the Mount Manager.  This might be useful, for example, when a volume becomes unavailable for a period of time and is unable to respond to normal Mount Manager queries.

 

IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION ? this operation allows a device to simulate the PnP manager?s notification method that a new volume has become available.  This mechanism is only useful for those volumes that do not support the standard plug and play mechanism for registering the device interface.

 

IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH ? this operation allows a caller to query the Mount Manager for a given device and locate the symbolic link (?dos device name?) corresponding to the drive letter.  This is equivalent to the functionality exported by the call RtlVolumeDeviceToDosName.

 

IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS ? this operation is similar to IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH except that it returns all mount paths to the specified volume, rather than just one of the paths.

 

Support for the Mount Manager?s device management interface can actually be seen in several DDK examples.  For example, here is a code snippet from the floppy disk driver (src/storage/fdc/flpydisk/floppy.c):

 

case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {

 

    PMOUNTDEV_NAME mountName;

 

    FloppyDump( FLOPSHOW, ("FloppyDeviceControl: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n") );

    ASSERT(disketteExtension->DeviceName.Buffer);

 

    if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <

         sizeof(MOUNTDEV_NAME) ) {

 

        ntStatus = STATUS_INVALID_PARAMETER;

        break;

    }

 

    mountName = Irp->AssociatedIrp.SystemBuffer;

    mountName->NameLength = disketteExtension->DeviceName.Length;

 

    if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <

         sizeof(USHORT) + mountName->NameLength) {

 

        ntStatus = STATUS_BUFFER_OVERFLOW;

        Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);

        break;

    }

 

    RtlCopyMemory( mountName->Name, disketteExtension->DeviceName.Buffer,

                   mountName->NameLength);

 

    ntStatus = STATUS_SUCCESS;

    Irp->IoStatus.Information = sizeof(USHORT) + mountName->NameLength;

    break;

    }

 

case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {

 

    PMOUNTDEV_UNIQUE_ID uniqueId;

 

    FloppyDump( FLOPSHOW, ("FloppyDeviceControl: IOCTL_MOUNTDEV_QUERY_UNIQUE_ID\n") );

 

    if ( !disketteExtension->InterfaceString.Buffer ||

         irpSp->Parameters.DeviceIoControl.OutputBufferLength <

          sizeof(MOUNTDEV_UNIQUE_ID)) {

 

        ntStatus = STATUS_INVALID_PARAMETER;

        break;

    }

 

    uniqueId = Irp->AssociatedIrp.SystemBuffer;

    uniqueId->UniqueIdLength =

            disketteExtension->InterfaceString.Length;

 

    if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <

        sizeof(USHORT) + uniqueId->UniqueIdLength) {

 

        ntStatus = STATUS_BUFFER_OVERFLOW;

        Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);

        break;

    }

 

    RtlCopyMemory( uniqueId->UniqueId,

                   disketteExtension->InterfaceString.Buffer,

                   uniqueId->UniqueIdLength );

 

    ntStatus = STATUS_SUCCESS;

    Irp->IoStatus.Information = sizeof(USHORT) +

                                uniqueId->UniqueIdLength;

    break;

    }

 

Note that these are the two required routines, although the floppy disk driver does support the IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME operation as well, which is entirely optional.

 

Looking at the ?unique id? returned here it is clear that it is nothing more than the device identification string!  In this case, the device name is nothing more than the string originally passed to IoCreateDevice from the driver?s AddDevice entry point.  In the case of the floppy disk, this would be a name like \Device\Floppy0.

 

For the volume managers on Windows, they would likely associate the unique ID with some characteristic of the volume itself ? a GUID, for example, or some other unique way of tracking that volume.  This ensures that should the configuration of the machine change (a new SCSI controller, or different SCSI id values, for example) the drive names would remain the same.

 

There are no DDK examples of using the device manager?s management APIs, so we constructed a simple utility that interrogates the mount manager database and displays the current configuration information.  The actual code was quite simple, consisting of one primary routine to retrieve the information:

 

 

//

// QueryMountPoint

//

// Inputs:

//   MountPoint - this is the buffer containing the mountpoint structure used for the query

//   MountPointLength - this is the total size of the MountPoint buffer

//   MountPointInfoLength - the size of the mount point Info structure

//

// Outputs:

//   MountPointInfo - this is the returned mount point information

//   MountPointInfoLength - the # of bytes actually needed

//

// Returns:

//   Results of the underlying operation

//

// Notes:

//   Re-opening the mount manager could be optimized if that were an important goal;

//   We avoid it to minimize handle context problems.

//

NTSTATUS QueryMountPoint(PMOUNTMGR_MOUNT_POINT MountPoint,

                         ULONG MountPointLength,

                         PVOID MountPointInfo,

                         PULONG MountPointInfoLength)

{

    OBJECT_ATTRIBUTES mmgrObjectAttributes;

    UNICODE_STRING mmgrObjectName;

    NTSTATUS status;

    HANDLE mmgrHandle;

    IO_STATUS_BLOCK iosb;

    HANDLE testEvent;

 

    //

    // First, we need to obtain a handle to the mount manager, so we must:

    //

    //  - Initialize the unicode string with the mount manager name

    //  - Build an object attributes structure

    //  - Open the mount manager

    //

    // This should yield a valid handle for calling the mount manager

    //

 

    //

    // Initialize the unicode string with the mount manager's name

    //

    RtlInitUnicodeString(&mmgrObjectName, MOUNTMGR_DEVICE_NAME);

 

    DbgPrint("Mount Manager Device Object is %wZ\n", &mmgrObjectName);

   

    //

    // Initialize object attributes.

    //

    mmgrObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);

 

    mmgrObjectAttributes.RootDirectory = NULL;

 

    mmgrObjectAttributes.ObjectName = &mmgrObjectName;

 

    //

    // Note: in a kernel driver, we?d add OBJ_KERNEL_HANDLE

    // as another attribute.

    //

    mmgrObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;

 

    mmgrObjectAttributes.SecurityDescriptor = NULL;

 

    mmgrObjectAttributes.SecurityQualityOfService = NULL;

 

    //

    // Open the mount manager

    //

    status = ZwCreateFile(&mmgrHandle,

                          FILE_READ_DATA|FILE_WRITE_DATA,

                          &mmgrObjectAttributes,

                          &iosb,

                          0, // allocation is meaningless

                          0, // no attributes specified

                          FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // we're willing to share

                          FILE_OPEN, // must already exist

                          FILE_NON_DIRECTORY_FILE, // must NOT be a directory

                          NULL, // no EA buffer

                          0); // no EA buffer size...

 

 

    if (!NT_SUCCESS(status) ||

        !NT_SUCCESS(iosb.Status)) {

 

        DbgPrint("Unable to open %wZ, error = 0x%x\n", &mmgrObjectName, status);

       

        return status;

 

    }

 

 

    //

    // If we get to here, we assume it was successful.  We need an event object

    // for monitoring the completion of I/O operations.

    //

 

    status = ZwCreateEvent(&testEvent,

                           GENERIC_ALL,

                           0, // no object attributes

                           NotificationEvent,

                           FALSE);

 

    if (!NT_SUCCESS(status)) {

        //

        // Bummer.

        //

        

        DbgPrint("Cannot create event (0x%x)\n", status);

       

        return status;

       

    }

 

    status =  ZwDeviceIoControlFile(mmgrHandle,

                                    testEvent,

                                    0, // no apc

                                    0, // no apc context

                                    &iosb,

                                    IOCTL_MOUNTMGR_QUERY_POINTS,

                                    MountPoint, // input buffer

                                    MountPointLength, // size of input buffer

                                    MountPointInfo, // output buffer

                                    *MountPointInfoLength); // size of output buffer

   

    if (STATUS_PENDING == status) {

      //

      // Must wait for the I/O operation to complete

      //

      status = ZwWaitForSingleObject(testEvent, TRUE, 0);

 

      if (NT_SUCCESS(status)) {

 

        status = iosb.Status;

 

      }

     

    }

 

    //

    // Regardless of the results, we are done with the mount manager and event

    // handles so discard them.

    //

    (void) ZwClose(testEvent);

    (void) ZwClose(mmgrHandle);

 

   

    if (!NT_SUCCESS(status)) {

     

      DbgPrint("DeviceIoControlFile failed 0x%x\n", status);

     

      return status;

 

    }

 

    *MountPointInfoLength = iosb.Information;

   

    return STATUS_SUCCESS;

}

 

The returned results can then be displayed in whatever format is convenient.  For this simple utility we relied upon a simple printf model:

 

    //

    // Decode returned data

    //

    mountPtr = &mountPoints->MountPoints[0];

 

    DbgPrint("mountPoints->Size is %d\n", mountPoints->Size);

 

    DbgPrint("mountPoints->NumberOfMountPoints is %d\n", mountPoints->NumberOfMountPoints);

 

    for (index = 0; index < mountPoints->NumberOfMountPoints; index++) {

 

        DbgPrint("\nMount Point # %d\n", index);

 

        mountPtr = &mountPoints->MountPoints[index];

 

        DbgPrint("\tmountPtr->SymbolicLinkNameOffset is %d\n", mountPtr->SymbolicLinkNameOffset);

 

        DbgPrint("\tmountPtr->SymbolicLinkNameLength is %d\n", mountPtr->SymbolicLinkNameLength);

 

        DbgPrint("\tmountPtr->UniqueIdOffset is %d\n", mountPtr->UniqueIdOffset);

 

        DbgPrint("\tmountPtr->UniqueIdLength is %d\n", mountPtr->UniqueIdLength);

 

        DbgPrint("\tmountPtr->DeviceNameOffset is %d\n", mountPtr->DeviceNameOffset);

 

        DbgPrint("\tmountPtr->DeviceNameLength is %d\n", mountPtr->DeviceNameLength);

 

        if (mountPtr->SymbolicLinkNameOffset && mountPtr->SymbolicLinkNameLength) {

            UNICODE_STRING string;

            ULONG index2;

 

            //

            // Try to display the link name

            //

            string.Length = string.MaximumLength = mountPtr->SymbolicLinkNameLength;

 

            string.Buffer = (PWCHAR)&buffer[mountPtr->SymbolicLinkNameOffset];

 

            DbgPrint("\tSymbolic Link Name: %wZ\n", &string);

        }

 

        if (mountPtr->UniqueIdLength && mountPtr->UniqueIdOffset) {

            UNICODE_STRING string;

            ULONG index2;

 

 

            //

            // Try to display the unique ID

            //

            string.MaximumLength = string.Length = mountPtr->UniqueIdLength;

 

            string.Buffer = (PWCHAR)&buffer[mountPtr->UniqueIdOffset];

 

            DbgPrint("\tUnique ID: %wZ\n", &string);

           

        }

 

        if (mountPtr->DeviceNameLength && mountPtr->DeviceNameOffset) {

            UNICODE_STRING string;

            ULONG index2;

 

 

            //

            // Try to display the device name

            //

            string.MaximumLength = string.Length = mountPtr->DeviceNameLength;

 

            string.Buffer = (PWCHAR) &buffer[mountPtr->DeviceNameOffset];

 

            DbgPrint("\tDevice Name: %wZ\n", &string);

           

        }

 

    }

 

Here are the sample results that we received when we ran it on a system here:

 

Mount Manager Device Object is \Device\MountPointManager

mountPoints->Size is 1042

mountPoints->NumberOfMountPoints is 6

 

Mount Point # 0

       mountPtr->SymbolicLinkNameOffset is 210

       mountPtr->SymbolicLinkNameLength is 96

       mountPtr->UniqueIdOffset is 152

       mountPtr->UniqueIdLength is 12

       mountPtr->DeviceNameOffset is 164

       mountPtr->DeviceNameLength is 46

       Symbolic Link Name: \??\Volume{b46946c3-f029-11d3-878b-806d6172696f}

       Unique ID:

       Device Name: \Device\HarddiskVolume1

 

Mount Point # 1

       mountPtr->SymbolicLinkNameOffset is 306

       mountPtr->SymbolicLinkNameLength is 28

       mountPtr->UniqueIdOffset is 152

       mountPtr->UniqueIdLength is 12

       mountPtr->DeviceNameOffset is 164

       mountPtr->DeviceNameLength is 46

       Symbolic Link Name: \DosDevices\C:

       Unique ID:

       Device Name: \Device\HarddiskVolume1

 

Mount Point # 2

       mountPtr->SymbolicLinkNameOffset is 600

       mountPtr->SymbolicLinkNameLength is 28

       mountPtr->UniqueIdOffset is 334

       mountPtr->UniqueIdLength is 238

       mountPtr->DeviceNameOffset is 572

       mountPtr->DeviceNameLength is 28

       Symbolic Link Name: \DosDevices\F:

       Unique ID: \??\IDE#CdRomMATSHITA_DVD-ROM_SR-8174________________C221____#5&35c6ca11&0&0.0.0#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}

       Device Name: \Device\CdRom0

 

Mount Point # 3

       mountPtr->SymbolicLinkNameOffset is 628

       mountPtr->SymbolicLinkNameLength is 96

       mountPtr->UniqueIdOffset is 334

       mountPtr->UniqueIdLength is 238

       mountPtr->DeviceNameOffset is 572

       mountPtr->DeviceNameLength is 28

       Symbolic Link Name: \??\Volume{113269c0-7869-11d4-bcaf-806d6172696f}

       Unique ID: \??\IDE#CdRomMATSHITA_DVD-ROM_SR-8174________________C221____#5&35c6ca11&0&0.0.0#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}

       Device Name: \Device\CdRom0

 

Mount Point # 4

       mountPtr->SymbolicLinkNameOffset is 918

       mountPtr->SymbolicLinkNameLength is 28

       mountPtr->UniqueIdOffset is 724

       mountPtr->UniqueIdLength is 164

       mountPtr->DeviceNameOffset is 888

       mountPtr->DeviceNameLength is 30

       Symbolic Link Name: \DosDevices\A:

       Unique ID: \??\FDC#GENERIC_FLOPPY_DRIVE#5&29337118&1&0#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}

       Device Name: \Device\Floppy0

 

Mount Point # 5

       mountPtr->SymbolicLinkNameOffset is 946

       mountPtr->SymbolicLinkNameLength is 96

       mountPtr->UniqueIdOffset is 724

       mountPtr->UniqueIdLength is 164

       mountPtr->DeviceNameOffset is 888

       mountPtr->DeviceNameLength is 30

       Symbolic Link Name: \??\Volume{113269c1-7869-11d4-bcaf-9ba4bf332ada}

       Unique ID: \??\FDC#GENERIC_FLOPPY_DRIVE#5&29337118&1&0#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}

       Device Name: \Device\Floppy0

 

One notable change that occurs in Windows XP is the manner in which drive letters are maintained on the system.  In Windows XP (and in .NET) each session (a group of related processes associated with a single logon session) has its own drive letter map. This allows Terminal Server (for example) to distinguish between the drive letters created by different users.  This has been problematic for drivers that create their own drive letters (as was typical in previous versions).

 

In Windows 2000, there is a ?\??? directory that contained all of the globally visible device names.  Mapping individual session device names is done using a private device map shared between the processes of a given session.  In Windows XP, the ?\??? directory is eliminated.  We have displayed an image of the object manager name space (using the new OSR ?Object Viewer? utility available for download from our website).

 

 

Note the conspicuous absence of a ?\??? from this display.  There is a new directory ?\Global??? but it does not precisely replace the old ?\??? directory.  For example, there is also a ?\DosDevices? symbolic link ? and it points off to ?\???.  What happens inside the object manager is that this resolves to the correct session-specific drive letter map (note the ?Sessions? directory in the left hand pane of this listing?  There are actual session-specific device maps located in subdirectories of that session map).

 

This causes problems for drivers that create drive letters directly because the drive letter they create will appear in the session specific map if they create it within the context of a session (note there is great documentation on this effect in the MSDN documentation about the Win32 API call DefineDosDevice).

 

Fortunately, this problem does not impact the Mount Manager ? it creates its drive letters in the ?\Global??? path, and does correction for any of its callers that request names in the ?\??? or ?\DosDevices? directories.

 

The Mount Manager has achieved its primary purpose ? it provides a robust way of tracking devices and associating them with drive letters, and minimizes the likelihood that they will conflict with one another.  It fits in well with the Plug and Play architecture and reliably handles the arrival (and departure) of devices.  Anyone implementing a storage driver that supports volumes should be supporting the MOUNTDEV operations.  For those attempting to manipulate volumes (map from volume to drive letter or mount point) should also be using the Mount Manager.  For example, file system filter drivers that need to use drive letters (for display purposes) or path names, can rely upon the Mount Manager to simplify this resolution.

 

This article was printed from OSR Online http://www.osronline.com

Copyright 2017 OSR Open Systems Resources, Inc.