windows_namespace_request_sys/file_id.rs
1// Copyright (c) Mike Grier.
2
3//! The `FileIdInfo` class of `GetFileInformationByHandleEx`, read as a typed value.
4//!
5//! [`QueryFileInformation`](crate::query::QueryFileInformation) makes the same call for every class
6//! and returns the bytes it wrote. For this class the bytes are a `FILE_ID_INFO`, and reading one
7//! out of them needs `unsafe`, so a consumer that forbids it cannot. This entry makes the call with a
8//! `FILE_ID_INFO` as the buffer and returns the two fields as values. It is still one Win32 call:
9//! a typed reading of one class, not a second operation (see `D-31` in the crate's design notes).
10//!
11//! # What the values are, and what they are not
12//!
13//! The `FILE_ID_INFO` page describes `VolumeSerialNumber` as "the serial number of the volume that
14//! contains a file" and `FileId` as "the 128-bit file identifier for the file. The file identifier
15//! and the volume serial number uniquely identify a file on a single computer." Both are carried as
16//! the call wrote them: the serial as a `u64`, the identifier as its 16 bytes. Nothing here says
17//! what either means beyond that, and the identifier is not folded into an integer, which would
18//! impose a byte order the call does not state.
19
20use std::mem::MaybeUninit;
21
22use windows_sys::Win32::Storage::FileSystem::{FILE_ID_INFO, GetFileInformationByHandleEx};
23
24use win_shared_os_owned_handle::SharedHandle;
25
26use crate::handle::SharedRaw;
27use crate::outcome::{Outcome, perform_bool};
28use crate::query::FileInformationClass;
29
30/// The volume serial number and the 128-bit file identifier `FileIdInfo` reports, as the call
31/// wrote them.
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33pub struct FileIdInfo {
34 volume_serial_number: u64,
35 file_id: [u8; 16],
36}
37
38impl FileIdInfo {
39 /// A value with these fields, for a caller that holds them from elsewhere.
40 #[must_use]
41 pub const fn new(volume_serial_number: u64, file_id: [u8; 16]) -> Self {
42 Self {
43 volume_serial_number,
44 file_id,
45 }
46 }
47
48 /// `VolumeSerialNumber`: the serial number of the volume that contains the file.
49 #[must_use]
50 pub const fn volume_serial_number(&self) -> u64 {
51 self.volume_serial_number
52 }
53
54 /// `FileId`: the 128-bit file identifier, as its 16 bytes.
55 #[must_use]
56 pub const fn file_id(&self) -> [u8; 16] {
57 self.file_id
58 }
59}
60
61/// The two fields of what the call wrote. Private, so the dependency's structure is not part of this
62/// crate's surface.
63fn from_raw(info: &FILE_ID_INFO) -> FileIdInfo {
64 FileIdInfo::new(info.VolumeSerialNumber, info.FileId.Identifier)
65}
66
67/// An owned, marshalable parameter set for `GetFileInformationByHandleEx` with `FileIdInfo`.
68///
69/// # Example
70///
71/// ```
72/// use std::fs;
73///
74/// use windows_namespace_request_sys::file_id::QueryFileIdInfo;
75///
76/// let path = std::env::temp_dir().join(format!("wnrs-fid-{}.tmp", std::process::id()));
77/// fs::write(&path, b"example")?;
78///
79/// let first = fs::File::open(&path)?;
80/// let second = fs::File::open(&path)?;
81/// let one = QueryFileIdInfo::new(first).perform()?;
82/// let two = QueryFileIdInfo::new(second).perform()?;
83///
84/// // Two opens of one file report one identity.
85/// assert_eq!(one, two);
86/// # fs::remove_file(&path)?;
87/// # Ok::<(), Box<dyn std::error::Error>>(())
88/// ```
89#[derive(Clone, Debug)]
90#[must_use = "an unperformed request queries nothing"]
91pub struct QueryFileIdInfo {
92 handle: SharedHandle,
93}
94
95impl QueryFileIdInfo {
96 /// Begins a request against `handle`, which the request holds as one more holder of it.
97 pub fn new(handle: impl Into<SharedHandle>) -> Self {
98 Self {
99 handle: handle.into(),
100 }
101 }
102
103 /// The handle being queried, shared with its other holders.
104 pub fn handle(&self) -> &SharedHandle {
105 &self.handle
106 }
107
108 /// Performs the query on the calling thread.
109 ///
110 /// # Errors
111 ///
112 /// Returns the raw Win32 code, unaltered.
113 pub fn perform(&self) -> Outcome<FileIdInfo> {
114 let mut information = MaybeUninit::<FILE_ID_INFO>::uninit();
115 let size = u32::try_from(size_of::<FILE_ID_INFO>())
116 .expect("this fixed, small struct's size always fits a u32");
117
118 perform_bool(|| {
119 // SAFETY: the handle is one this request holds and keeps open across the call, and
120 // the buffer is writable `FILE_ID_INFO` storage of exactly the size passed, which is the
121 // structure this class writes.
122 unsafe {
123 GetFileInformationByHandleEx(
124 self.handle.raw(),
125 FileInformationClass::ID.as_raw(),
126 information.as_mut_ptr().cast(),
127 size,
128 )
129 }
130 })?;
131
132 // SAFETY: a successful call of this class fills the whole structure.
133 Ok(from_raw(unsafe { information.assume_init_ref() }))
134 }
135}
136
137impl crate::request::Request for QueryFileIdInfo {
138 type Error = crate::Win32Error;
139 type Output = FileIdInfo;
140
141 fn perform(&self) -> Outcome<FileIdInfo> {
142 Self::perform(self)
143 }
144}
145
146#[cfg(test)]
147mod tests;