Skip to main content

windows_namespace_request_sys/
file_info.rs

1// Copyright (c) Mike Grier.
2
3//! The `GetFileInformationByHandle` entry.
4//!
5//! Entry 6 of the audited catalogue: the **non-`Ex`** call, returning a
6//! `BY_HANDLE_FILE_INFORMATION`.
7//!
8//! # Why this is not a class of the `Ex` entry
9//!
10//! It is a distinct Win32 call with its own signature, its own out-parameter,
11//! and no class argument at all -- so the one-entry-per-Win32-call rule makes
12//! it its own entry. The two overlap in what they report and are not
13//! interchangeable: this call yields the link count and a 64-bit file index in
14//! one shot, where the `Ex` form's `FileIdInfo` gives a 128-bit id and no link
15//! count. The watcher uses this one where the `Ex` form would not do.
16//!
17//! # A pure read
18//!
19//! Measured: this call does **not** disturb a directory enumeration in
20//! progress, on the handle or on a duplicate of it. It composes freely with
21//! [`crate::query`]'s enumeration classes.
22
23use std::mem::MaybeUninit;
24
25use windows_sys::Win32::Storage::FileSystem::{
26    BY_HANDLE_FILE_INFORMATION, GetFileInformationByHandle,
27};
28
29use win_shared_os_owned_handle::SharedHandle;
30
31use crate::handle::SharedRaw;
32use crate::outcome::{Outcome, perform_bool};
33
34/// An owned, marshalable parameter set for `GetFileInformationByHandle`.
35///
36/// # Example
37///
38/// ```
39/// use std::fs;
40///
41/// use windows_namespace_request_sys::file_info::QueryFileInformationByHandle;
42///
43/// let path = std::env::temp_dir().join(format!("wnrs-fi-{}.tmp", std::process::id()));
44/// fs::write(&path, b"example")?;
45/// let file = fs::File::open(&path)?;
46///
47/// let information = QueryFileInformationByHandle::new(file).perform()?;
48///
49/// // The 64-bit file index this call reports in one shot, which the Ex form's
50/// // FileIdInfo does not give in this shape.
51/// let index = (u64::from(information.nFileIndexHigh) << 32)
52///     | u64::from(information.nFileIndexLow);
53/// assert_ne!(index, 0);
54/// assert_eq!(information.nFileSizeLow, b"example".len() as u32);
55/// # fs::remove_file(&path)?;
56/// # Ok::<(), Box<dyn std::error::Error>>(())
57/// ```
58#[derive(Clone, Debug)]
59#[must_use = "an unperformed request queries nothing"]
60pub struct QueryFileInformationByHandle {
61    handle: SharedHandle,
62}
63
64impl QueryFileInformationByHandle {
65    /// Begins a request against `handle`, which the request holds as one more holder of it.
66    pub fn new(handle: impl Into<SharedHandle>) -> Self {
67        Self {
68            handle: handle.into(),
69        }
70    }
71
72    /// The handle being queried, shared with its other holders.
73    pub fn handle(&self) -> &SharedHandle {
74        &self.handle
75    }
76
77    /// Performs the query on the calling thread.
78    ///
79    /// # Errors
80    ///
81    /// Returns the raw Win32 code, unaltered.
82    pub fn perform(&self) -> Outcome<BY_HANDLE_FILE_INFORMATION> {
83        let mut information = MaybeUninit::<BY_HANDLE_FILE_INFORMATION>::uninit();
84
85        perform_bool(|| {
86            // SAFETY: the handle is one this request holds and keeps
87            // open across the call, and the out-parameter points at writable
88            // storage of exactly the right size.
89            unsafe { GetFileInformationByHandle(self.handle.raw(), information.as_mut_ptr()) }
90        })?;
91
92        // SAFETY: a successful call fully initialises the structure.
93        Ok(unsafe { information.assume_init() })
94    }
95}
96
97impl crate::request::Request for QueryFileInformationByHandle {
98    type Error = crate::Win32Error;
99    type Output = BY_HANDLE_FILE_INFORMATION;
100
101    fn perform(&self) -> Outcome<BY_HANDLE_FILE_INFORMATION> {
102        Self::perform(self)
103    }
104}
105
106#[cfg(test)]
107mod tests;