Now that the DFS client can follow link referrals, I wanted to write down where
things stand and propose an order for the rest, so the shape can be agreed before
any of it is written.
Everything described as working or failing below was measured against a lab rather
than inferred from the spec: a Windows Server 2022 domain controller, a separate
file server hosting both a namespace root and a link target, and two namespaces —
one standalone (\\SERVER\Files) and one domain-based (\\lab.local\Namespace).
What works
Verified end to end, including when the link target is on a different machine:
TreeConnect to a namespace root detects SMB2_SHAREFLAG_DFS_ROOT and returns a
DFS-aware store.
- A path crossing a link produces
STATUS_PATH_NOT_COVERED, the client requests
FSCTL_DFS_GET_REFERRALS, connects to the referral target, re-authenticates,
tree connects and retries.
- Read, write and directory enumeration through a link all succeed.
- Multiple referral targets are tried in order, so an unreachable first target
fails over to the next.
What doesn't
1. Domain-based namespaces cannot be connected at all (#355)
Against current master:
Connect("lab.local") -> true
Login(...) -> STATUS_SUCCESS
TreeConnect("Namespace") -> STATUS_BAD_NETWORK_NAME
Windows' own client traverses \\lab.local\Namespace\Link successfully at the same
moment.
The part I think matters most for deciding scope: connect and login both
succeed. The session to the domain controller is established and authenticated.
The failure is that TreeConnect then asks that DC for a share it does not host,
because a domain-based namespace exists only as a root referral.
That rules out the cheaper fix I first assumed would be enough. A name-resolution
hook does not help — the name resolves fine, and the caller would still have no way
to learn which server hosts the namespace. That is exactly what a root referral
returns, and it is FSCTL_DFS_GET_REFERRALS over the SMB session we already have:
no LDAP, no RPC, nothing domain-controller specific. DfsReferralHelper is public
and already does it.
Impact is high, since domain-based namespaces are the normal deployment form. The
only workaround is to hard-code a specific namespace server, which throws away the
availability the namespace exists to provide.
2. Referrals are never cached
A fresh referral is requested on every CREATE that returns
STATUS_PATH_NOT_COVERED, and DfsReferralEntry.TimeToLive is parsed but never
honoured. This is a load and latency concern rather than a correctness one, and I
think it should stay deferred until the correctness work is done.
3. Only V3 and V4 referral entries are consumed
The target loop matches DfsReferralEntryV3, which also catches V4 through
inheritance. V1 and V2 entries are parsed by the codec and then silently skipped.
Modern Windows returns V4 so this has not caused a problem, but a silent skip
against an older or third-party namespace server would be hard to diagnose.
4. The SMB1 client has no DFS support
The SMB1 referral messages exist but SMB1Client never uses them. Very possibly
the right call given SMB1's status — flagging it so it is a decision rather than an
oversight.
A note on testing
TreeConnectHelper never emits ShareFlags.DfsRoot, so SMBLibrary's own server
cannot host a namespace. No in-process server test can produce a DFS root to
connect to. That is why the DFS tests are unit tests over fakes, and why anything
end-to-end has to be environment-gated against a real Windows namespace. Worth
recording as a constraint rather than rediscovering it each review.
Proposed order
Each step is independently useful and independently reviewable, and none of them
need the full MS-DFSC client model.
1. Document the manual root referral (docs only). A ClientExamples.md entry
showing how a caller resolves a domain-based namespace today with existing public
API: connect to the domain, TreeConnect("IPC$"), GetDfsReferral for the
namespace path, then connect to the returned namespace server. No code change, it
unblocks people hitting #355 immediately, and it makes the mechanism concrete
before we discuss automating it.
2. Resolve the root referral inside TreeConnect (closes #355). On
STATUS_BAD_NETWORK_NAME, request a root referral for \\<server>\<share>; if one
comes back, connect to a returned namespace server and retry there. Structurally
the same shape as the link referral that already works — a distinguished status, a
referral, a retry — and it reuses DfsReferralHelper and DfsPath. It would live
behind SMBLibrary.Client.DFS so SMB2Client does not grow DFS logic.
One design question I would rather settle before writing it: automatic on
STATUS_BAD_NETWORK_NAME, or opt-in via an explicit call? I lean automatic, since
the failure is otherwise unrecoverable and the extra round trip only happens on a
path that has already failed. But it does add a second connection attempt to a
previously terminal error, so I am happy to go the other way if you would prefer.
3. Honour referral TTL and cache. Only after step 2. Keyed by path prefix,
shared across the stores of one client.
4. Small follow-ups. Consume V1/V2 entries or fail loudly instead of skipping;
decide explicitly whether SMB1 DFS is in or out.
Happy to start at step 1, or to reorder if you see it differently.
Now that the DFS client can follow link referrals, I wanted to write down where
things stand and propose an order for the rest, so the shape can be agreed before
any of it is written.
Everything described as working or failing below was measured against a lab rather
than inferred from the spec: a Windows Server 2022 domain controller, a separate
file server hosting both a namespace root and a link target, and two namespaces —
one standalone (
\\SERVER\Files) and one domain-based (\\lab.local\Namespace).What works
Verified end to end, including when the link target is on a different machine:
TreeConnectto a namespace root detectsSMB2_SHAREFLAG_DFS_ROOTand returns aDFS-aware store.
STATUS_PATH_NOT_COVERED, the client requestsFSCTL_DFS_GET_REFERRALS, connects to the referral target, re-authenticates,tree connects and retries.
fails over to the next.
What doesn't
1. Domain-based namespaces cannot be connected at all (#355)
Against current master:
Windows' own client traverses
\\lab.local\Namespace\Linksuccessfully at the samemoment.
The part I think matters most for deciding scope: connect and login both
succeed. The session to the domain controller is established and authenticated.
The failure is that
TreeConnectthen asks that DC for a share it does not host,because a domain-based namespace exists only as a root referral.
That rules out the cheaper fix I first assumed would be enough. A name-resolution
hook does not help — the name resolves fine, and the caller would still have no way
to learn which server hosts the namespace. That is exactly what a root referral
returns, and it is
FSCTL_DFS_GET_REFERRALSover the SMB session we already have:no LDAP, no RPC, nothing domain-controller specific.
DfsReferralHelperis publicand already does it.
Impact is high, since domain-based namespaces are the normal deployment form. The
only workaround is to hard-code a specific namespace server, which throws away the
availability the namespace exists to provide.
2. Referrals are never cached
A fresh referral is requested on every
CREATEthat returnsSTATUS_PATH_NOT_COVERED, andDfsReferralEntry.TimeToLiveis parsed but neverhonoured. This is a load and latency concern rather than a correctness one, and I
think it should stay deferred until the correctness work is done.
3. Only V3 and V4 referral entries are consumed
The target loop matches
DfsReferralEntryV3, which also catches V4 throughinheritance. V1 and V2 entries are parsed by the codec and then silently skipped.
Modern Windows returns V4 so this has not caused a problem, but a silent skip
against an older or third-party namespace server would be hard to diagnose.
4. The SMB1 client has no DFS support
The SMB1 referral messages exist but
SMB1Clientnever uses them. Very possiblythe right call given SMB1's status — flagging it so it is a decision rather than an
oversight.
A note on testing
TreeConnectHelpernever emitsShareFlags.DfsRoot, so SMBLibrary's own servercannot host a namespace. No in-process server test can produce a DFS root to
connect to. That is why the DFS tests are unit tests over fakes, and why anything
end-to-end has to be environment-gated against a real Windows namespace. Worth
recording as a constraint rather than rediscovering it each review.
Proposed order
Each step is independently useful and independently reviewable, and none of them
need the full MS-DFSC client model.
1. Document the manual root referral (docs only). A
ClientExamples.mdentryshowing how a caller resolves a domain-based namespace today with existing public
API: connect to the domain,
TreeConnect("IPC$"),GetDfsReferralfor thenamespace path, then connect to the returned namespace server. No code change, it
unblocks people hitting #355 immediately, and it makes the mechanism concrete
before we discuss automating it.
2. Resolve the root referral inside
TreeConnect(closes #355). OnSTATUS_BAD_NETWORK_NAME, request a root referral for\\<server>\<share>; if onecomes back, connect to a returned namespace server and retry there. Structurally
the same shape as the link referral that already works — a distinguished status, a
referral, a retry — and it reuses
DfsReferralHelperandDfsPath. It would livebehind
SMBLibrary.Client.DFSsoSMB2Clientdoes not grow DFS logic.One design question I would rather settle before writing it: automatic on
STATUS_BAD_NETWORK_NAME, or opt-in via an explicit call? I lean automatic, sincethe failure is otherwise unrecoverable and the extra round trip only happens on a
path that has already failed. But it does add a second connection attempt to a
previously terminal error, so I am happy to go the other way if you would prefer.
3. Honour referral TTL and cache. Only after step 2. Keyed by path prefix,
shared across the stores of one client.
4. Small follow-ups. Consume V1/V2 entries or fail loudly instead of skipping;
decide explicitly whether SMB1 DFS is in or out.
Happy to start at step 1, or to reorder if you see it differently.