khive_db/
file_identity.rs1use std::io;
4use std::path::Path;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub struct DatabaseFileIdentity {
8 #[cfg(unix)]
9 device: u64,
10 #[cfg(unix)]
11 inode: u64,
12 #[cfg(windows)]
13 volume_serial: u64,
14 #[cfg(windows)]
15 file_id: [u8; 16],
16}
17
18#[cfg(unix)]
19impl DatabaseFileIdentity {
20 pub(crate) fn unix_parts(self) -> (u64, u64) {
21 (self.device, self.inode)
22 }
23}
24
25#[cfg(unix)]
26pub fn database_file_identity(path: &Path) -> io::Result<DatabaseFileIdentity> {
27 use std::os::unix::fs::MetadataExt as _;
28
29 let metadata = std::fs::metadata(path)?;
30 Ok(DatabaseFileIdentity {
31 device: metadata.dev(),
32 inode: metadata.ino(),
33 })
34}
35
36#[cfg(unix)]
38pub fn database_file_identity_from_file(file: &std::fs::File) -> io::Result<DatabaseFileIdentity> {
39 use std::os::unix::fs::MetadataExt as _;
40
41 let metadata = file.metadata()?;
42 Ok(DatabaseFileIdentity {
43 device: metadata.dev(),
44 inode: metadata.ino(),
45 })
46}
47
48#[cfg(unix)]
51pub fn file_link_count(file: &std::fs::File) -> io::Result<u64> {
52 use std::os::unix::fs::MetadataExt as _;
53
54 Ok(file.metadata()?.nlink())
55}
56
57#[cfg(windows)]
58pub fn database_file_identity(path: &Path) -> io::Result<DatabaseFileIdentity> {
59 use std::os::windows::fs::OpenOptionsExt as _;
60 use std::os::windows::io::AsRawHandle as _;
61 use windows_sys::Win32::Storage::FileSystem::{
62 FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
63 };
64
65 let file = std::fs::OpenOptions::new()
66 .read(true)
67 .access_mode(FILE_READ_ATTRIBUTES)
68 .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
69 .open(path)?;
70 identity_from_handle(file.as_raw_handle())
71}
72
73#[cfg(windows)]
75pub fn database_file_identity_from_file(file: &std::fs::File) -> io::Result<DatabaseFileIdentity> {
76 use std::os::windows::io::AsRawHandle as _;
77
78 identity_from_handle(file.as_raw_handle())
79}
80
81#[cfg(windows)]
84pub fn file_link_count(file: &std::fs::File) -> io::Result<u64> {
85 use std::os::windows::io::AsRawHandle as _;
86 use windows_sys::Win32::Storage::FileSystem::{
87 GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION,
88 };
89
90 let mut info = BY_HANDLE_FILE_INFORMATION::default();
91 if unsafe { GetFileInformationByHandle(file.as_raw_handle(), &raw mut info) } == 0 {
94 return Err(io::Error::last_os_error());
95 }
96 Ok(u64::from(info.nNumberOfLinks))
97}
98
99#[cfg(windows)]
100fn identity_from_handle(
101 handle: std::os::windows::io::RawHandle,
102) -> io::Result<DatabaseFileIdentity> {
103 use windows_sys::Win32::Storage::FileSystem::{
104 FileIdInfo, GetFileInformationByHandleEx, FILE_ID_INFO,
105 };
106
107 let mut info = FILE_ID_INFO::default();
108 let ok = unsafe {
111 GetFileInformationByHandleEx(
112 handle,
113 FileIdInfo,
114 (&raw mut info).cast(),
115 std::mem::size_of::<FILE_ID_INFO>() as u32,
116 )
117 };
118 if ok == 0 {
119 return Err(io::Error::last_os_error());
120 }
121 Ok(DatabaseFileIdentity {
122 volume_serial: info.VolumeSerialNumber,
123 file_id: info.FileId.Identifier,
124 })
125}
126
127#[cfg(windows)]
128pub fn sqlite_opened_file_identity(
129 conn: &rusqlite::Connection,
130) -> Result<DatabaseFileIdentity, crate::error::SqliteError> {
131 use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
132
133 let mut handle: HANDLE = std::ptr::null_mut();
134 let result = unsafe {
137 rusqlite::ffi::sqlite3_file_control(
138 conn.handle(),
139 c"main".as_ptr(),
140 rusqlite::ffi::SQLITE_FCNTL_WIN32_GET_HANDLE,
141 (&raw mut handle).cast(),
142 )
143 };
144 if result != rusqlite::ffi::SQLITE_OK || handle.is_null() || handle == INVALID_HANDLE_VALUE {
145 return Err(crate::error::SqliteError::InvalidData(format!(
146 "cannot inspect opened SQLite database handle (file control {result})"
147 )));
148 }
149 Ok(identity_from_handle(handle)?)
150}