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;