Skip to content

std::fs incorrect behavior when dealing with UNIX domain socket on Windows #109106

Closed
@fanzeyi

Description

@fanzeyi

I tried this code:

use std::path::Path;
use std::env::args_os;

fn main() {
    let p = args_os().skip(1).next().expect("to have a path argument");
    let p: &Path = p.as_ref();

    dbg!(p.display());
    dbg!(p.exists());
    dbg!(p.metadata());
    dbg!(p.symlink_metadata());
    dbg!(p.is_file());
    dbg!(p.is_dir());
    dbg!(p.is_symlink());
}

I expected to see this happen:

The API should report the given path exists.

Instead, this happened:

When passed a path to a UNIX domain socket on Windows, I get:

[src\main.rs:22] p.display() = "<REDACTED>\\socket"
[src\main.rs:23] p.exists() = false
[src\main.rs:24] p.metadata() = Err(
    Os {
        code: 1920,
        kind: Uncategorized,
        message: "The file cannot be accessed by the system.",
    },
)
[src\main.rs:25] p.symlink_metadata() = Ok(
    Metadata {
        file_type: FileType(
            FileType {
                attributes: 1056,
                reparse_tag: 2147483683,
            },
        ),
        is_dir: false,
        is_file: true,
        permissions: Permissions(
            FilePermissions {
                attrs: 1056,
            },
        ),
        modified: Ok(
            SystemTime {
                intervals: 133232173107398736,
            },
        ),
        accessed: Ok(
            SystemTime {
                intervals: 133232173107398736,
            },
        ),
        created: Ok(
            SystemTime {
                intervals: 133172157242066860,
            },
        ),
        ..
    },
)
[src\main.rs:26] p.is_file() = false
[src\main.rs:27] p.is_dir() = false
[src\main.rs:28] p.is_symlink() = false

Meta

rustc --version --verbose:

rustc --version --verbose
rustc 1.68.0 (2c8cc3432 2023-03-06)
binary: rustc
commit-hash: 2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74
commit-date: 2023-03-06
host: x86_64-pc-windows-msvc
release: 1.68.0
LLVM version: 15.0.6

Analysis

I think this is simply an unintended consequence of supporting symlinks on Windows. It is interesting that symlink_metadata seems to be producing a correct result here (IMO this metadata should be returned by metadata()).

The differences between stat (metadata) and lstat (symlink_metadata) is the ReparsePoint flag.

pub fn stat(path: &Path) -> io::Result<FileAttr> {
metadata(path, ReparsePoint::Follow)
}
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
metadata(path, ReparsePoint::Open)
}

On Windows, UNIX domain sockets are implemented as reparse points just like symlink. However Rust is only setting the FILE_FLAG_OPEN_REPARSE_POINT flag when we are testing for symlink. Without this flag CreateFileW is going to return the access error above.

I think the issue is that, the standard library didn't consider that the possibility to have other type of files that are reparse points but not symlinks.

I'm not sure what is the best fix for this, but this is kinda surprising.

Metadata

Metadata

Assignees

Labels

A-ioArea: `std::io`, `std::fs`, `std::net` and `std::path`C-bugCategory: This is a bug.E-help-wantedCall for participation: Help is requested to fix this issue.O-windowsOperating system: WindowsT-libsRelevant to the library team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions