tokio/fs/try_exists.rs
1use crate::fs::asyncify;
2
3use std::io;
4use std::path::Path;
5
6/// Returns `Ok(true)` if the path points at an existing entity.
7///
8/// This function will traverse symbolic links to query information about the
9/// destination file. In case of broken symbolic links this will return `Ok(false)`.
10///
11/// This is the async equivalent of [`std::path::Path::try_exists`][std].
12///
13/// [std]: fn@std::path::Path::try_exists
14///
15/// # Examples
16///
17/// ```no_run
18/// use tokio::fs;
19///
20/// # async fn dox() -> std::io::Result<()> {
21/// fs::try_exists("foo.txt").await?;
22/// # Ok(())
23/// # }
24/// ```
25pub async fn try_exists(path: impl AsRef<Path>) -> io::Result<bool> {
26 let path = path.as_ref();
27
28 #[cfg(all(
29 tokio_unstable,
30 feature = "io-uring",
31 feature = "rt",
32 feature = "fs",
33 // libc::statx is only supported on these platforms
34 // FIXME: Add musl target env when our minimum supported
35 // rust version is 1.93. To clarify, statx support is
36 // introduced to musl in 1.25 as mentioned officially here:
37 // https://musl.libc.org/releases.html.
38 // However, rustup target_env building for *-linux-musl
39 // uses 1.25 musl on all *-linux-musl platforms starting
40 // in 1.93 stable rust version.
41 // https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
42 any(target_env = "gnu", target_os = "android")
43 ))]
44 {
45 if let Ok(handle) = crate::runtime::Handle::try_current() {
46 if let Some(driver_handle) = handle.inner.driver().io.as_ref() {
47 if driver_handle
48 .check_and_init(io_uring::opcode::Statx::CODE)
49 .await?
50 {
51 return try_exists_uring(path).await;
52 }
53 }
54 }
55 }
56
57 try_exists_spawn_blocking(path).await
58}
59
60cfg_io_uring! {
61 #[inline]
62 #[cfg(
63 // libc::statx is only supported on these platforms
64 // FIXME: Add musl target env when our minimum supported
65 // rust version is 1.93. To clarify, statx support is
66 // introduced to musl in 1.25 as mentioned officially here:
67 // https://musl.libc.org/releases.html.
68 // However, rustup target_env building for *-linux-musl
69 // uses 1.25 musl on all *-linux-musl platforms starting
70 // in 1.93 stable rust version.
71 // https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
72 any(target_env = "gnu", target_os = "android")
73 )]
74 async fn try_exists_uring(path: &Path) -> io::Result<bool> {
75 use crate::runtime::driver::op::Op;
76
77 match Op::metadata(path)?.await {
78 Ok(_) => Ok(true),
79 Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
80 Err(error) => Err(error),
81 }
82 }
83}
84
85async fn try_exists_spawn_blocking(path: &Path) -> io::Result<bool> {
86 let path = path.to_owned();
87 // FIXME: When MSRV is 1.81, change this to
88 // std::fs::exists() to be consistent with
89 // all other tokio::fs operations
90 asyncify(move || path.try_exists()).await
91}