file_source/
metadata_ext.rs

1//! FIXME: A workaround to fix <https://github.com/vectordotdev/vector/issues/1480> resulting from <https://github.com/rust-lang/rust/issues/63010>
2//! Most of code is cribbed directly from the Rust stdlib and ported to work with winapi.
3//!
4//! In stdlib imported code, warnings are allowed.
5
6use std::fs::File;
7#[cfg(unix)]
8use std::os::unix::fs::MetadataExt;
9#[cfg(windows)]
10use std::{mem::zeroed, ptr};
11
12#[cfg(windows)]
13use winapi::shared::minwindef::DWORD;
14#[cfg(windows)]
15use winapi::um::{
16    fileapi::GetFileInformationByHandle, fileapi::BY_HANDLE_FILE_INFORMATION,
17    ioapiset::DeviceIoControl, winioctl::FSCTL_GET_REPARSE_POINT,
18    winnt::FILE_ATTRIBUTE_REPARSE_POINT, winnt::MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
19};
20
21#[cfg(not(windows))]
22pub trait PortableFileExt {
23    fn portable_dev(&self) -> std::io::Result<u64>;
24    fn portable_ino(&self) -> std::io::Result<u64>;
25}
26
27#[cfg(windows)]
28pub trait PortableFileExt: std::os::windows::io::AsRawHandle {
29    fn portable_dev(&self) -> std::io::Result<u64>;
30    fn portable_ino(&self) -> std::io::Result<u64>;
31    // This code is from the Rust stdlib https://github.com/rust-lang/rust/blob/30ddb5a8c1e85916da0acdc665d6a16535a12dd6/src/libstd/sys/windows/fs.rs#L458-L478
32    #[allow(unused_assignments, unused_variables)]
33    fn reparse_point<'a>(
34        &self,
35        space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
36    ) -> std::io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> {
37        unsafe {
38            let mut bytes = 0;
39            cvt({
40                DeviceIoControl(
41                    self.as_raw_handle(),
42                    FSCTL_GET_REPARSE_POINT,
43                    ptr::null_mut(),
44                    0,
45                    space.as_mut_ptr() as *mut _,
46                    space.len() as DWORD,
47                    &mut bytes,
48                    ptr::null_mut(),
49                )
50            })?;
51            Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER)))
52        }
53    }
54    // This code is from the Rust stdlib https://github.com/rust-lang/rust/blob/30ddb5a8c1e85916da0acdc665d6a16535a12dd6/src/libstd/sys/windows/fs.rs#L326-L351
55    #[allow(unused_assignments, unused_variables)]
56    fn get_file_info(&self) -> std::io::Result<BY_HANDLE_FILE_INFORMATION> {
57        unsafe {
58            let mut info: BY_HANDLE_FILE_INFORMATION = zeroed();
59            cvt(GetFileInformationByHandle(self.as_raw_handle(), &mut info))?;
60            let mut reparse_tag = 0;
61            if info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
62                let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize];
63                if let Ok((_, buf)) = self.reparse_point(&mut b) {
64                    reparse_tag = buf.ReparseTag;
65                }
66            }
67            Ok(info)
68        }
69    }
70}
71
72#[cfg(unix)]
73impl PortableFileExt for File {
74    fn portable_dev(&self) -> std::io::Result<u64> {
75        Ok(self.metadata()?.dev())
76    }
77    fn portable_ino(&self) -> std::io::Result<u64> {
78        Ok(self.metadata()?.ino())
79    }
80}
81
82#[cfg(windows)]
83impl PortableFileExt for File {
84    fn portable_dev(&self) -> std::io::Result<u64> {
85        let info = self.get_file_info()?;
86        Ok(info.dwVolumeSerialNumber.into())
87    }
88    // This is not exactly inode, but it's close. See https://docs.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information
89    fn portable_ino(&self) -> std::io::Result<u64> {
90        let info = self.get_file_info()?;
91        // https://github.com/rust-lang/rust/blob/30ddb5a8c1e85916da0acdc665d6a16535a12dd6/src/libstd/sys/windows/fs.rs#L347
92        Ok((info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32))
93    }
94}
95
96// This code is from the Rust stdlib https://github.com/rust-lang/rust/blob/a916ac22b9f7f1f0f7aba0a41a789b3ecd765018/src/libstd/sys/windows/c.rs#L380-L386
97#[cfg(windows)]
98#[allow(dead_code, non_snake_case, non_camel_case_types)]
99pub struct REPARSE_DATA_BUFFER {
100    pub ReparseTag: libc::c_uint,
101    pub ReparseDataLength: libc::c_ushort,
102    pub Reserved: libc::c_ushort,
103    pub rest: (),
104}
105
106// This code is from the Rust stdlib  https://github.com/rust-lang/rust/blob/30ddb5a8c1e85916da0acdc665d6a16535a12dd6/src/libstd/sys/hermit/mod.rs#L141-L143
107#[cfg(windows)]
108pub fn cvt(result: i32) -> std::io::Result<usize> {
109    if result < 0 {
110        Err(std::io::Error::from_raw_os_error(-result))
111    } else {
112        Ok(result as usize)
113    }
114}