Skip to content

Junction creation fails silently with verbatim \\?\ prefix paths #30

@DK26

Description

@DK26

Summary

When calling junction::create() with paths that have the Windows verbatim prefix (\\?\), the function returns Ok(()) but creates a broken junction that cannot be accessed.

Environment

  • OS: Windows 10/11
  • Rust: stable (1.83)
  • Crate version: junction 1.3.0

Reproduction

fn main() {
    let tmp = tempfile::tempdir().unwrap();
    
    // Create target directory
    let target = tmp.path().join("data").join("dir");
    std::fs::create_dir_all(&target).unwrap();
    
    // Create link parent
    let link_parent = tmp.path().join("links");
    std::fs::create_dir_all(&link_parent).unwrap();
    let link = link_parent.join("junc");
    
    // Canonicalize to get verbatim paths (\\?\C:\...)
    let target_verbatim = std::fs::canonicalize(&target).unwrap();
    let link_parent_verbatim = std::fs::canonicalize(&link_parent).unwrap();
    let link_verbatim = link_parent_verbatim.join("junc");
    
    println!("Target: {:?}", target_verbatim);
    println!("Link: {:?}", link_verbatim);
    
    // Create junction with verbatim paths
    let result = junction::create(&target_verbatim, &link_verbatim);
    println!("Create result: {:?}", result);
    
    // Try to access the junction
    let meta = std::fs::metadata(&link_verbatim);
    println!("Metadata: {:?}", meta);
    
    // Check what the junction target is
    let target_read = junction::get_target(&link_verbatim);
    println!("Target read: {:?}", target_read);
}

Expected Behavior

Junction is created and accessible:

Create result: Ok(())
Metadata: Ok(...)
Target read: Ok("\\?\C:\Users\...\data\dir")

Actual Behavior

Junction creation appears to succeed but the junction is broken:

Create result: Ok(())
Metadata: Err(Os { code: 123, kind: InvalidFilename, message: "The filename, directory name, or volume label syntax is incorrect." })
Target read: Err(Custom { kind: NotFound, error: "`junction` does not exist" })

Workaround

Strip the \\?\ prefix before passing paths to junction::create():

fn strip_verbatim_prefix(path: &Path) -> Cow<'_, Path> {
    let s = path.as_os_str().to_string_lossy();
    if let Some(rest) = s.strip_prefix(r"\\?\") {
        Cow::Owned(PathBuf::from(rest))
    } else {
        Cow::Borrowed(path)
    }
}

// Use stripped paths
junction::create(
    strip_verbatim_prefix(&target_verbatim).as_ref(),
    strip_verbatim_prefix(&link_verbatim).as_ref()
)?;

Analysis

The verbatim prefix (\\?\) is commonly produced by std::fs::canonicalize() on Windows. Since junctions are NTFS reparse points and the Windows API should handle verbatim paths, the issue might be in how the path is being stored in the reparse point data structure or how it's being converted for the NTFS API call.

Impact

This affects any code that uses canonicalized paths with the junction crate, which is a common pattern for security-sensitive applications that need to resolve symlinks before creating junctions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions