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(windows)]
49pub fn database_file_identity(path: &Path) -> io::Result<DatabaseFileIdentity> {
50 use std::os::windows::fs::OpenOptionsExt as _;
51 use std::os::windows::io::AsRawHandle as _;
52 use windows_sys::Win32::Storage::FileSystem::{
53 FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
54 };
55
56 let file = std::fs::OpenOptions::new()
57 .read(true)
58 .access_mode(FILE_READ_ATTRIBUTES)
59 .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
60 .open(path)?;
61 identity_from_handle(file.as_raw_handle())
62}
63
64#[cfg(windows)]
65fn identity_from_handle(
66 handle: std::os::windows::io::RawHandle,
67) -> io::Result<DatabaseFileIdentity> {
68 use windows_sys::Win32::Storage::FileSystem::{
69 FileIdInfo, GetFileInformationByHandleEx, FILE_ID_INFO,
70 };
71
72 let mut info = FILE_ID_INFO::default();
73 let ok = unsafe {
76 GetFileInformationByHandleEx(
77 handle,
78 FileIdInfo,
79 (&raw mut info).cast(),
80 std::mem::size_of::<FILE_ID_INFO>() as u32,
81 )
82 };
83 if ok == 0 {
84 return Err(io::Error::last_os_error());
85 }
86 Ok(DatabaseFileIdentity {
87 volume_serial: info.VolumeSerialNumber,
88 file_id: info.FileId.Identifier,
89 })
90}
91
92#[cfg(windows)]
93pub fn sqlite_opened_file_identity(
94 conn: &rusqlite::Connection,
95) -> Result<DatabaseFileIdentity, crate::error::SqliteError> {
96 use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
97
98 let mut handle: HANDLE = std::ptr::null_mut();
99 let result = unsafe {
102 rusqlite::ffi::sqlite3_file_control(
103 conn.handle(),
104 c"main".as_ptr(),
105 rusqlite::ffi::SQLITE_FCNTL_WIN32_GET_HANDLE,
106 (&raw mut handle).cast(),
107 )
108 };
109 if result != rusqlite::ffi::SQLITE_OK || handle.is_null() || handle == INVALID_HANDLE_VALUE {
110 return Err(crate::error::SqliteError::InvalidData(format!(
111 "cannot inspect opened SQLite database handle (file control {result})"
112 )));
113 }
114 Ok(identity_from_handle(handle)?)
115}