5.9.3 Restore
REG_IOC_RESTORE replaces the key on the fd and its entire subtree
from a stream. It is not a merge: the target's contents and descendants
are torn down before the stream's contents are written.
It requires SeRestorePrivilege and, like backup, performs no per-key
AccessCheck and is audited unconditionally. The input fd must be
readable, or EBADF, and restoring onto an orphaned key is ENOENT.
Because restore rewrites every descriptor in the subtree,
SeRestorePrivilege effectively confers WRITE_DAC and WRITE_OWNER
over everything within reach of one (§5.8.1).
5.9.3.1 One transaction #
The entire restore — teardown and rebuild together — is wrapped in a
single read-write source transaction. A source that answers
RSI_TXN_NOT_SUPPORTED for RSI_TXN_READ_WRITE cannot be a restore
target: restore requires atomicity and there is no partial-restore
mode. Every failure path aborts the transaction, so a failed restore
rolls back the teardown as well.
5.9.3.2 The target key survives #
The stream's header names a root GUID, and that GUID is remapped to the already-open target key everywhere it appears — in parent references, in child references, in value key references — before anything is validated or dispatched.
The target key object is not replaced. Its GUID, parent, name,
volatile flag and symlink flag remain what they were; they are never
taken from the stream. What the stream's root record supplies is the
mutable part: the Security Descriptor and the last write time, written
to the target with RSI_WRITE_KEY inside the transaction.
The root record's immutable flags must match the target's. A backup
of a volatile key restored onto a non-volatile one, or a symlink onto a
non-symlink, is EINVAL.
Descendants keep their backup GUIDs. Those are written into the target verbatim.
5.9.3.3 Order of operations #
- Validate the whole stream, including the trailer's record count and checksum, and retain every replayable record.
- Read the layer manifest and apply the precedence gate (below).
- Verify the root record and its immutable flags against the live target.
- Tear down the target's contents and descendants — path entries, values, blanket tombstones, and descendant key records — inside the transaction.
- Write the root's mutable fields, then replay its section's values and blanket tombstones.
- For each non-root key in stream order: create it, write its last write time, then replay its path entries, values and blanket tombstones.
- Commit.
The stream is read from the fd exactly once, sequentially. Nothing seeks, so a pipe is a valid input.
Validating the whole stream first is stronger than the format requires: a checksum failure aborts before any source mutation rather than after some. The cost is memory rather than seeking — the replayable records are retained in kernel memory across the teardown.
5.9.3.4 The precedence gate #
Before any key record is written, LCS checks the layer manifest. If any
declared layer has a precedence above 0, or any existing cached
layer table entry with the same folded identity does, and the caller
does not hold SeTcbPrivilege, the restore aborts with EPERM before
a single byte reaches the source.
And if the stream contains ordinary key and value records for
Machine\System\Registry\Layers\<Name>\, writes that create or raise
persisted metadata above precedence 0 hit the ordinary inline
SeTcbPrivilege check as well (§5.3.4).
Both exist so that SeRestorePrivilege cannot be used to smuggle a
Group Policy-tier layer past the defence in depth that guards
precedence.
5.9.3.5 Layers in a restored stream #
Manifest records create, update, delete, enable, disable and authorise nothing. If restored entries reference a layer that is not in the current table, and the stream does not also restore that layer's metadata subtree as ordinary registry data, those entries become latent unknown-layer entries and are ignored during resolution until real metadata exists (§5.3.6). If the metadata subtree is included, it is restored through the ordinary path, and those records — not the manifest — are what define the layer.
5.9.3.6 Sequence remapping #
Backup sequence numbers preserve the backup's internal ordering, but a restore is a new mutation and its entries must outrank everything already present. So the numbers are remapped rather than written through.
Before dispatching the first layer-qualified record, LCS takes the
global sequence-allocation gate and records the current next sequence
as the offset. Every restored record is then written with
offset + backup_sequence, which preserves relative order while
placing the whole set above pre-restore state.
The gate is held until the restore reaches a terminal state. Other sequence-allocating mutations wait; reads are not blocked. A restore with no layer-qualified records at all never takes it.
If a remapped value would overflow, the restore fails EOVERFLOW — and
it fails at validation, before teardown, rather than part-way through.
The valid remapped range stops just below U64_MAX, which is never
handed out (§5.3.7).
When the restore reaches any terminal state — commit, abort, failure or cancellation — LCS advances the global counter past the highest number it dispatched, and does not roll that back. Sequence numbers a failed restore dispatched become unused gaps, exactly like those of any other failed write.
5.9.3.7 GUID collisions #
A GUID that appears twice in one stream, other than the root, is
EINVAL, and so is a non-root GUID equal to the target root's.
A non-root GUID that already exists outside the subtree being
replaced is EEXIST — but it is discovered by the source rejecting the
create during replay, not by a check beforehand. LCS has no index of
which GUIDs exist elsewhere, so the collision surfaces mid-restore, and
the transaction rolls back.
The parent of every path entry, after remapping, must be either the restore root or a key record already processed earlier in the stream. That check is made up front, and it is what stops a crafted backup injecting path entries into arbitrary parts of the existing namespace outside the subtree being replaced.
5.9.3.8 Watches #
A restore is an arbitrary subtree replacement and LCS retains no exact
before-and-after diff for it. On a successful commit it publishes the
affected hive's generation increment and dispatches a no-name
OVERFLOW to the armed watches on that source (§5.6.3). A restore that
fails or aborts before commit emits nothing.