Skip to main content

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;