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}