1use std::{fs, io, path::Path};
31
32#[cfg(feature = "serde")]
33use serde::{Deserialize, Serialize};
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38pub enum FileId {
39 #[cfg_attr(feature = "serde", serde(rename = "lowres"))]
47 LowRes {
48 #[cfg_attr(feature = "serde", serde(rename = "volume"))]
50 volume_serial_number: u32,
51
52 #[cfg_attr(feature = "serde", serde(rename = "index"))]
54 file_index: u64,
55 },
56
57 #[cfg_attr(feature = "serde", serde(rename = "highres"))]
63 HighRes {
64 #[cfg_attr(feature = "serde", serde(rename = "volume"))]
66 volume_serial_number: u64,
67
68 #[cfg_attr(feature = "serde", serde(rename = "file"))]
70 file_id: u128,
71 },
72}
73
74impl FileId {
75 pub fn new_low_res(volume_serial_number: u32, file_index: u64) -> Self {
76 FileId::LowRes {
77 volume_serial_number,
78 file_index,
79 }
80 }
81
82 pub fn new_high_res(volume_serial_number: u64, file_id: u128) -> Self {
83 FileId::HighRes {
84 volume_serial_number,
85 file_id,
86 }
87 }
88}
89
90pub fn get_file_id(path: impl AsRef<Path>) -> io::Result<FileId> {
92 let file = open_file(path)?;
93
94 unsafe { get_file_info_ex(&file).or_else(|_| get_file_info(&file)) }
95}
96
97pub fn get_low_res_file_id(path: impl AsRef<Path>) -> io::Result<FileId> {
99 let file = open_file(path)?;
100
101 unsafe { get_file_info(&file) }
102}
103
104pub fn get_high_res_file_id(path: impl AsRef<Path>) -> io::Result<FileId> {
106 let file = open_file(path)?;
107
108 unsafe { get_file_info_ex(&file) }
109}
110
111unsafe fn get_file_info_ex(file: &fs::File) -> Result<FileId, io::Error> {
112 use std::{mem, os::windows::prelude::*};
113 use windows_sys::Win32::{
114 Foundation::HANDLE,
115 Storage::FileSystem::{FileIdInfo, GetFileInformationByHandleEx, FILE_ID_INFO},
116 };
117
118 let mut info: FILE_ID_INFO = mem::zeroed();
119 let ret = GetFileInformationByHandleEx(
120 file.as_raw_handle() as HANDLE,
121 FileIdInfo,
122 &mut info as *mut FILE_ID_INFO as _,
123 mem::size_of::<FILE_ID_INFO>() as u32,
124 );
125
126 if ret == 0 {
127 return Err(io::Error::last_os_error());
128 };
129
130 Ok(FileId::new_high_res(
131 info.VolumeSerialNumber,
132 u128::from_le_bytes(info.FileId.Identifier),
133 ))
134}
135
136unsafe fn get_file_info(file: &fs::File) -> Result<FileId, io::Error> {
137 use std::{mem, os::windows::prelude::*};
138 use windows_sys::Win32::{
139 Foundation::HANDLE,
140 Storage::FileSystem::{GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION},
141 };
142
143 let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed();
144 let ret = GetFileInformationByHandle(file.as_raw_handle() as HANDLE, &mut info);
145 if ret == 0 {
146 return Err(io::Error::last_os_error());
147 };
148
149 Ok(FileId::new_low_res(
150 info.dwVolumeSerialNumber,
151 ((info.nFileIndexHigh as u64) << 32) | (info.nFileIndexLow as u64),
152 ))
153}
154
155fn open_file<P: AsRef<Path>>(path: P) -> io::Result<fs::File> {
156 use std::{fs::OpenOptions, os::windows::fs::OpenOptionsExt};
157 use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS;
158
159 OpenOptions::new()
160 .access_mode(0)
161 .custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
162 .open(path)
163}