feat(client): add resilient sync primitives for large and DFS-backed shares - #4
feat(client): add resilient sync primitives for large and DFS-backed shares#4kalicyh wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new incremental, bounded-memory directory enumeration API (DirectoryReader) to the SMB2 client layer, integrating it into existing listing functionality while preserving cancellation safety for stateful QUERY_DIRECTORY enumeration.
Changes:
- Introduce
DirectoryReaderwithnext_batch()and explicitclose(); re-export it fromclientand crate root. - Add DFS-aware
SmbClient::open_directory_readerand refactorTree::list_directoryto collect via the new reader (single wire implementation). - Expand documentation and benchmarks notes; add unit tests covering batching, EOF close, early close, and cancellation-resume behavior.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/lib.rs | Re-exports DirectoryReader from the crate root. |
| src/client/tree.rs | Implements DirectoryReader, refactors list_directory to use it, and adds focused mock-transport tests. |
| src/client/mod.rs | Exposes DFS-aware SmbClient::open_directory_reader and re-exports DirectoryReader. |
| src/client/CLAUDE.md | Documents the new DirectoryReader design and cancellation-safety invariant. |
| README.md | Adds the new API to the “one thing and get the result” list. |
| docs/benchmark-findings.md | Updates listing compounding notes to reflect the “no last batch marker” constraint. |
| CHANGELOG.md | Adds an Unreleased entry describing incremental directory enumeration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
088be61 to
cdc2a75
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/client/tree.rs:583
Tree::list_directorynow callsopen_directory_reader, which already normalizes the path and emits a TRACE log. The additionaltrace!("tree: list_directory path=...", self.format_path(path))both duplicates log volume and forces a second path normalization/allocation (including DFS prefix formatting) for every listing when TRACE is enabled.
// TRACE, not DEBUG: a recursive scan calls list_directory once per directory
// (millions of times on a large share), so at DEBUG it dominates a consumer's
// log. Per-operation mutations (rename/delete/write) stay at DEBUG. See AGENTS.md.
trace!("tree: list_directory path={}", self.format_path(path));
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
fae5bf9 to
40e5bb7
Compare
40e5bb7 to
d874fba
Compare
Bind mutable handles, directory cursors, compounds, and routed DFS trees to the session that allocated their IDs. Validate resume targets on the retained handle, clean up cascaded stat closes, use the compact directory layout with compatibility fallback, and keep credit refunds in their original connection generation.
d874fba to
7663ab1
Compare
Summary
This remains one integrated PR for long-running SMB sync and backup workflows. It adds the primitives needed to enumerate large directories incrementally, identify and mutate the exact remote object, checkpoint streamed writes safely, and keep owned handles pinned to the correct DFS target and SMB session.
What changes
Incremental directory enumeration
Adds
DirectoryReader, exposed throughTreeandSmbClient.next_batch()returns one serverQUERY_DIRECTORYresponse at a time;list_directory()collects the same reader internally.close().SessionId; staleTreeId/FileIdvalues are never reused after reconnect.TreeId, avoiding a clonedTreeand its strings.FileIdFullDirectoryInformationlayout. Servers that returnSTATUS_NOT_SUPPORTEDorSTATUS_INVALID_INFO_CLASSare restarted on the same open handle withFileBothDirectoryInformation.Stable identity and exact mutations
DirectoryEntryexposeschangedand optionalfile_index.FileInfoexposeschangedand optionalFileIdentity; stable identity requires both a non-zero file index and its volume serial.RenameOptionsmaps directly to the server's atomicReplaceIfExistsbehavior.MutationHandleobtains metadata and renames or deletes through the same openFileId, avoiding a path-based stat-to-mutation race.TreeIdorFileIdinto the replacement session.statissue a standalone CLOSE instead of leaking the successful CREATE handle.Durable writer checkpoints and safe resume
Adds
FileWriter::flush_checkpoint().A successful checkpoint sends all accepted data, drains outstanding WRITEs, issues SMB FLUSH, and returns the absolute durable file offset without closing the handle. The durable offset advances only after FLUSH succeeds; cancellation keeps accepted data owned by the writer, and short WRITE responses are rejected.
open_existing_file_writer_at()now requires the checkpoint's expectedFileIdentity. A compoundedFileOpenplus twoQUERY_INFOoperations validates identity and CREATE's currentEndOfFileon the exact handle retained for writing. Missing, replaced, identity-unavailable, or truncated targets are closed and rejected before any WRITE.DFS and reconnect safety
(server, share, connection generation, SessionId).Credential handling
ClientConfigand Kerberos credentialDebugoutput.zeroizedependency.Public API and compatibility
New public API includes
DirectoryReader,FileIdentity,RenameOptions,MutationHandle,FileWriter::flush_checkpoint(), safe existing-file positioned writer opens, and DFS-routed owned-handle opens.Source-compatibility notes:
DirectoryEntrygainschangedandfile_index.FileInfogainschangedandidentity.#[non_exhaustive].open_existing_file_writer_at()requires an expectedFileIdentity.Drop, so callers must borrow or clone individual public fields instead of moving them out.statremains one normal wire round trip but expands from four to six compound operations; the two identity queries are optional.FileIdFullDirectoryInformationand has aFileBothDirectoryInformationfallback.zeroizeis a new runtime dependency.Existing
list_directory,rename, andFileWriter::finishentry points remain available.Validation
Current head:
7663ab1(rebased onto upstream v0.18.1,b5baae4)justpasses locally: formatting, Clippy (regular and all features), tests (regular and all features), and documentation.cargo clippy --all-targets --all-features -- -D warningspasses.Changelog
No
CHANGELOG.mdentry is included; release-note wording is left to the maintainer.