Skip to content

fix: do not fail on sole directory inputs beyond MAX_PATH on Windows - #411

Merged
Byron merged 2 commits into
Byron:mainfrom
Mathjk:fix/long-path-single-dir
Sep 30, 2026
Merged

Byron merged 2 commits into
Byron:mainfrom
Mathjk:fix/long-path-single-dir

Conversation

@Mathjk

@Mathjk Mathjk commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Fixes #410.

Bug

For a sole directory input, extract_paths_maybe_set_cwd / extract_aggregate_inputs_maybe_set_cwd call set_current_dir(path) so entries can be reported relative to it. On Windows, SetCurrentDirectoryW enforces MAX_PATH (os error 206) — even for verbatim paths — although the directory itself is perfectly traversable. So dua <dir> fails whenever the single dir arg is ≥259 chars, in every mode (list, aggregate, i, stacks). Measured boundary: 258 chars OK, 259 fails; a 277-char file arg and multi-arg dirs work fine.

Fix (+97/-19, src/main.rs only)

When set_current_dir on a sole dir fails, expand the directory into absolute input paths via a new dirlist() helper (plain fs::read_dir handles long absolute paths because std verbatim-converts them, and dua-lib's walker handles them since #407). The aggregate fast path delegates to extract_paths_maybe_set_cwd on chdir failure — one fallback implementation, no duplication.

  • Successful chdir → byte-identical behavior (no-arg/no-dir-arg cases unchanged).
  • expanded_root is returned so interactive mode's root_path stays correct.
  • Ignore/-i checks resolve against base_dir (expanded root or cwd) — same semantics.
  • Fallback shows absolute paths for the previously-crashing case — consistent with how multiple inputs are already displayed.

Verified on real Windows

Input Before After
dua <259-char dir> Error: 文件名或扩展名太长。 (os error 206), exit 1 16 B D:\...\f.bin, exit 0
dua <258-char dir> works, relative display unchanged
file arg 277 chars, multi-arg, relative, no-arg work unchanged
stacks / aggregate -d1 / i crash at 259 work (absolute display)

Tests

New #[cfg(windows)] test sole_directory_beyond_max_path_expands_into_absolute_inputs: builds a >MAX_PATH tree via \\?\ path, asserts the dir expands to absolute inputs without changing cwd. Passes on Windows via cross-compiled test exe. cargo test 147/147 on Linux; cargo fmt --check + clippy clean.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The fallback changes descendant ignore-pattern matching because the expanded root is not propagated to traversal callers.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds a Windows long-path fallback for sole-directory inputs.

Changes:

  • Expands directories into absolute child paths when changing directory fails.
  • Preserves the expanded root for interactive mode.
  • Adds a Windows regression test.
File Description
src/​main.rs Implements fallback path expansion and testing.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/main.rs
let cwd_device = device_id(&cwd).ok();
// With an expanded root, the working directory is unchanged, so its entries are
// resolved against the root instead for the ignore checks below.
let base_dir = expanded_root.as_deref().unwrap_or(&cwd);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - fixed in the amended commit. The expanded directory is now recorded in a new WalkOptions::base_dir field: extract_paths_maybe_set_cwd sets it when it expands the sole input directory (the extract_aggregate_inputs_maybe_set_cwd delegation included), and iter_from_paths uses it in place of std::env::current_dir() as the base for ignore-pattern matching. Since aggregate, aggregate_tree, stacks and the interactive app (including refreshes and parent scans) all walk through iter_from_paths, descendants now resolve relative to the expanded root exactly as they would have after a successful chdir - e.g. subdir/ignored again excludes root/subdir/ignored. Verified on Windows against a 266-char root: --ignore-from output is now byte-for-byte identical to the equivalent short-path tree. Added base_dir_anchors_ignore_patterns_of_expanded_roots as a regression test.

A sole directory input is changed into so its entries can be listed and
reported relative to it, but `set_current_dir` enforces `MAX_PATH` on
Windows (os error 206) even though the directory itself can still be
traversed through verbatim paths.

When the change fails, expand the directory into absolute input paths
directly instead of aborting, which makes a single long directory input
work in list, aggregate, stacks and interactive modes alike. Reported
paths are absolute then, consistent with how multiple inputs are already
displayed.

The expanded root is returned to callers so the interactive mode still
knows which directory the inputs belong to, and recorded in the new
`WalkOptions::base_dir`, which every traversal uses as the working
directory stand-in for ignore-pattern matching. That keeps `--ignore-from`
patterns matching descendants relative to the expanded root - identical to
what a successful `chdir` would have produced.

Fixes Byron#410
@Mathjk
Mathjk force-pushed the fix/long-path-single-dir branch from deb527e to 2fbb8f5 Compare September 29, 2026 21:36
<!-- agent -->
- [P1] Use the expanded root for interactive refreshes — /Users/byron/dev/github.com/Byron/dua-cli.fix-long-path-single-dir/src/main.rs:223-225
  When changing directory fails and the expanded directory has at most one entry, pressing `R` at the initial view scans the launch directory instead. `AppState::refresh` resolves the still-empty synthetic tree root to `.` unless `root_paths.len() > 1`; neither `root_path` nor `base_dir` is consulted, so unrelated files replace the requested listing. Make interactive path resolution use the expanded root. The same omission also points the initial title and Git-ignore discovery at the wrong directory.

- [P2] Measure the Windows test path in UTF-16 code units — /Users/byron/dev/github.com/Byron/dua-cli.fix-long-path-single-dir/src/main.rs:1318-1320
  With a sufficiently non-ASCII temporary-directory path, such as one containing a CJK username, `OsStr::len()` can exceed 270 bytes while the path remains below `MAX_PATH` in UTF-16 code units. The directory change then succeeds, failing the unchanged-CWD assertion and potentially leaving other tests with a changed working directory. Count `encode_wide()` units when extending the fixture so it reliably exercises the fallback.

Assisted-by: GPT 6.0 Astra
Co-authored-by: GPT 6.0 Astra <codex@openai.com>
@Byron
Byron merged commit 9a854d2 into Byron:main Sep 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: single directory input of 259+ chars fails with 'os error 206'

3 participants