[Tutor] Partition in Windows or Linux

Eryk Sun eryksun at gmail.com
Thu May 28 13:51:30 EDT 2020


On 5/23/20, Mats Wichmann <mats at wichmann.us> wrote:
>
> Windows partitions, *if* they look like they have valid Windows-readable
> file systems on them *and* haven't been excluded by volume management
> settings, are mapped in to the "filesystem" as a drive letter (that even
> includes local network storage).

Device names are created in the object manager namespace, not a
filesystem. Most devices are created in the r"\Device" directory.
Persistent links to devices, and paths on devices (e.g. mapped and
subst drives), are created for a given logon context in
r"\Sessions\0\DosDevices\<logon ID>", except the SYSTEM logon uses
"\GLOBAL??" instead. For non-SYSTEM logons, the local device-link
directory implicitly shadows the global SYSTEM directory, and there's
also an explicit "Global" link. For example, if the user has a mapped
drive named "Z:" and there's also a global r"\GLOBAL\Z:" defined, the
global path "Z:/path/to/file" can be accessed via
"//?/Global/Z:/path/to/file".

A volume device might not be assigned a drive-letter, but it will
always be assigned a persistent "Volume{GUID}" name, where a GUID is a
unique 16-byte identifier, which is rendered as a hexadecimal string
with 32 digits. For example, the persistent volume name is of the form
"Volume{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".

Note that the primary mountpoint is the root path on a device, e.g.
"C:/", and not the device name itself. "C:" is a drive-relative path
that evaluates to the current directory on the drive and can be used
with a remaining path. For example, if the working directory on the
drive is "C:/eggs", then "C:spam" resolves to "C:/eggs/spam".
Drive-relative paths don't apply to a fully-qualified device path,
i.e. a path that's rooted in the local device object directory "//./"
or "//?/". For example, "//?/C:" is the volume device itself, which
opens as something similar to a Unix block-device file (e.g. you have
to read and write in multiples of the sector size), and "//?/C:/" is
the root mountpoint if the volume device is mounted.

You can use CMD's `mklink /j` command to create a mountpoint (aka a
junction), which can target any directory in a filesystem that's
mounted on any local device. This is basically equivalent to a Unix
"bind" mountpoint, though not as powerful. (A junction is not
equivalent to a Unix symlink. The parsing semantics and access rules
are different from symlinks. Windows has symlinks if that's what you
need.)

The Windows API has particular behavior for "volume mountpoints" that
target the filesystem root directory using the "Volume{GUID}" volume
device name. Such mountpoints are usually set via mountvol.exe or
diskmgmt.msc.  For example, the GetVolumePathNameW function looks for
volume mountpoints. They're more like regular Unix mountpoints, as
opposed to bind mountpoints. Though, in some respects, all mountpoints
except for the root path on a device are like Unix bind mountpoints.

A folder mountpoint is technically a filesystem reparse point with the
reparse tag IO_REPARSE_TAG_MOUNT_POINT. The system reparses to the
target path. Ultimately, all opens use the primary mountpoint at the
root path of a device. For example, if "C:/Mount/BackupDrive" is a
junction to "//?/Volume{GUID}/", then opening
"C:/Mount/BackupDrive/file" will reparse to opening
"//?/Volume{GUID}/file" and ultimately to
r"\Device\HarddiskVolume<N>\file", where "<N>" is its current volume
device number. The latter is not persistent across boots or for
removable drives that get swapped in and out of the system, which is
why we use the "Volume{GUID}" device name.

Symlinks are also implemented as reparse points, with the tag
IO_REPARSE_TAG_SYMBOLIC_LINK, but they have different parsing
behavior. Symlinks resolve to the target path while parsing a path,
whereas mountpoints are handled more like regular directories while a
path is being parsed, which is relevant when parsing traverses
relative symlinks. Also, remote symlinks are always evaluated locally
using local devices, whereas remote mountpoints are evaluated remotely
using the remote system's devices. Symlinks are also subject to the
L2L (local to local), L2R (local to remote), R2L, and R2R evaluation
policies. R2L should almost always be disallowed. The link target
either won't be resolved or it will resolve to a local path that's
unrelated to the intended target (e.g. "//server/share/symlink" ->
"C:/Users/johnsmith"). L2R and R2R can be enabled to allow symlinks to
target remote paths, which mountpoints can never target since they get
evaluated remotely. (If mountpoints allowed remote devices, consider
the problem of delegating the user's security context across a
succession of servers acting on behalf of the user.) Creating symlinks
also requires a user to have the symlink privilege, unless the system
is in developer mode.

> a drive letter like "F:" is not technically part of a path,

The primary mountpoint in Windows is the root path of the volume
device, so device names are an *integral* component of Windows file
paths. (This contrasts with Unix, where filesystems have to be mounted
on a directory, i.e. "/dev/sda1/spam" makes no sense in Unix.)  Names
such as "F:" and "Volume{GUID}" are persistent, registered names for
an automatic device name, but ultimately resolve to the base device
name. For example, opening "F:/spam" gets translated to the native NT
path r"\??\F:\spam" and passed to the kernel, which resolves "F:" to
the current path of the volume device, such as
r"\Device\HarddiskVolume10".

When opening r"\Device\HarddiskVolume10\spam", the object manager in
the kernel parses the path up to "HarddiskVolume10" device object. The
device-object type has its own parse routine set by the I/O manager.
This routine sends an IRP_MJ_CREATE request to the device with the
remaining path r"\spam". Since this is a mounted volume device, the
request goes to the filesystem device that mounted the volume device
instead of directly to the volume device. This is the case even if you
open the volume device directly, e.g. "//?/C:" (aka
r"\Device\HarddiskVolume2"), with no remaining path. The filesystem
device has complete control over access to the volume.

> Unfortunately, Windows shells have a working directory per drive letter
> and Python doesn't which can lead to some confusion when the paths are
> relative paths but a drive letter is also specified. Okay - I'll stop now.

For drives A:-Z:, per-drive working directories are set in
conventionally hidden environment variables such as "=C:" and "=Z:".
The Windows API automatically reads these environment variables, but
SetCurrentDirectoryW doesn't set them. It's up to each process to set
them. If they're not set, the API defaults to using the root path
(e.g. "Z:/").

Python's implementation of os.chdir() sets these per-drive variables,
and ntpath.abspath() is based on WinAPI GetFullPathNameW, which uses
them.  Thus Windows Python participates fully in the per-drive working
directory system.

By default, child processes inherit the parent's environment block, so
by default they inherit the parent's per-drive working directories.


More information about the Tutor mailing list