Skip to main content

windows_namespace_request_sys/
file_basic.rs

1// Copyright (c) Mike Grier.
2
3//! The `FileBasicInfo` 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_BASIC_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_BASIC_INFO` as the buffer and returns its five fields as values. It is still one Win32 call:
9//! a typed reading of one class, not a second operation, shaped as
10//! [`QueryFileIdInfo`](crate::file_id::QueryFileIdInfo) is (`D-33` in the crate's design notes).
11//!
12//! # What the values are, and what they are not
13//!
14//! The `FILE_BASIC_INFO` page gives the four times "in FILETIME format, which is a 64-bit value
15//! representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)", and the
16//! attributes as the file attribute constants. All five are carried as the call wrote them: the
17//! times as `i64`, the attributes as their raw `u32`. Nothing here converts a time, names an
18//! attribute, or says what either means beyond the page.
19
20use std::mem::MaybeUninit;
21
22use windows_sys::Win32::Storage::FileSystem::{FILE_BASIC_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 four times and the attributes `FileBasicInfo` reports, as the call wrote them.
31#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32pub struct FileBasicInfo {
33    creation_time: i64,
34    last_access_time: i64,
35    last_write_time: i64,
36    change_time: i64,
37    file_attributes: u32,
38}
39
40impl FileBasicInfo {
41    /// A value with these fields, for a caller that holds them from elsewhere.
42    #[must_use]
43    pub const fn new(
44        creation_time: i64,
45        last_access_time: i64,
46        last_write_time: i64,
47        change_time: i64,
48        file_attributes: u32,
49    ) -> Self {
50        Self {
51            creation_time,
52            last_access_time,
53            last_write_time,
54            change_time,
55            file_attributes,
56        }
57    }
58
59    /// `CreationTime`: when the file was created, in `FILETIME` units.
60    #[must_use]
61    pub const fn creation_time(&self) -> i64 {
62        self.creation_time
63    }
64
65    /// `LastAccessTime`: when the file was last accessed, in `FILETIME` units.
66    #[must_use]
67    pub const fn last_access_time(&self) -> i64 {
68        self.last_access_time
69    }
70
71    /// `LastWriteTime`: when the file was last written to, in `FILETIME` units. The page relates it
72    /// to the data stream.
73    #[must_use]
74    pub const fn last_write_time(&self) -> i64 {
75        self.last_write_time
76    }
77
78    /// `ChangeTime`: when the file was changed, in `FILETIME` units. The page relates it to the
79    /// file's metadata, "such as renames and attribute changes".
80    #[must_use]
81    pub const fn change_time(&self) -> i64 {
82        self.change_time
83    }
84
85    /// `FileAttributes`: the file attribute constants, as their raw bits.
86    #[must_use]
87    pub const fn file_attributes(&self) -> u32 {
88        self.file_attributes
89    }
90}
91
92/// The five fields of what the call wrote. Private, so the dependency's structure is not part of this
93/// crate's surface.
94fn from_raw(info: &FILE_BASIC_INFO) -> FileBasicInfo {
95    FileBasicInfo::new(
96        info.CreationTime,
97        info.LastAccessTime,
98        info.LastWriteTime,
99        info.ChangeTime,
100        info.FileAttributes,
101    )
102}
103
104/// An owned, marshalable parameter set for `GetFileInformationByHandleEx` with `FileBasicInfo`.
105///
106/// # Example
107///
108/// ```
109/// use std::fs;
110///
111/// use windows_namespace_request_sys::file_basic::QueryFileBasicInfo;
112/// use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_DIRECTORY;
113///
114/// let path = std::env::temp_dir().join(format!("wnrs-fb-{}.tmp", std::process::id()));
115/// fs::write(&path, b"example")?;
116///
117/// let basic = QueryFileBasicInfo::new(fs::File::open(&path)?).perform()?;
118///
119/// // The attributes are the call's bits; a file is not a directory.
120/// assert_eq!(basic.file_attributes() & FILE_ATTRIBUTE_DIRECTORY, 0);
121/// # fs::remove_file(&path)?;
122/// # Ok::<(), Box<dyn std::error::Error>>(())
123/// ```
124#[derive(Clone, Debug)]
125#[must_use = "an unperformed request queries nothing"]
126pub struct QueryFileBasicInfo {
127    handle: SharedHandle,
128}
129
130impl QueryFileBasicInfo {
131    /// Begins a request against `handle`, which the request holds as one more holder of it.
132    pub fn new(handle: impl Into<SharedHandle>) -> Self {
133        Self {
134            handle: handle.into(),
135        }
136    }
137
138    /// The handle being queried, shared with its other holders.
139    pub fn handle(&self) -> &SharedHandle {
140        &self.handle
141    }
142
143    /// Performs the query on the calling thread.
144    ///
145    /// # Errors
146    ///
147    /// Returns the raw Win32 code, unaltered.
148    pub fn perform(&self) -> Outcome<FileBasicInfo> {
149        let mut information = MaybeUninit::<FILE_BASIC_INFO>::uninit();
150        let size = u32::try_from(size_of::<FILE_BASIC_INFO>())
151            .expect("this fixed, small struct's size always fits a u32");
152
153        perform_bool(|| {
154            // SAFETY: the handle is one this request holds and keeps open across the call, and
155            // the buffer is writable `FILE_BASIC_INFO` storage of exactly the size passed, which is
156            // the structure this class writes.
157            unsafe {
158                GetFileInformationByHandleEx(
159                    self.handle.raw(),
160                    FileInformationClass::BASIC.as_raw(),
161                    information.as_mut_ptr().cast(),
162                    size,
163                )
164            }
165        })?;
166
167        // SAFETY: a successful call of this class fills the whole structure.
168        Ok(from_raw(unsafe { information.assume_init_ref() }))
169    }
170}
171
172impl crate::request::Request for QueryFileBasicInfo {
173    type Error = crate::Win32Error;
174    type Output = FileBasicInfo;
175
176    fn perform(&self) -> Outcome<FileBasicInfo> {
177        Self::perform(self)
178    }
179}
180
181#[cfg(test)]
182mod tests;