tokio_uring/fs/statx.rs
1use super::File;
2use crate::io::{cstr, SharedFd};
3use crate::runtime::driver::op::Op;
4use std::{ffi::CString, io, path::Path};
5
6impl File {
7 /// Returns statx(2) metadata for an open file via a uring call.
8 ///
9 /// The libc::statx structure returned is described in the statx(2) man page.
10 ///
11 /// This high level version of the statx function uses `flags` set to libc::AT_EMPTY_PATH and
12 /// `mask` set to libc::STATX_ALL which are described in the same man page.
13 ///
14 /// More specific uring statx(2) calls can be made with the StatxBuilder.
15 ///
16 /// # Examples
17 ///
18 /// ```no_run
19 /// use tokio_uring::fs::File;
20 ///
21 /// tokio_uring::start(async {
22 /// let f = File::create("foo.txt").await.unwrap();
23 ///
24 /// // Fetch file metadata
25 /// let statx = f.statx().await.unwrap();
26 ///
27 /// // Close the file
28 /// f.close().await.unwrap();
29 /// })
30 /// ```
31 pub async fn statx(&self) -> io::Result<libc::statx> {
32 let flags = libc::AT_EMPTY_PATH;
33 let mask = libc::STATX_ALL;
34 Op::statx(Some(self.fd.clone()), None, flags, mask)?.await
35 }
36
37 /// Returns a builder that can return statx(2) metadata for an open file using the uring
38 /// device.
39 ///
40 /// `flags` and `mask` can be changed from their defaults and a `path` that is absolule or
41 /// relative can also be provided.
42 ///
43 /// `flags` defaults to libc::AT_EMPTY_PATH.
44 ///
45 /// `mask` defaults to libc::STATX_ALL.
46 ///
47 /// Refer to statx(2) for details on the arguments and the returned value.
48 ///
49 /// A little from the man page:
50 ///
51 /// - statx(2) uses path, dirfd, and flags to identify the target file.
52 /// - statx(2) uses mask to tell the kernel which fields the caller is interested in.
53 ///
54 /// # Examples
55 ///
56 /// ```no_run
57 /// use tokio_uring::fs::File;
58 ///
59 /// tokio_uring::start(async {
60 /// let f = File::create("foo.txt").await.unwrap();
61 ///
62 /// // Fetch file metadata
63 /// let statx = f.statx_builder()
64 /// .flags(libc::AT_NO_AUTOMOUNT)
65 /// .statx().await.unwrap();
66 ///
67 /// // Close the file
68 /// f.close().await.unwrap();
69 /// })
70 /// ```
71 pub fn statx_builder(&self) -> StatxBuilder {
72 StatxBuilder {
73 file: Some(self.fd.clone()),
74 path: None,
75 flags: libc::AT_EMPTY_PATH,
76 mask: libc::STATX_ALL,
77 }
78 }
79}
80
81/// Returns statx(2) metadata for a path via a uring call.
82///
83/// The libc::statx structure returned is described in the statx(2) man page.
84///
85/// This high level version of the statx function uses `flags` set to libc::AT_EMPTY_PATH and
86/// `mask` set to libc::STATX_ALL which are described in the same man page.
87///
88/// And this version of the function does not work on an open file descriptor can be more expedient
89/// when an open file descriptor isn't necessary for other reasons anyway.
90///
91/// The path can be absolute or relative. A relative path is interpreted against the current
92/// working direcgtory.
93///
94/// More specific uring statx(2) calls can be made with the StatxBuilder.
95///
96/// # Examples
97///
98/// ```no_run
99/// tokio_uring::start(async {
100///
101/// // Fetch file metadata
102/// let statx = tokio_uring::fs::statx("foo.txt").await.unwrap();
103/// })
104/// ```
105pub async fn statx<P: AsRef<Path>>(path: P) -> io::Result<libc::statx> {
106 StatxBuilder::new().pathname(path).unwrap().statx().await
107}
108
109/// A builder used to make a uring statx(2) call.
110///
111/// This builder supports the `flags` and `mask` options and can be finished with a call to
112/// `statx()`.
113///
114/// See StatxBuilder::new for more details.
115pub struct StatxBuilder {
116 file: Option<SharedFd>,
117 path: Option<CString>,
118 flags: i32,
119 mask: u32,
120}
121
122impl Default for StatxBuilder {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl StatxBuilder {
129 /// Returns a builder to fully specify the arguments to the uring statx(2) operation.
130 ///
131 /// The libc::statx structure returned in described in the statx(2) man page.
132 ///
133 /// This builder defaults to having no open file descriptor and defaults `flags` to
134 /// libc::AT_EMPTY_PATH and `mask` to libc::STATX_ALL.
135 ///
136 /// Refer to the man page for details about the `flags`, `mask` values and returned structure,
137 /// libc::statx.
138 ///
139 /// # Examples
140 ///
141 /// ```no_run
142 /// tokio_uring::start(async {
143 /// let want_mode: u16 = 0o775;
144 ///
145 /// // Fetch file metadata
146 /// let statx = tokio_uring::fs::StatxBuilder::new()
147 /// .mask(libc::STATX_MODE)
148 /// .pathname("foo.txt").unwrap()
149 /// .statx().await.unwrap();
150 /// let got_mode = statx.stx_mode & 0o7777;
151 ///
152 /// if want_mode == got_mode {
153 /// println!("Success: wanted mode {want_mode:#o}, got mode {got_mode:#o}");
154 /// } else {
155 /// println!("Fail: wanted mode {want_mode:#o}, got mode {got_mode:#o}");
156 /// }
157 /// })
158 /// ```
159 #[must_use]
160 pub fn new() -> StatxBuilder {
161 StatxBuilder {
162 file: None,
163 path: None,
164 flags: libc::AT_EMPTY_PATH,
165 mask: libc::STATX_ALL,
166 }
167 }
168
169 /// Sets the `dirfd` option, setting or replacing the file descriptor which may be for a
170 /// directory but doesn't have to be. When used with a path, it should be a directory but when
171 /// used without a path, can be any file type. So `dirfd` is a bit of a misnomer but it is what
172 /// the statx(2) man page calls it.
173 ///
174 /// # Examples
175 ///
176 /// ```no_run
177 /// use tokio_uring::fs::{self, File};
178 ///
179 /// tokio_uring::start(async {
180 /// let dir = fs::OpenOptions::new()
181 /// .open("/home/linux")
182 /// .await.unwrap();
183 ///
184 /// // Fetch file metadata
185 /// let statx = fs::StatxBuilder::new()
186 /// .dirfd(&dir)
187 /// .mask(libc::STATX_TYPE)
188 /// .pathname(".cargo").unwrap()
189 /// .statx().await.unwrap();
190 ///
191 /// dir.close().await.unwrap();
192 /// })
193 /// ```
194 #[must_use]
195 pub fn dirfd(&mut self, file: &File) -> &mut Self {
196 self.file = Some(file.fd.clone());
197 self
198 }
199
200 /// Sets the `path` option, setting or replacing the path option to the command.
201 /// The path may be absolute or relative.
202 ///
203 /// # Examples
204 ///
205 /// ```no_run
206 /// use tokio_uring::fs::{self, File};
207 ///
208 /// tokio_uring::start(async {
209 /// let dir = fs::OpenOptions::new()
210 /// .open("/home/linux")
211 /// .await.unwrap();
212 ///
213 /// // Fetch file metadata
214 /// let statx = fs::StatxBuilder::new()
215 /// .dirfd(&dir)
216 /// .pathname(".cargo").unwrap()
217 /// .mask(libc::STATX_TYPE)
218 /// .statx().await.unwrap();
219 ///
220 /// dir.close().await.unwrap();
221 /// })
222 /// ```
223 pub fn pathname<P: AsRef<Path>>(&mut self, path: P) -> io::Result<&mut Self> {
224 self.path = Some(cstr(path.as_ref())?);
225 Ok(self)
226 }
227
228 /// Sets the `flags` option, replacing the default.
229 ///
230 /// See statx(2) for a full description of `flags`.
231 ///
232 /// # Examples
233 ///
234 /// ```no_run
235 /// tokio_uring::start(async {
236 /// // Fetch file metadata
237 /// let statx = tokio_uring::fs::StatxBuilder::new()
238 /// .flags(libc::AT_NO_AUTOMOUNT)
239 /// .pathname("foo.txt").unwrap()
240 /// .statx().await.unwrap();
241 /// })
242 /// ```
243 #[must_use]
244 pub fn flags(&mut self, flags: i32) -> &mut Self {
245 self.flags = flags;
246 self
247 }
248
249 /// Sets the `mask` option, replacing the default.
250 ///
251 /// # Examples
252 ///
253 /// ```no_run
254 /// tokio_uring::start(async {
255 /// // Fetch file metadata
256 /// let statx = tokio_uring::fs::StatxBuilder::new()
257 /// .mask(libc::STATX_BASIC_STATS)
258 /// .pathname("foo.txt").unwrap()
259 /// .statx().await.unwrap();
260 /// })
261 /// ```
262 #[must_use]
263 pub fn mask(&mut self, mask: u32) -> &mut Self {
264 self.mask = mask;
265 self
266 }
267
268 /// Returns the metadata requested for the optional open file. If no open file was provided,
269 /// the metadata for the current working directory is returned.
270 ///
271 /// # Examples
272 ///
273 /// ```no_run
274 /// use tokio_uring::fs::{self, File};
275 ///
276 /// tokio_uring::start(async {
277 /// let dir = fs::OpenOptions::new()
278 /// .open("/home/linux")
279 /// .await.unwrap();
280 ///
281 /// // Fetch file metadata
282 /// let statx = fs::StatxBuilder::new()
283 /// .dirfd(&dir)
284 /// .pathname(".cargo").unwrap()
285 /// .mask(libc::STATX_TYPE)
286 /// .statx().await.unwrap();
287 ///
288 /// dir.close().await.unwrap();
289 /// })
290 /// ```
291 pub async fn statx(&mut self) -> io::Result<libc::statx> {
292 // TODO should the statx() terminator be renamed to something like submit()?
293 let fd = self.file.take();
294 let path = self.path.take();
295 Op::statx(fd, path, self.flags, self.mask)?.await
296 }
297}
298
299// TODO consider replacing this with a Statx struct with useful helper methods.
300/// Returns two bools, is_dir and is_regfile.
301///
302/// They both can't be true at the same time and there are many reasons they may both be false.
303#[allow(dead_code)]
304pub async fn is_dir_regfile<P: AsRef<Path>>(path: P) -> (bool, bool) {
305 let mut builder = crate::fs::StatxBuilder::new();
306 if builder.mask(libc::STATX_TYPE).pathname(path).is_err() {
307 return (false, false);
308 }
309
310 let res = builder.statx().await;
311 match res {
312 Ok(statx) => (
313 (u32::from(statx.stx_mode) & libc::S_IFMT) == libc::S_IFDIR,
314 (u32::from(statx.stx_mode) & libc::S_IFMT) == libc::S_IFREG,
315 ),
316 Err(_) => (false, false),
317 }
318}