This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients cgohlke, eryksun, paul.moore, steve.dower, tim.golden, zach.ware
Date 2019-09-11.13:44:43
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1568209484.28.0.370094339457.issue37993@roundup.psfhosted.org>
In-reply-to
Content
> It's a few steps deep, but DefineDosDeviceW() [1] specifies that it 
> creates junctions, and while it's not necessarily obvious how to get
> from SUBST to that page, Wikipedia managed it [2]. 

Take care to not conflate device junctions with the file-system reparse point that we commonly call a junction (aka a "mount point"). The term "junction" is used generically in the DefineDosDeviceW docs as well as in "Defining an MS-DOS Device Name". It's not supporting your earlier statement: "If we can easily tell the difference between directory junctions and mapped drives, given that they are both identical types of reparse points". Junctions in the object namespace have nothing to do with file-system reparse points.

A junction is a joining between two things -- in this case between two names in the object namespace. We can explore the NT object namespace via Sysinternals WinObj. The Object Manager maintains this namespace as a nested tree of Directory objects that contain other named kernel objects (e.g. an I/O Manager "Device", a Configuration Manager "Key", a Memory Manager "Section", a Process Manager "Job"). It also implements a named SymbolicLink object type, which is the basis for device junctions. 

SymbolicLink objects get reparsed by the Object Manager. The target path is a new absolute path in the object namespace. The system calls for working with SymbolicLink objects are as follows:

    NtCreateSymbolicLinkObject - Create a SymbolicLink object
        with the provided name, attributes and target path,
        and return a handle for it.
    NtOpenSymbolicLinkObject - Return a handle for an existing
        SymbolicLink object.
    NtQuerySymbolicLinkObject - Get the target path of a 
        SymbolicLink object.
    NtMakeTemporaryObject - Make a SymbolicLink object 
        temporary, so that it's automatically unlinked from 
        its parent Directory when no longer referenced.

They can be created with any name in any object directory. But specifically for device junctions they get created in particular object directories (discussed below) and often with DOS drive names "A:" - "Z:". Of course, other names are also used such as "CON" -> "\Device\ConDrv\Console", "NUL" -> "\Device\Null", "PIPE" -> "\Device\NamedPipe", and "PhysicalDrive0" -> "\Device\Harddisk0\DR0".

The target path isn't limited to just an object in the object namespace. It can include a remaining path that's parsed by the object. For example, the target could be "\Device\HarddiskVolume2\Windows\System32", where the object is "\Device\HarddiskVolume2" and the remaining path is "\Windows\System32". (It could just as well target a file-system file such as "kernel32.dll" in that directory.) The drives created by subst.exe take advantage of this capability to link directly to file-system directories. But it's noteworthy that this is a weird sort of drive that causes bugs in some API functions such as GetVolumePathNameW, which assumes a DOS drive is a junction to a volume device, not a file-system directory.

Each logon session has a local object Directory for its device junctions (AKA "DOS devices"). It makes sense for local devices to be associated with a logon session because credentials for mapped drives are associated with the user's logon session. The local Directory is located at "\Sessions\0\DosDevices\<logon session ID>". It's in desktop session 0 (non-interactive services) because logon sessions aren't necessarily limited to a single desktop session. The local directory shadows the system global directory, "\Global??". Name lookup first checks the local directory and then the global one. The SYSTEM logon uses "\Global??" as its local directory, so defining a device junction in a SYSTEM context always creates a global junction. A user's local directory is typically used just for mapped and subst drives.

The local device directory for the current user is accessible as "\??\", which the Object Manager reserves for this case. So native code doesn't need to look up the logon-session ID and create the "\Session\0\DosDevices\<logon session ID>" path. Neither does the Object Manager itself because the local and global directories are cached per process and per logon session. The local directory also contains a "Global" SymbolicLink to the global directory. 

The equivalent of NT "\??\" in the Windows API is either "\\?\" (non-normalized path) or "\\.\" (normalized path). For example, we can access r"\\?\Global\Z:", which may not be the same device as "\\?\Z:".

DefineDosDeviceW sends an LPC request to the desktop session server, csrss.exe, in order to define a device junction. This request is handled by basesrv!BaseSrvDefineDosDevice. As necessary, BaseSrvDefineDosDevice impersonates the caller to ensure it creates a local junction in the right directory. 

BaseSrvDefineDosDevice either redefines or creates a new SymbolicLink object. If the device junction already exists, it tries to redefine the target. First it queries the existing SymbolicLink to read its target. This allows a trick that takes advantage of NT counted strings. The buffer is made large enough for the new target path and the old path, separated by a NUL. The string's length includes only the new target path, but its maximum length includes all previous target paths. Thus we can "push" a new mapping for a junction (e.g. drive "Z:") and "pop" it off to restore the previous mapping when it's no longer needed.
History
Date User Action Args
2019-09-11 13:44:44eryksunsetrecipients: + eryksun, paul.moore, tim.golden, cgohlke, zach.ware, steve.dower
2019-09-11 13:44:44eryksunsetmessageid: <1568209484.28.0.370094339457.issue37993@roundup.psfhosted.org>
2019-09-11 13:44:44eryksunlinkissue37993 messages
2019-09-11 13:44:43eryksuncreate