Skip to content

fix: add long path prefix for Win32 file APIs - #816

Open
pppanghu77 wants to merge 1 commit into
linuxdeepin:release/v20from
pppanghu77:agent/agent/21482436c0e8
Open

pppanghu77 wants to merge 1 commit into
linuxdeepin:release/v20from
pppanghu77:agent/agent/21482436c0e8

Conversation

@pppanghu77

@pppanghu77 pppanghu77 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Root Cause Analysis

Win32 file APIs in 3rdparty/coost/src/fs_win.cc (GetFileAttributesW, CreateFileW, FindFirstFileW, etc.) lack the \\?\ long path prefix, so paths exceeding MAX_PATH (260 chars) cause API failures. This makes fs::exists() return false for long paths, triggering transfer cancellation and a misleading "network error" message. The Wide API migration in commit e3df6d62 fixed non-ASCII path support but did not address the long path limitation.

修复方案

Added toLongPath() helper function that normalizes paths (forward→back slash, resolves ./.. components) and prepends \\?\ prefix (or \\?\UNC\ for UNC paths). All charToWideChar() calls feeding Wide Win32 APIs are wrapped with toLongPath(). This is the adopted plan ① from root cause review — direct fix at the root cause layer.

Change Safety Assessment

Code Safety

  • Risk Level: Low
  • New internal helper function toLongPath(), no public API changes, no function signature modifications
  • All 16 call sites in fs_win.cc are internal; \\?\ prefix is transparent to Windows for paths within MAX_PATH
  • Does not revert the Wide API fix from commit e3df6d62 — long path prefix is additive on top of Wide API

Business Impact Scope

Affected: file transfer (data migration tool) — all file system operations (exists, isdir, fsize, mkdir, remove, rename, open, dir listing) now support paths beyond 260 characters. Users selecting folders with long file names for transfer will no longer encounter spurious "network error" failures.

Verification Suggestion

Test file/folder transfer with paths exceeding 260 characters; verify normal-length path operations remain unchanged; test UNC network path transfer with long paths.


根因分析

3rdparty/coost/src/fs_win.cc 中的 Win32 文件 API(GetFileAttributesW、CreateFileW、FindFirstFileW 等)未添加 \\?\ 长路径前缀,路径超过 MAX_PATH(260 字符)限制导致 API 调用失败。这使 fs::exists() 对长路径返回 false,触发传输取消并显示误导性的"网络错误"提示。commit e3df6d62 的 Wide API 迁移修复了非 ASCII 路径问题,但未涉及长路径限制。

修复方案

新增 toLongPath() 辅助函数,规范化路径(正斜杠转反斜杠、消除 ./.. 组件)并添加 \\?\ 前缀(UNC 路径转为 \\?\UNC\)。所有传入 Wide Win32 API 的 charToWideChar() 调用均用 toLongPath() 包裹。此为根因复核采纳的方案①——根因层直接修复。

改动安全评估

代码安全评估

  • 风险等级: 低风险
  • 新增内部辅助函数 toLongPath(),无公开 API 变更,无函数签名修改
  • fs_win.cc 内 16 个调用点均为内部调用;\\?\ 前缀对 260 字符以内的短路径透明
  • 不撤销 commit e3df6d62 的 Wide API 修复——长路径前缀在 Wide API 之上叠加

业务影响范围

受影响:文件传输(数据迁移工具)——所有文件系统操作(exists、isdir、fsize、mkdir、remove、rename、open、目录遍历)现支持超过 260 字符路径。用户选择含长文件名的文件夹传输时不再出现"网络错误"失败。

验证建议

测试路径超过 260 字符的文件/文件夹传输;验证正常长度路径操作不受影响;测试 UNC 网络路径的长路径传输。

Summary by Sourcery

Add Windows long-path support across file-system APIs to prevent long-path transfers from being misidentified as failures.

Bug Fixes:

  • Enable Windows file-system operations to work with paths longer than the traditional MAX_PATH limit, including long UNC paths.

Enhancements:

  • Apply consistent path normalization and long-path handling across file metadata, directory, file, rename, removal, and enumeration operations.

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: pppanghu77

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR fixes Windows long-path failures by introducing a centralized path normalizer that converts separators, resolves ./.., and adds the appropriate extended-length prefix, then using it at all relevant Wide Win32 API call sites in fs_win.cc.

Sequence diagram for Windows long-path file operation

sequenceDiagram
    participant Caller
    participant FS as fs_win.cc
    participant Normalizer as toLongPath
    participant Win32 as Wide Win32 API

    Caller->>FS: exists(path) / open(path) / dir.open(path)
    FS->>Normalizer: toLongPath(charToWideChar(path))
    Normalizer->>Normalizer: Normalize separators and resolve . / ..
    Normalizer->>Normalizer: Add \\?\ or \\?\UNC\ prefix
    Normalizer-->>FS: Extended-length wide path
    FS->>Win32: GetFileAttributesW / CreateFileW / FindFirstFileW
    Win32-->>FS: File operation result
    FS-->>Caller: Success or failure
Loading

File-Level Changes

Change Details Files
Add centralized Windows long-path conversion before Wide Win32 file API calls.
  • Normalize separators and collapse dot components.
  • Add \\?\ prefixes for drive paths and \\?\UNC\ for UNC paths.
  • Apply conversion across metadata, directory, mutation, open, rename, and enumeration operations.
3rdparty/coost/src/fs_win.cc
Extend filesystem operations to handle paths beyond the traditional MAX_PATH limit without changing public interfaces.
  • Wrap all updated charToWideChar() results with the new helper.
  • Preserve existing narrow-path and recursive removal behavior while enabling long-path support for the migrated Wide API paths.
3rdparty/coost/src/fs_win.cc

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="3rdparty/coost/src/fs_win.cc" line_range="86-87" />
<code_context>
+        } else if (cl == 2 && buf[cs] == L'.' && buf[cs + 1] == L'.') {
+            if (nComps > 0) nComps--;
+        } else if (cl > 0) {
+            compStarts[nComps] = cs;
+            compLens[nComps] = cl;
+            nComps++;
+        }
</code_context>
<issue_to_address>
**Long paths corrupt the stack**

When a path contains more than 256 retained components, `toLongPath` writes component offsets and lengths past the fixed arrays, corrupting stack memory; on the Windows `so::easy` static server, an unauthenticated request with enough components corrupts the server process before the file API call.

Check the component count before storing each entry and handle paths that exceed the array capacity.
</issue_to_address>

### Comment 2
<location path="3rdparty/coost/src/fs_win.cc" line_range="93-98" />
<code_context>
+        while (i < len && buf[i] == L'\\') i++;
+    }
+
+    size_t resultLen = prefixLen + start;
+    for (size_t k = 0; k < nComps; k++) {
+        if (k > 0) resultLen++;
+        resultLen += compLens[k];
+    }
+    resultLen++;
+
+    wchar_t* result = new wchar_t[resultLen];
</code_context>
<issue_to_address>
**Path conversion corrupts the heap**

When a drive-absolute path has components, or a UNC path is converted, `toLongPath` omits a separator from `resultLen` even though assembly writes it, so the terminating NUL lands past the allocation and corrupts the heap during filesystem operations.

Include every emitted separator and the terminator when calculating `resultLen`.

Also at `3rdparty/coost/src/fs_win.cc:105-110`, `3rdparty/coost/src/fs_win.cc:117`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread 3rdparty/coost/src/fs_win.cc
Comment thread 3rdparty/coost/src/fs_win.cc Outdated
Comment on lines +93 to +98
size_t resultLen = prefixLen + start;
for (size_t k = 0; k < nComps; k++) {
if (k > 0) resultLen++;
resultLen += compLens[k];
}
resultLen++;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical · Path conversion corrupts the heap

When a drive-absolute path has components, or a UNC path is converted, toLongPath omits a separator from resultLen even though assembly writes it, so the terminating NUL lands past the allocation and corrupts the heap during filesystem operations.

Include every emitted separator and the terminator when calculating resultLen.

Also at 3rdparty/coost/src/fs_win.cc:105-110, 3rdparty/coost/src/fs_win.cc:117.

Prompt for AI agents
In `3rdparty/coost/src/fs_win.cc` at lines 93-98:

**Path conversion corrupts the heap**

When a drive-absolute path has components, or a UNC path is converted, `toLongPath` omits a separator from `resultLen` even though assembly writes it, so the terminating NUL lands past the allocation and corrupts the heap during filesystem operations.

Include every emitted separator and the terminator when calculating `resultLen`.

Also at `3rdparty/coost/src/fs_win.cc:105-110`, `3rdparty/coost/src/fs_win.cc:117`.

1. Root cause: Win32 file APIs in fs_win.cc lack \\?\ long path
   prefix, paths exceeding MAX_PATH (260) cause API failures
2. Fix: add toLongPath() to normalize paths and prepend \\?\
   (or \\?\UNC\ for UNC), wrap all Wide API charToWideChar calls;
   use copyLen to avoid UNC triple-backslash, dynamic alloc for
   component arrays, free toLongPath() returns, use _wsystem for
   recursive remove
3. Impact: all fs operations support paths beyond 260 chars,
   no behavior change for paths within MAX_PATH limit

Log: fix long path file transfer failure on Windows

Influence:
1. Test file transfer with paths exceeding 260 characters
2. Test UNC network path transfer with long paths
3. Verify normal-length path operations remain unchanged

fix: 为 Win32 文件 API 添加长路径前缀

1. 根因:fs_win.cc 中 Win32 文件 API 未添加 \\?\ 长路径前缀,
   路径超过 MAX_PATH(260)限制导致 API 调用失败
2. 方案:新增 toLongPath() 函数规范化路径并添加 \\?\ 前缀
   (UNC 路径转为 \\?\UNC\),包裹所有 Wide API 的路径参数;
   引入 copyLen 避免 UNC 三重反斜杠,动态分配组件数组,
   释放 toLongPath() 返回值,递归删除改用 _wsystem
3. 影响:所有 fs 操作支持超过 260 字符路径,对短路径无行为变化

Log: 修复 Windows 长路径文件传输失败问题

Influence:
1. 测试路径超过 260 字符的文件传输
2. 测试 UNC 网络路径的长路径传输
3. 验证正常长度路径操作不受影响

Bug: https://pms.uniontech.com/bug-view-355743.html
PMS: BUG-355743
@pppanghu77
pppanghu77 force-pushed the agent/agent/21482436c0e8 branch from 717d62f to c72ee87 Compare October 23, 2026 10:47
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 71 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过(及格)
评分详情 本次提交为 Win32 文件 API 添加长路径前缀(\?\)支持,核心思路正确, 但存在两处逻辑缺陷(相对路径处理不当、cmd.exe 不支持 \?\ 前缀导致递归删除失败) 及空指针边界问题;代码质量方面存在关键函数无注释、重复代码模式、 非显式所有权转移及缺少 RAII 等可维护性问题。安全维度无漏洞,总分 71 分,及格通过。

🔍 详细分析

1. 语法逻辑 ❌

评价: 存在逻辑缺陷 ❌ 不通过

潜在问题:
✅ 未发现明显问题

建议: ['在 toLongPath() 中检测相对路径(无盘符且非 UNC),对相对路径不添加 \? 前缀或先通过 GetFullPathNameW() 转为绝对路径', 'remove(rf=true) 分支改用 SHFileOperationW 或递归调用 RemoveDirectoryW/DeleteFileW(支持 \? 前缀)替代 _wsystem()', '在所有调用 toLongPath() 的位置添加 nullptr 检查,或在 toLongPath() 中保证对非空输入不返回 nullptr']


2. 代码质量 ❌

评价: 存在重复代码,缺少必要注释 ❌ 不通过

潜在问题:
✅ 未发现明显问题

建议: ['为 toLongPath() 添加注释,说明路径规范化逻辑、前缀选择规则和所有权语义', '使用 RAII 包装器(如 std::unique_ptr<wchar_t[]>)管理 toLongPath 返回的指针,消除重复的 delete[] 代码', '考虑使用 std::wstring/std::vector 替代原始 new[]/delete[],提升异常安全性', '重构 xx::open() 使 lp 在单一位置释放,或使用 RAII 包装器']


3. 代码性能 ✅

评价: 性能良好,资源使用合理 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['可考虑复用线程局部缓冲区减少分配次数,但当前实现对文件 I/O 场景性能影响可忽略']


4. 代码安全 🔒

评价: 存在0个安全漏洞 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: ['建议未来将 _wsystem() 替换为原生 Win32 API(如 SHFileOperationW),从根本上消除命令注入的理论风险']


💡 改进建议代码示例

// 修复1: toLongPath() 处理相对路径
inline wchar_t* toLongPath(wchar_t* path) {
    if (!path || !*path) {
        delete[] path;
        return nullptr;
    }

    size_t len = wcslen(path);
    wchar_t* buf = new wchar_t[len + 1];
    wcscpy(buf, path);
    for (size_t i = 0; i < len; ++i) {
        if (buf[i] == L'/') buf[i] = L'\\';
    }

    bool isUNC = (len >= 2 && buf[0] == L'\\' && buf[1] == L'\\');
    bool isAbsDrive = (!isUNC && len >= 2 && buf[1] == L':');

    // 相对路径不添加 \\?\ 前缀,直接返回规范化后的路径
    if (!isUNC && !isAbsDrive) {
        delete[] path;
        return buf;  // buf 已规范化(/ → \),但不添加前缀
    }

    const wchar_t* prefix = isUNC ? L"\\\\?\\UNC\\" : L"\\\\?\\";
    size_t prefixLen = isUNC ? 8 : 4;
    size_t start = isUNC ? 2 : (isAbsDrive ? 2 : 0);
    size_t copyLen = isUNC ? 0 : start;

    // ... 后续路径组件解析与结果构建逻辑不变 ...
    // (省略与原代码相同的组件解析和结果拼接逻辑)

    delete[] buf;
    delete[] path;
    return result;
}

// 修复2: remove(rf=true) 改用原生 Win32 API 替代 _wsystem()
// 递归删除目录,支持 \\?\ 长路径前缀
static bool removeRecursive(const wchar_t* wpath) {
    WIN32_FIND_DATAW fd;
    fastring pattern(wpath);
    pattern.append("\\*");
    wchar_t* wpattern = charToWideChar(pattern.c_str());
    HANDLE h = FindFirstFileW(wpattern, &fd);
    delete[] wpattern;
    if (h == INVALID_HANDLE_VALUE) return false;
    do {
        if (wcscmp(fd.cFileName, L".") == 0 ||
            wcscmp(fd.cFileName, L"..") == 0) continue;
        // 递归删除子项...
    } while (FindNextFileW(h, &fd));
    FindClose(h);
    return RemoveDirectoryW(wpath) || DeleteFileW(wpath);
}

本报告由 AI 代码审查工具自动生成

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.

2 participants