Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: properly handle DOS device paths in strip_windows_prefix #455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 22, 2025

Conversation

chenxinyanc
Copy link
Contributor

@chenxinyanc chenxinyanc commented Apr 16, 2025

Fixes #454

This PR changed the name and return type of strip_windows_prefix to try_strip_windows_prefix, allowing it to return None to indicate unsupported DOS device paths.

Most significantly, symlinks referring to a drive without drive letter, usually accessed via a mount point (Mounted Volume), should not be resolved at all, as nodejs import/require does not support such properly, as of Node 22.

This PR also rectified the UNC path resolution to prepend the "\\" portion to the path.

@Boshen Boshen self-assigned this Apr 16, 2025
@Boshen
Copy link
Member

Boshen commented Apr 17, 2025

This is what I put together from the stackoverflow answer:

/// When applicable, converts a [DOS device path](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#dos-device-paths)
/// to a normal path (usually, "Traditional DOS paths" or "UNC path") that can be consumed by the `import`/`require` syntax of Node.js.
/// i.e. Remove `\\?\`, `\\.\` prefix then `\\?\UNC` prefix.
/// Code adapted from <https://stackoverflow.com/questions/50322817/how-do-i-remove-the-prefix-from-a-canonical-windows-path>
#[cfg(target_os = "windows")]
#[inline]
pub fn dunce(p: PathBuf) -> PathBuf {
    let p = p.strip_prefix(r#"\\?\"#).map(|p| p.to_path_buf()).unwrap_or(p);
    let p = p.strip_prefix(r#"\\.\"#).map(|p| p.to_path_buf()).unwrap_or(p);
    dunce::simplified(&p).to_path_buf()
}

Copy link

codspeed-hq bot commented Apr 17, 2025

CodSpeed Performance Report

Merging #455 will not alter performance

Comparing chenxinyanc:xinyan-strip-prefix (d46a428) with main (b9f1c67)

Summary

✅ 3 untouched benchmarks

@chenxinyanc
Copy link
Contributor Author

chenxinyanc commented Apr 17, 2025

I think the tricky part is, node JS cannot handle imports from either of these paths (even though ordinary fs operations still work).

r"\\?\Volume{c8ec34d8-3ba6-45c3-9b9d-3e4148e12d00}\file4.txt"
r"\\?\BootPartition\file4.txt"

In these cases, we might need to keep the symlink as-is, and that's what I was trying to describe in #454, which is to prevent resolving (or following the link target of)

D:\unrs-resolver-mounted-partition\src\mount-root\foo.ts

into

\\?\Volume{c8ec34d8-3ba6-45c3-9b9d-3e4148e12d00}\foo.ts

because NodeJS cannot import from the second path, but it can import from the first path.

That's also why I'm letting strip_windows_prefix return an Option<PathBuf> instead of PathBuf itself.

Probably you can check whether the &Path passed from dunce::simplified still starts with \\?\ or \\.\ to decide whether not to follow the symlinks.

@chenxinyanc
Copy link
Contributor Author

chenxinyanc commented Apr 17, 2025

btw we might need to strip the r"\\?\" part even if the corresponding traditional DOS path exceeds 260 chars. Because while NodeJS supports importing from long paths since nodejs/node#50753 , it still does not support DOS device path (path starting with \\?\). I've made a mistake here. However, you might need to consider whether you still want to keep stripping the \\?\ prefix even if the path exceeds 260 chars.

This case might not have been handled by dunce itself.

https://gitlab.com/kornelski/dunce/-/blob/master/src/lib.rs?ref_type=heads#L180-182

image

I've just given the following module path a try

r"\\?\D:\test\long long long long path\long long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long path\test test test test test.js"

While we cannot represent the path above as a "valid" traditional DOS path, because the length of r"D:\test\...\test test test test test.js" would exceed 260 chars, NodeJS still supports its import

Welcome to Node.js v22.7.0.
Type ".help" for more information.
> require("D:\\test\\long long long long path\\long long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long path\\test test test test test.js")
{}
> await import("file:///D:\\test\\long long long long path\\long long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long path\\test test test test test.js")
[Module: null prototype] { default: {} }
> require("\\\\?\\D:\\test\\long long long long path\\long long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long pathlong long long long path\\test test test test test.js")
{}
// I couldn't figure out a valid `file:///` URI with DOS device path (\\?\...) to use `await import`.

@Boshen
Copy link
Member

Boshen commented Apr 19, 2025

I'll come back to this PR when I get the time.

@Boshen Boshen marked this pull request as draft April 19, 2025 04:07
@chenxinyanc
Copy link
Contributor Author

chenxinyanc commented Apr 21, 2025

I'll come back to this PR when I get the time.

Please take your time.

However, I think my version should be a practically working one, which might be a good place to get started, before we receive other feedbacks in the long run.

@JounQin
Copy link
Contributor

JounQin commented Apr 21, 2025

@chenxinyanc I think you can PR to unrs-resolver first and resolve your issue quickly, and I'll sync with upstream changes later.

@chenxinyanc
Copy link
Contributor Author

Thanks @JounQin ! I'll cherry-pick the PR and probably we can see how it turns out in unrs-resolver first.

chenxinyanc added a commit to chenxinyanc/unrs-resolver that referenced this pull request Apr 21, 2025
e8a11841083164401bca72841d8c9b2d8f04185d strip_windows_prefix: Handle DOS device paths properly.
2862145da2685b3365e0bfaafdd23751abec2858 [autofix.ci] apply automated fixes
70f675e01bfc780c84d443fdf801028b303717fa Fix build break.
b6cbd102d0723747a5b3ab5bb1c10664d34249e7 Fix build break.
@Boshen Boshen force-pushed the xinyan-strip-prefix branch from 2a8dcea to 068bef9 Compare April 22, 2025 03:58
@Boshen Boshen force-pushed the xinyan-strip-prefix branch from 068bef9 to d46a428 Compare April 22, 2025 04:05
@Boshen Boshen marked this pull request as ready for review April 22, 2025 04:18
@Boshen
Copy link
Member

Boshen commented Apr 22, 2025

Sorry I went too far trying to optimize this thing, reverted my changes and cleaned up the code structure instead.

@Boshen Boshen merged commit 850cbf9 into oxc-project:main Apr 22, 2025
11 checks passed
@oxc-bot oxc-bot mentioned this pull request Apr 22, 2025
Boshen pushed a commit that referenced this pull request Apr 22, 2025
## 🤖 New release

* `oxc_resolver`: 5.3.0 -> 6.0.0 (⚠ API breaking changes)

### ⚠ `oxc_resolver` breaking changes

```text
--- failure inherent_method_missing: pub method removed or renamed ---

Description:
A publicly-visible method or associated fn is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.40.0/src/lints/inherent_method_missing.ron

Failed in:
  FileSystemOs::strip_windows_prefix, previously in file /tmp/.tmpZFrfKd/oxc_resolver/src/file_system.rs:168
```

<details><summary><i><b>Changelog</b></i></summary><p>

<blockquote>

##
[6.0.0](oxc_resolver-v5.3.0...oxc_resolver-v6.0.0)
- 2025-04-22

### <!-- 1 -->Bug Fixes

- properly handle DOS device paths in strip_windows_prefix
([#455](#455))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
@chenxinyanc
Copy link
Contributor Author

@Boshen Sorry for the flurry but it seems my prior change in this PR is not sufficient to fix my issue: unrs/unrs-resolver#84 (comment)

I've just tried the binary built from 5fc3395 in this repo locally and confirmed the same outcome.

No need to worry at this point, though. IMO this is not exactly a regression:

  • Before this PR: ResolverFactory.resolve returns invalid module path (with Volume{...} segments)
    • This will eventually cause downstream consumers to get a non-existent path and report mysterious errors (e.g. path not found).
  • After this PR: ResolverFactory.resolve returns Err(PathNotSupported)

Since I've already added some changes today in the downstream PR: unrs/unrs-resolver#84, I will probably open another PR here in oxc-resolver when I'm ready. My plan would be

  • we keep the PR (in unrs-resolver) a draft PR, and
  • when I'm done with conflict resolution and unit tests in this PR, I open a PR in oxc-resolver with the same change, and
  • after the PR gets accepted in oxc-resolver, (JounQin) just need to do a simple merge-upstream, and
  • eventually, we close the PR in unrs-resolver without needing to merge it.

Sorry for the troubles 🌚 things should have been simpler if I had stuck to making changes in oxc-resolver repo instead.

@Boshen
Copy link
Member

Boshen commented Apr 22, 2025

@Boshen Sorry for the flurry but it seems my prior change in this PR is not sufficient to fix my issue: unrs/unrs-resolver#84 (comment)

I've just tried the binary built from 5fc3395 in this repo locally and confirmed the same outcome.

No need to worry at this point, though. IMO this is not exactly a regression:

  • Before this PR: ResolverFactory.resolve returns invalid module path (with Volume{...} segments)

    • This will eventually cause downstream consumers to get a non-existent path and report mysterious errors (e.g. path not found).
  • After this PR: ResolverFactory.resolve returns Err(PathNotSupported)

Since I've already added some changes today in the downstream PR: unrs/unrs-resolver#84, I will probably open another PR here in oxc-resolver when I'm ready. My plan would be

  • we keep the PR (in unrs-resolver) a draft PR, and
  • when I'm done with conflict resolution and unit tests in this PR, I open a PR in oxc-resolver with the same change, and
  • after the PR gets accepted in oxc-resolver, (JounQin) just need to do a simple merge-upstream, and
  • eventually, we close the PR in unrs-resolver without needing to merge it.

Sorry for the troubles 🌚 things should have been simpler if I had stuck to making changes in oxc-resolver repo instead.

Sounds good to me.

JounQin pushed a commit to chenxinyanc/unrs-resolver that referenced this pull request Apr 22, 2025
e8a11841083164401bca72841d8c9b2d8f04185d strip_windows_prefix: Handle DOS device paths properly.
2862145da2685b3365e0bfaafdd23751abec2858 [autofix.ci] apply automated fixes
70f675e01bfc780c84d443fdf801028b303717fa Fix build break.
b6cbd102d0723747a5b3ab5bb1c10664d34249e7 Fix build break.
chenxinyanc added a commit to chenxinyanc/unrs-resolver that referenced this pull request Apr 23, 2025
…oject/oxc-resolver#455

e8a11841083164401bca72841d8c9b2d8f04185d strip_windows_prefix: Handle DOS device paths properly.
2862145da2685b3365e0bfaafdd23751abec2858 [autofix.ci] apply automated fixes
70f675e01bfc780c84d443fdf801028b303717fa Fix build break.
b6cbd102d0723747a5b3ab5bb1c10664d34249e7 Fix build break.
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.

Mounted Volume in the path causes incorrect resolution output
4 participants