From fe74287a160d0f953812deb057c34cc21a0fe9b8 Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) Kassimo Qian" Date: Sat, 4 Dec 2021 12:35:18 -0800 Subject: [PATCH 1/9] Add `fs::try_exists` --- tokio/src/fs/mod.rs | 3 +++ tokio/src/fs/try_exists.rs | 31 +++++++++++++++++++++++++++ tokio/tests/fs_try_exists.rs | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 tokio/src/fs/try_exists.rs create mode 100644 tokio/tests/fs_try_exists.rs diff --git a/tokio/src/fs/mod.rs b/tokio/src/fs/mod.rs index 3afefc6e3fb..30ce6a91a35 100644 --- a/tokio/src/fs/mod.rs +++ b/tokio/src/fs/mod.rs @@ -102,6 +102,9 @@ pub use self::write::write; mod copy; pub use self::copy::copy; +mod try_exists; +pub use self::try_exists::try_exists; + #[cfg(test)] mod mocks; diff --git a/tokio/src/fs/try_exists.rs b/tokio/src/fs/try_exists.rs new file mode 100644 index 00000000000..d7e5154f3e4 --- /dev/null +++ b/tokio/src/fs/try_exists.rs @@ -0,0 +1,31 @@ +use crate::fs::asyncify; +use std::path::Path; + +/// Returns `Ok(true)` if the path points at an existing entity. +/// +/// This function will traverse symbolic links to query information about the +/// destination file. In case of broken symbolic links this will return `Ok(false)`. +/// +/// This is the async equivalent of [`std::fs::try_exists`][std]. +/// +/// [std]: fn@std::fs::try_exists +/// +/// # Examples +/// +/// ```no_run +/// use tokio::fs; +/// +/// # async fn dox() -> std::io::Result<()> { +/// fs::try_exists("foo.txt").await?; +/// # Ok(()) +/// # } +/// ``` + +pub async fn try_exists(path: impl AsRef) -> Result { + let path = path.as_ref().to_owned(); + match asyncify(move || std::fs::metadata(path)).await { + Ok(_) => Ok(true), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error), + } +} diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs new file mode 100644 index 00000000000..b6813a1fe6b --- /dev/null +++ b/tokio/tests/fs_try_exists.rs @@ -0,0 +1,41 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] + +use std::os::unix::prelude::PermissionsExt; + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn try_exists() { + let dir = tempdir().unwrap(); + + let existing_path = dir.path().join("foo.txt"); + fs::write(&existing_path, b"Hello File!").await.unwrap(); + let nonexisting_path = dir.path().join("bar.txt"); + + assert_eq!(fs::try_exists(existing_path).await.unwrap(), true); + assert_eq!(fs::try_exists(nonexisting_path).await.unwrap(), false); + + let permission_denied_directory_path = dir.path().join("baz"); + fs::create_dir(&permission_denied_directory_path) + .await + .unwrap(); + let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); + fs::write(&permission_denied_file_path, b"Hello File!") + .await + .unwrap(); + let mut perms = tokio::fs::metadata(&permission_denied_directory_path) + .await + .unwrap() + .permissions(); + perms.set_mode(0o244); + fs::set_permissions(&permission_denied_directory_path, perms) + .await + .unwrap(); + let permission_denied_result = fs::try_exists(permission_denied_file_path).await; + assert_eq!( + permission_denied_result.err().unwrap().kind(), + std::io::ErrorKind::PermissionDenied + ); +} From e2c6c686f86c18cf48cf8d0864bb0960e220ae7d Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) Kassimo Qian" Date: Sat, 4 Dec 2021 13:10:16 -0800 Subject: [PATCH 2/9] Opt out FreeBSD test on permission since root user can always stat --- tokio/tests/fs_try_exists.rs | 46 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs index b6813a1fe6b..04c359ef30d 100644 --- a/tokio/tests/fs_try_exists.rs +++ b/tokio/tests/fs_try_exists.rs @@ -17,25 +17,29 @@ async fn try_exists() { assert_eq!(fs::try_exists(existing_path).await.unwrap(), true); assert_eq!(fs::try_exists(nonexisting_path).await.unwrap(), false); - let permission_denied_directory_path = dir.path().join("baz"); - fs::create_dir(&permission_denied_directory_path) - .await - .unwrap(); - let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); - fs::write(&permission_denied_file_path, b"Hello File!") - .await - .unwrap(); - let mut perms = tokio::fs::metadata(&permission_denied_directory_path) - .await - .unwrap() - .permissions(); - perms.set_mode(0o244); - fs::set_permissions(&permission_denied_directory_path, perms) - .await - .unwrap(); - let permission_denied_result = fs::try_exists(permission_denied_file_path).await; - assert_eq!( - permission_denied_result.err().unwrap().kind(), - std::io::ErrorKind::PermissionDenied - ); + // FreeBSD root user always has permission to stat. + #[cfg(not(freebsd))] + { + let permission_denied_directory_path = dir.path().join("baz"); + fs::create_dir(&permission_denied_directory_path) + .await + .unwrap(); + let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); + fs::write(&permission_denied_file_path, b"Hello File!") + .await + .unwrap(); + let mut perms = tokio::fs::metadata(&permission_denied_directory_path) + .await + .unwrap() + .permissions(); + perms.set_mode(0o244); + fs::set_permissions(&permission_denied_directory_path, perms) + .await + .unwrap(); + let permission_denied_result = fs::try_exists(permission_denied_file_path).await; + assert_eq!( + permission_denied_result.err().unwrap().kind(), + std::io::ErrorKind::PermissionDenied + ); + } } From 7643a4dd3eab1548d6878c7e62921a813cdf735a Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 13:21:15 -0800 Subject: [PATCH 3/9] Add `fs::try_exists` --- tokio/tests/fs_try_exists.rs | 45 ------------------------------------ 1 file changed, 45 deletions(-) delete mode 100644 tokio/tests/fs_try_exists.rs diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs deleted file mode 100644 index 04c359ef30d..00000000000 --- a/tokio/tests/fs_try_exists.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![warn(rust_2018_idioms)] -#![cfg(feature = "full")] - -use std::os::unix::prelude::PermissionsExt; - -use tempfile::tempdir; -use tokio::fs; - -#[tokio::test] -async fn try_exists() { - let dir = tempdir().unwrap(); - - let existing_path = dir.path().join("foo.txt"); - fs::write(&existing_path, b"Hello File!").await.unwrap(); - let nonexisting_path = dir.path().join("bar.txt"); - - assert_eq!(fs::try_exists(existing_path).await.unwrap(), true); - assert_eq!(fs::try_exists(nonexisting_path).await.unwrap(), false); - - // FreeBSD root user always has permission to stat. - #[cfg(not(freebsd))] - { - let permission_denied_directory_path = dir.path().join("baz"); - fs::create_dir(&permission_denied_directory_path) - .await - .unwrap(); - let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); - fs::write(&permission_denied_file_path, b"Hello File!") - .await - .unwrap(); - let mut perms = tokio::fs::metadata(&permission_denied_directory_path) - .await - .unwrap() - .permissions(); - perms.set_mode(0o244); - fs::set_permissions(&permission_denied_directory_path, perms) - .await - .unwrap(); - let permission_denied_result = fs::try_exists(permission_denied_file_path).await; - assert_eq!( - permission_denied_result.err().unwrap().kind(), - std::io::ErrorKind::PermissionDenied - ); - } -} From ec6bc2c08323b10508ad84d96de0a7df53f2b2a6 Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 13:36:03 -0800 Subject: [PATCH 4/9] Update to latest code head --- tokio/src/fs/try_exists.rs | 14 ++++++------ tokio/tests/fs_try_exists.rs | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 tokio/tests/fs_try_exists.rs diff --git a/tokio/src/fs/try_exists.rs b/tokio/src/fs/try_exists.rs index d7e5154f3e4..4bfaf041d01 100644 --- a/tokio/src/fs/try_exists.rs +++ b/tokio/src/fs/try_exists.rs @@ -1,4 +1,6 @@ use crate::fs::asyncify; + +use std::io; use std::path::Path; /// Returns `Ok(true)` if the path points at an existing entity. @@ -6,9 +8,9 @@ use std::path::Path; /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `Ok(false)`. /// -/// This is the async equivalent of [`std::fs::try_exists`][std]. +/// This is the async equivalent of [`std::path::Path::try_exists`][std]. /// -/// [std]: fn@std::fs::try_exists +/// [std]: fn@std::path::Path::try_exists /// /// # Examples /// @@ -21,11 +23,7 @@ use std::path::Path; /// # } /// ``` -pub async fn try_exists(path: impl AsRef) -> Result { +pub async fn try_exists(path: impl AsRef) -> io::Result { let path = path.as_ref().to_owned(); - match asyncify(move || std::fs::metadata(path)).await { - Ok(_) => Ok(true), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), - Err(error) => Err(error), - } + asyncify(move || path.as_path().try_exists()).await } diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs new file mode 100644 index 00000000000..3a0b61e4b4e --- /dev/null +++ b/tokio/tests/fs_try_exists.rs @@ -0,0 +1,41 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support file operations + +use std::os::unix::prelude::PermissionsExt; + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn try_exists() { + let dir = tempdir().unwrap(); + + let existing_path = dir.path().join("foo.txt"); + fs::write(&existing_path, b"Hello File!").await.unwrap(); + let nonexisting_path = dir.path().join("bar.txt"); + + assert_eq!(fs::try_exists(existing_path).await.unwrap(), true); + assert_eq!(fs::try_exists(nonexisting_path).await.unwrap(), false); + + let permission_denied_directory_path = dir.path().join("baz"); + fs::create_dir(&permission_denied_directory_path) + .await + .unwrap(); + let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); + fs::write(&permission_denied_file_path, b"Hello File!") + .await + .unwrap(); + let mut perms = tokio::fs::metadata(&permission_denied_directory_path) + .await + .unwrap() + .permissions(); + perms.set_mode(0o244); + fs::set_permissions(&permission_denied_directory_path, perms) + .await + .unwrap(); + let permission_denied_result = fs::try_exists(permission_denied_file_path).await; + assert_eq!( + permission_denied_result.err().unwrap().kind(), + std::io::ErrorKind::PermissionDenied + ); +} From adf82daea19afa877c391c2e50a4cacef5113073 Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 13:39:58 -0800 Subject: [PATCH 5/9] clippy --- tokio/tests/fs_try_exists.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs index 3a0b61e4b4e..6d6739c6a53 100644 --- a/tokio/tests/fs_try_exists.rs +++ b/tokio/tests/fs_try_exists.rs @@ -14,8 +14,8 @@ async fn try_exists() { fs::write(&existing_path, b"Hello File!").await.unwrap(); let nonexisting_path = dir.path().join("bar.txt"); - assert_eq!(fs::try_exists(existing_path).await.unwrap(), true); - assert_eq!(fs::try_exists(nonexisting_path).await.unwrap(), false); + assert!(fs::try_exists(existing_path).await.unwrap()); + assert!(!fs::try_exists(nonexisting_path).await.unwrap()); let permission_denied_directory_path = dir.path().join("baz"); fs::create_dir(&permission_denied_directory_path) From e8dbee01287d4fbaadc613baccd429957521f6c5 Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 15:21:13 -0800 Subject: [PATCH 6/9] Revert to old impl that works for 1.49.0 --- tokio/src/fs/try_exists.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tokio/src/fs/try_exists.rs b/tokio/src/fs/try_exists.rs index 4bfaf041d01..3b488a2c2a2 100644 --- a/tokio/src/fs/try_exists.rs +++ b/tokio/src/fs/try_exists.rs @@ -22,8 +22,11 @@ use std::path::Path; /// # Ok(()) /// # } /// ``` - pub async fn try_exists(path: impl AsRef) -> io::Result { let path = path.as_ref().to_owned(); - asyncify(move || path.as_path().try_exists()).await + match asyncify(move || std::fs::metadata(path)).await { + Ok(_) => Ok(true), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error), + } } From de09cd2ae012428c0edfe7d9d15a1478ac5d4487 Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 15:27:30 -0800 Subject: [PATCH 7/9] Only enable perm modification test for unix --- tokio/tests/fs_try_exists.rs | 48 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs index 6d6739c6a53..1962c8d4114 100644 --- a/tokio/tests/fs_try_exists.rs +++ b/tokio/tests/fs_try_exists.rs @@ -1,8 +1,6 @@ #![warn(rust_2018_idioms)] #![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support file operations -use std::os::unix::prelude::PermissionsExt; - use tempfile::tempdir; use tokio::fs; @@ -16,26 +14,30 @@ async fn try_exists() { assert!(fs::try_exists(existing_path).await.unwrap()); assert!(!fs::try_exists(nonexisting_path).await.unwrap()); + #[cfg(unix)] + { + use std::os::unix::prelude::PermissionsExt; + let permission_denied_directory_path = dir.path().join("baz"); + fs::create_dir(&permission_denied_directory_path) + .await + .unwrap(); + let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); + fs::write(&permission_denied_file_path, b"Hello File!") + .await + .unwrap(); + let mut perms = tokio::fs::metadata(&permission_denied_directory_path) + .await + .unwrap() + .permissions(); - let permission_denied_directory_path = dir.path().join("baz"); - fs::create_dir(&permission_denied_directory_path) - .await - .unwrap(); - let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); - fs::write(&permission_denied_file_path, b"Hello File!") - .await - .unwrap(); - let mut perms = tokio::fs::metadata(&permission_denied_directory_path) - .await - .unwrap() - .permissions(); - perms.set_mode(0o244); - fs::set_permissions(&permission_denied_directory_path, perms) - .await - .unwrap(); - let permission_denied_result = fs::try_exists(permission_denied_file_path).await; - assert_eq!( - permission_denied_result.err().unwrap().kind(), - std::io::ErrorKind::PermissionDenied - ); + perms.set_mode(0o244); + fs::set_permissions(&permission_denied_directory_path, perms) + .await + .unwrap(); + let permission_denied_result = fs::try_exists(permission_denied_file_path).await; + assert_eq!( + permission_denied_result.err().unwrap().kind(), + std::io::ErrorKind::PermissionDenied + ); + } } From f8bb8a9dc4eec9af4a6349bbebaf3587388665b2 Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 15:38:52 -0800 Subject: [PATCH 8/9] Skip perm test for freebsd too --- tokio/tests/fs_try_exists.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs index 1962c8d4114..d259b5caaf0 100644 --- a/tokio/tests/fs_try_exists.rs +++ b/tokio/tests/fs_try_exists.rs @@ -14,7 +14,8 @@ async fn try_exists() { assert!(fs::try_exists(existing_path).await.unwrap()); assert!(!fs::try_exists(nonexisting_path).await.unwrap()); - #[cfg(unix)] + // FreeBSD root user always has permission to stat. + #[cfg(all(unix, not(target_os = "freebsd")))] { use std::os::unix::prelude::PermissionsExt; let permission_denied_directory_path = dir.path().join("baz"); From 9b5b7d14c89772d91233b32a0ed64a0e841bcfd4 Mon Sep 17 00:00:00 2001 From: Kevin Qian Date: Mon, 20 Feb 2023 16:33:29 -0800 Subject: [PATCH 9/9] Add comment about why we use metadata based impl for now --- tokio/src/fs/try_exists.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tokio/src/fs/try_exists.rs b/tokio/src/fs/try_exists.rs index 3b488a2c2a2..069518bf946 100644 --- a/tokio/src/fs/try_exists.rs +++ b/tokio/src/fs/try_exists.rs @@ -24,6 +24,8 @@ use std::path::Path; /// ``` pub async fn try_exists(path: impl AsRef) -> io::Result { let path = path.as_ref().to_owned(); + // std's Path::try_exists is not available for current Rust min supported version. + // Current implementation is based on its internal implementation instead. match asyncify(move || std::fs::metadata(path)).await { Ok(_) => Ok(true), Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),