file_source_common/
metadata_ext.rs1#![allow(async_fn_in_trait)]
6
7#[cfg(unix)]
8use std::fs::Metadata;
9#[cfg(unix)]
10use std::os::unix::fs::MetadataExt;
11#[cfg(windows)]
12use std::{mem::zeroed, ptr};
13use tokio::fs::File;
14
15#[cfg(windows)]
16use winapi::shared::minwindef::DWORD;
17#[cfg(windows)]
18use winapi::um::{
19 fileapi::BY_HANDLE_FILE_INFORMATION, fileapi::GetFileInformationByHandle,
20 ioapiset::DeviceIoControl, winioctl::FSCTL_GET_REPARSE_POINT,
21 winnt::FILE_ATTRIBUTE_REPARSE_POINT, winnt::MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
22};
23
24pub trait PortableFileExt {
25 fn portable_dev(&self) -> u64;
26 fn portable_ino(&self) -> u64;
27}
28
29#[cfg(unix)]
30pub trait AsyncFileInfo {
31 async fn file_info(&self) -> std::io::Result<Metadata>;
32}
33
34#[cfg(windows)]
35pub trait AsyncFileInfo: std::os::windows::io::AsRawHandle {
36 #[allow(unused_assignments, unused_variables)]
38 fn reparse_point<'a>(
39 &self,
40 space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
41 ) -> std::io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> {
42 unsafe {
43 let mut bytes = 0;
44 cvt({
45 DeviceIoControl(
46 self.as_raw_handle(),
47 FSCTL_GET_REPARSE_POINT,
48 ptr::null_mut(),
49 0,
50 space.as_mut_ptr() as *mut _,
51 space.len() as DWORD,
52 &mut bytes,
53 ptr::null_mut(),
54 )
55 })?;
56 Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER)))
57 }
58 }
59 #[allow(unused_assignments, unused_variables)]
61 fn file_info_inner(&self) -> std::io::Result<BY_HANDLE_FILE_INFORMATION> {
62 unsafe {
63 let mut info: BY_HANDLE_FILE_INFORMATION = zeroed();
64 cvt(GetFileInformationByHandle(self.as_raw_handle(), &mut info))?;
65 let mut reparse_tag = 0;
66 if info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
67 let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize];
68 if let Ok((_, buf)) = self.reparse_point(&mut b) {
69 reparse_tag = buf.ReparseTag;
70 }
71 }
72 Ok(info)
73 }
74 }
75 async fn file_info(&self) -> std::io::Result<BY_HANDLE_FILE_INFORMATION>;
76}
77
78#[cfg(unix)]
79impl AsyncFileInfo for File {
80 async fn file_info(&self) -> std::io::Result<Metadata> {
81 self.metadata().await
82 }
83}
84
85#[cfg(unix)]
86impl PortableFileExt for Metadata {
87 fn portable_dev(&self) -> u64 {
88 self.dev()
89 }
90 fn portable_ino(&self) -> u64 {
91 self.ino()
92 }
93}
94
95#[cfg(windows)]
96impl AsyncFileInfo for File {
97 async fn file_info(&self) -> std::io::Result<BY_HANDLE_FILE_INFORMATION> {
98 let file = self.try_clone().await?;
99 tokio::task::spawn_blocking(move || file.file_info_inner())
100 .await
101 .map_err(std::io::Error::other)?
102 }
103}
104
105#[cfg(windows)]
106impl PortableFileExt for BY_HANDLE_FILE_INFORMATION {
107 fn portable_dev(&self) -> u64 {
108 self.dwVolumeSerialNumber.into()
109 }
110 fn portable_ino(&self) -> u64 {
112 (self.nFileIndexLow as u64) | ((self.nFileIndexHigh as u64) << 32)
114 }
115}
116
117#[cfg(windows)]
119#[allow(dead_code, non_snake_case, non_camel_case_types)]
120pub struct REPARSE_DATA_BUFFER {
121 pub ReparseTag: libc::c_uint,
122 pub ReparseDataLength: libc::c_ushort,
123 pub Reserved: libc::c_ushort,
124 pub rest: (),
125}
126
127#[cfg(windows)]
129pub fn cvt(result: i32) -> std::io::Result<usize> {
130 if result < 0 {
131 Err(std::io::Error::from_raw_os_error(-result))
132 } else {
133 Ok(result as usize)
134 }
135}