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 crate::handle::{CapturedHandle, HandleCaptureError};
30use crate::outcome::{Outcome, perform_bool};
31
32/// An owned, marshalable parameter set for `GetFileInformationByHandle`.
33///
34/// # Example
35///
36/// ```
37/// use std::fs;
38/// use std::os::windows::io::AsHandle;
39///
40/// use windows_namespace_request_sys::file_info::QueryFileInformationByHandle;
41/// use windows_namespace_request_sys::CapturedHandle;
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(
48///     CapturedHandle::capture(file.as_handle())?,
49/// )
50/// .perform()?;
51///
52/// // The 64-bit file index this call reports in one shot, which the Ex form's
53/// // FileIdInfo does not give in this shape.
54/// let index = (u64::from(information.nFileIndexHigh) << 32)
55///     | u64::from(information.nFileIndexLow);
56/// assert_ne!(index, 0);
57/// assert_eq!(information.nFileSizeLow, b"example".len() as u32);
58/// # drop(file);
59/// # fs::remove_file(&path)?;
60/// # Ok::<(), Box<dyn std::error::Error>>(())
61/// ```
62#[derive(Debug)]
63#[must_use = "an unperformed request queries nothing"]
64pub struct QueryFileInformationByHandle {
65    handle: CapturedHandle,
66}
67
68impl QueryFileInformationByHandle {
69    /// Begins a request against `handle`.
70    pub fn new(handle: CapturedHandle) -> Self {
71        Self { handle }
72    }
73
74    /// The owned duplicate of the handle being queried.
75    pub fn handle(&self) -> &CapturedHandle {
76        &self.handle
77    }
78
79    /// Copies the request, duplicating the handle.
80    ///
81    /// # Errors
82    ///
83    /// Returns the handle-capture failure when the handle cannot be duplicated.
84    pub fn try_clone(&self) -> Result<Self, HandleCaptureError> {
85        Ok(Self {
86            handle: self.handle.try_clone()?,
87        })
88    }
89
90    /// Performs the query on the calling thread.
91    ///
92    /// # Errors
93    ///
94    /// Returns the raw Win32 code, unaltered.
95    pub fn perform(&self) -> Outcome<BY_HANDLE_FILE_INFORMATION> {
96        let mut information = MaybeUninit::<BY_HANDLE_FILE_INFORMATION>::uninit();
97
98        perform_bool(|| {
99            // SAFETY: the handle is a duplicate this request owns and keeps
100            // open across the call, and the out-parameter points at writable
101            // storage of exactly the right size.
102            unsafe { GetFileInformationByHandle(self.handle.raw(), information.as_mut_ptr()) }
103        })?;
104
105        // SAFETY: a successful call fully initialises the structure.
106        Ok(unsafe { information.assume_init() })
107    }
108}
109
110impl crate::request::Request for QueryFileInformationByHandle {
111    type Error = crate::Win32Error;
112    type Output = BY_HANDLE_FILE_INFORMATION;
113
114    fn perform(&self) -> Outcome<BY_HANDLE_FILE_INFORMATION> {
115        Self::perform(self)
116    }
117}
118
119#[cfg(test)]
120mod tests;