Eryk Sun <eryk...@gmail.com> added the comment:

> 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.

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue37993>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to