pub fn open_options_no_follow() -> std::fs::OpenOptions {
#[cfg_attr(not(any(unix, windows)), allow(unused_mut))]
let mut options = std::fs::OpenOptions::new();
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
options.custom_flags(libc::O_NOFOLLOW);
}
#[cfg(windows)]
{
use std::os::windows::fs::OpenOptionsExt;
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);
}
options
}
#[derive(Debug)]
pub enum FileOrSymlink {
File(std::fs::File),
Symlink,
}
pub fn open_read_only_no_follow(path: &std::path::Path) -> std::io::Result<FileOrSymlink> {
match open_options_no_follow().read(true).open(path) {
Ok(file) if file.metadata()?.file_type().is_symlink() => Ok(FileOrSymlink::Symlink),
Ok(file) => Ok(FileOrSymlink::File(file)),
Err(err)
if err.kind() != std::io::ErrorKind::NotFound
&& path.symlink_metadata().is_ok_and(|meta| meta.file_type().is_symlink()) =>
{
Ok(FileOrSymlink::Symlink)
}
Err(err) => Err(err),
}
}