5.2.8 Names and Case
Every string in the LCS interface — key names, value names, hive names,
layer names, paths — is UTF-8. Invalid UTF-8 is rejected with EINVAL
before parsing, routing, folding, layer resolution or source
dispatch.
Null bytes are rejected in all of them.
The rejection is EINVAL where the string is length-delimited — ioctl
and RSI strings — because there the null byte sits inside the counted
length. A syscall argument (a path or reg_create_key's layer name) is
a C string, and a null byte there is its terminator: the name the
kernel validates ends at it, and nothing after it is read.
The one thing that is not a string is value data, which is opaque bytes.
5.2.8.1 Lengths are byte counts #
Every configured length limit is measured in UTF-8 bytes, not Unicode scalar values and not display characters.
MaxPathComponentLength(default 255) bounds one component or one value or layer name.MaxTotalPathLength(default 16383) bounds a whole path.MaxKeyDepth(default 512) bounds nesting.
A syscall path arrives as a null-terminated C string, is copied under a hard bound, and has its terminator stripped before anything is measured — so the terminator is not part of the length.
Ioctl and RSI strings are length-delimited and need no terminator; a terminator byte included in the length is a null byte and is therefore invalid.
5.2.8.2 Separators #
Backslash is canonical. Forward slash is accepted on input and treated as a separator wherever a separator is recognised, so a component can never contain either.
There is no string-rewriting step: normalisation is a property of how paths are split rather than a transformation applied to them.
5.2.8.3 Case folding #
Comparison is case-insensitive and storage is case-preserving.
The algorithm is Unicode Simple Case Folding — the C and S
status entries of CaseFolding.txt, with the full (F) and Turkic
(T) entries excluded.
It is a fixed one-to-one codepoint mapping with no locale input, applied after decoding from UTF-8, never to raw bytes.
The Unicode version is pinned at 16.0. The table is generated by
pkm/tools/lcs/generate_casefold_table.py and checked in with the
digest of the source data, so adopting a newer Unicode version means
regenerating the table deliberately. It is not something that happens
by updating a dependency.
This gives practical compatibility with Windows'
RtlCompareUnicodeString without claiming byte-identical behaviour in
every edge case.
Unicode normalisation is not performed. NFC and NFD forms of the same visual character are different names, matching Windows.
Case folding is what "identity" means throughout: a layer's identity is
its folded name, a hive route's identity includes its folded name, and
a duplicate is a folded-equal duplicate. RoleA and rolea are one
layer, not two.
Every comparison folds, on both sides of the kernel boundary. The
Kernel Access Control Subsystem (KACS) parses private layer names into
a token and rejects a duplicate with EINVAL; that duplicate check
uses the same folding routine and the same table as LCS, so a pair
that LCS treats as one layer cannot occupy two of a token's slots.
There were two ASCII-only comparators, the base check on two of its
call sites and this KACS check, and both are gone (§5.3.2 for
base).