Skip to content

Moves inside a DFS link fail across folders, and bulk copies pay a refused request per file #21

Open

Description

@vdavid

Context

Since #15, every path-taking SmbClient call follows a DFS link inside a share. The flow per call: send the CREATE, get STATUS_PATH_NOT_COVERED, look up the referral (from the cache after the first time), and run the op again on the target.

Problem

Two gaps remain:

  • Renames across directories through a link fail. rename / rename_files move the destination along only when source and destination sit in one directory. Otherwise they return STATUS_PATH_NOT_COVERED. A move from data/a/x.txt to data/b/x.txt, with data a link, fails on a share where a plain move would work.
  • Every linked item pays a refused CREATE. Batch and streaming calls send one CREATE per item that the server refuses before the redirect, even when the referral cache already knows the path is behind a link. A 1,000-file copy out of a link costs 1,000 extra round trips (about 1–10 s on a LAN, far more over a VPN).

Solution

  • Rename: pass the referral's consumed prefix through to the op, so the destination can be rewritten the same way as the source whenever both sit under the same link, and keep STATUS_PATH_NOT_COVERED only when they don't (a cross-link move, which SMB can't do in one call anyway).
  • Pre-check: before sending, look the path up in the referral cache (MS-DFSC § 3.1.4.1 describes this client-side check), and go straight to the target when it hits. Keep the refused-CREATE path as the fallback for a cold cache or an expired entry.

For the implementer

  • SmbClient::follow_dfs_link, redirect, and link_rename_target in crates/smb2/src/client/mod.rs; DfsResolver in client/dfs.rs; the link-tree cache in client/connections.rs (link_tree).
  • Tests: a mock test that a cached link sends no refused CREATE; a Docker test on smb-dfs-root for a cross-directory rename under the link.
  • Watch the TTL: a cached referral past its TTL must fall back to the refused-CREATE path, not reuse the stale target (see the TTL refresh issue, Refresh DFS referrals in the background before they expire #16).

After #20

  • Rename: redirect now returns a Link carrying the referral depth and the target folder offset, which is what a cross-directory rename needs. What's missing is getting it into the op before it sends (the op only sees (conn, tree, remaining)): pass the Link in, or split rename into a redirect step and the SET_INFO. link_rename_target can then go.
  • Pre-check: since the caller's tree no longer moves, every repeat call through a link pays the refused CREATE, single-file calls included, so the pre-check is worth a bit more. It can reuse redirect and Link as they are.

Impact: Moves inside a DFS link work, and bulk copies out of a link skip one round trip per file · Severity: Low (moves fail with a clear error, and the extra round trips only cost speed) · Size: M (plumbing a prefix through, plus a cache pre-check)

Found while implementing #15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions