**Stack 1/5** — base `master`. Part of the #4096 split; see #4096 for
the overall picture.
## What this is
The core stack changes the Robotics, OpenUSD and Positioning work
depends on. No new projects; every change is to an existing one, so this
is reviewable without any of the companion specs.
## Contents
**`Opc.Ua.Server`**
- `FileSystem` gains a reusable `IFileDirectoryBinder` and
`IFileSystemHost`, so any node manager can serve a Part 5
`FileDirectoryType` over `IFileSystemProvider` instead of that binding
living inside one companion model.
- Entry names are validated before they reach the provider. The contract
promises forward-slash provider-relative paths but never enforced it, so
a backslash payload such as `..\..\..\Windows\System32\evil.txt`
bypassed the obvious provider-side check on Windows. Defence in depth —
the shipped `PhysicalFileSystemProvider` was already safe.
- `Location` adds a single `IGeoLocationProvider` seam so one provider
implementation serves both OPC 10000-211 GPOS and OPC 10030 ISA-95. The
two specs disagree on representation, not on content, so the difference
is a projection rather than a reason to write the provider twice. Ships
a WKT text formatter and an in-memory provider.
- `StateMachines` adds `FiniteStateMachineDispatcher`, the Part 16
transition guard used by DI software update and later by Robotics task
control.
**`Opc.Ua.Types`**
- Node copies no longer consume NodeIds from the `NodeIdFactory`.
`Initialize` materialises children through `CreateChild`, which assigns
a per-instance NodeId, but the copy then overwrites every child NodeId
from its source, so the assignment is discarded. A counting factory
silently burned identifiers; a tracking one leaked them.
- `JsonDecoder` resolves a relative encodeable type id against the
namespace table before the factory lookup.
**`Opc.Ua.Client`** — a file system path segment written without a
namespace prefix is qualified with the namespace of the parent it is
resolved against, matching the provider-owned BrowseNames.
**`Directory.Build.targets`** — the `RestrictForLegacyTfm` no-op now
also applies when a project pins a single `TargetFramework` and
`CustomTestTarget` names a different one. That is the same situation it
already handled for the legacy frameworks, and it is what keeps the
per-TFM CI matrix green once net10.0-only projects appear later in the
stack.
## Validation
`dotnet build UA.slnx` — 0 errors.
| Suite | Result |
| --- | --- |
| Opc.Ua.Types.Tests | 8088 / 0 |
| Opc.Ua.Server.Tests | 3873 / 1 |
| Opc.Ua.Client.Tests | 2105 / 0 |
| Opc.Ua.Core.Tests | 4145 / 0 |
| Opc.Ua.Features.Tests | 134 / 0 |
The single `Opc.Ua.Server.Tests` failure is
`ConfigureApplicationBuildsSharedClientAndServerConfigurationAsync`,
which is pre-existing and environmental: the PKI path in my working
directory exceeds MAX_PATH. It reproduces on clean `master` and passes
from a shorter directory.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8cbb8cd0-f0cb-4ab0-bea2-6202fbf69485
# Description
This focused draft extracts the FileType handle ownership fix from
#4021. It contains only the server FileSystem handle implementation,
focused tests, and FileSystem client documentation.
## Failure
`FileHandle` issued predictable per-file identifiers without recording
the owning Session. A different Session that obtained a handle could
read, write, seek, or close the stream, and open streams survived
Session closure.
## Fix
- Generate non-zero opaque handles and bind every open stream to the
Session that called `Open`.
- Require a valid Session for `FileType.Open`, `CreateFile` when it
requests an open handle, and all subsequent handle operations.
- Reject a handle presented by another Session.
- Close only the departing Session's handles from
`FileSystemNodeManager.SessionClosingAsync`.
- Document the Session ownership and lifetime.
OPC 10000-5 Section 6.2.5.2, Table 27 states that a `fileHandle` is
valid only for the Session used to call `Open` and that the Server shall
automatically close the file when that Session closes.
## Tests
- `dotnet test tests\Opc.Ua.Server.Tests\Opc.Ua.Server.Tests.csproj -f
net10.0 --filter
"FullyQualifiedName~FileHandleTests|FullyQualifiedName~FileObjectStateTests|FullyQualifiedName~DirectoryObjectStateTests"
--no-restore`: 71 passed.
- `dotnet test tests\Opc.Ua.Server.Tests\Opc.Ua.Server.Tests.csproj -f
net48 --filter
"FullyQualifiedName~FileHandleTests|FullyQualifiedName~FileObjectStateTests|FullyQualifiedName~DirectoryObjectStateTests"
--no-restore`: 71 passed.
Both builds emitted the same existing CA1873 warnings in unchanged
`Opc.Ua.Client` files; the changed files produced no warnings.
## Related Issues
- Focused split from #4021 (closed umbrella draft); no separate issue.
## Checklist
- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>