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;