windowsrustcross-platform

Find path of redirected input directory on windows


I'm working on a piece of code that checks if stdin represents a directory and, if it does, performs some operations on it

Currently, I have code that works on Unix systems:

let fd = stdin().as_fd().try_clone_to_owned().unwrap();
let file = File::from(fd);
let meta = file.metadata().unwrap();
if meta.is_dir() {
   let mut stdin_path = fs::canonicalize("/dev/stdin").unwrap();
   println!({:?}", stdin_path);
   // do something    
}

I'm trying to ensure it works on Windows as well. Since Windows doesn't have std::os::fd::AsFd , as_fd methods, plus there's no /dev/stdin, I'm not sure about the appropriate APIs to use to achieve the same functionality on Windows. Any guidance to make it work on Windows?


Solution

  • The Windows equivalent to get metadata is almost identical, but using handles instead of file descriptors:

    let handle = stdin().as_handle().try_clone_to_owned().unwrap();
    let file = File::from(handle);
    let meta = file.metadata().unwrap();
    

    However, this is extraordinarily rare. In order to test this, I had to make a separate Rust program that opens a file with OpenOptionsExt::custom_flags (see Directory Handles on MS Learn) and feeds it to the program containing the previous code.

    use std::os::windows::fs::OpenOptionsExt;
    use std::process::{Command, Stdio};
    
    const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x02000000;
    
    fn main() {
        let file = std::fs::OpenOptions::new()
            .read(true)
            .custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
            .open("src")
            .unwrap();
    
        Command::new("first_program.exe")
            .stdin(Stdio::from(file))
            .spawn()
            .unwrap()
            .wait()
            .unwrap();
    }
    

    It looks like the Rust standard library uses this when it needs to read a symbolic link or junction, or query metadata, and doesn't have a cross-platform way to return a directory as File. For shells, cmd doesn't let you redirect stdin from directories, and PowerShell doesn't even let you redirect stdin from files; you have to pipe them from Get-Content. The workaround from this answer using Start-Process also doesn't work with directories.

    So if you need to accept directories as stdin for technical reasons, the above solution will work. But if you just want to provide a convenient shortcut for users of your program, this isn't worth doing. It's better to say such operations are unsupported on Windows, since it is unlikely to be done on purpose.

    You can retrieve the path using the handle and passing it to GetFinalPathNameByHandleW (Rust API).