Skip to content

Commit e34336b

Browse files
authored
Add features corresponding to Rust 1.58's new filesystem features. (#218)
* Add features corresponding to Rust 1.58's new filesystem features. Add `Metadata::is_symlink` and `File::options`, corresponding to the new std features in [Rust 1.58]. This also updates tests/fs.rs and tests/fs_utf8.rs to the latest changes from upstream Rust. [Rust 1.58]: https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html * Use `io::ErrorKind::Unsupported` on nightly. On Rust nightly, use `io::ErrorKind::Unsupported` for unsupported OS features.
1 parent 5e5f28b commit e34336b

File tree

16 files changed

+521
-44
lines changed

16 files changed

+521
-44
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ cap-tempfile = { path = "cap-tempfile", version = "^0.22.2-alpha.0"}
2424
rand = "0.8.1"
2525
tempfile = "3.1.0"
2626
camino = "1.0.5"
27+
libc = "0.2.100"
2728

2829
[target.'cfg(not(windows))'.dev-dependencies]
2930
rustix = "0.31.0"

build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ fn main() {
66
use_feature_or_nothing("clamp"); // https://github.com/rust-lang/rust/issues/44095
77
use_feature_or_nothing("extend_one"); // https://github.com/rust-lang/rust/issues/72631
88
use_feature_or_nothing("io_error_more"); // https://github.com/rust-lang/rust/issues/86442
9+
use_feature_or_nothing("io_error_uncategorized");
910
use_feature_or_nothing("pattern"); // https://github.com/rust-lang/rust/issues/27721
1011
use_feature_or_nothing("seek_convenience"); // https://github.com/rust-lang/rust/issues/59359
1112
use_feature_or_nothing("shrink_to"); // https://github.com/rust-lang/rust/issues/56431

cap-async-std/src/fs/file.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ impl File {
149149
.await
150150
.map(|f| Self::from_std(f.into()))
151151
}
152+
153+
/// Returns a new `OpenOptions` object.
154+
///
155+
/// This corresponds to [`async_std::fs::File::options`].
156+
#[must_use]
157+
#[inline]
158+
pub fn options() -> OpenOptions {
159+
OpenOptions::new()
160+
}
152161
}
153162

154163
#[cfg(not(target_os = "wasi"))]

cap-async-std/src/fs_utf8/file.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@ impl File {
142142
.await
143143
.map(Self::from_cap_std)
144144
}
145+
146+
/// Returns a new `OpenOptions` object.
147+
///
148+
/// This corresponds to [`async_std::fs::File::options`].
149+
#[must_use]
150+
#[inline]
151+
pub fn options() -> OpenOptions {
152+
OpenOptions::new()
153+
}
145154
}
146155

147156
#[cfg(not(windows))]

cap-primitives/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ fn main() {
66
// https://doc.rust-lang.org/unstable-book/library-features/windows-file-type-ext.html
77
use_feature_or_nothing("windows_file_type_ext");
88
use_feature_or_nothing("io_error_more"); // https://github.com/rust-lang/rust/issues/86442
9+
use_feature_or_nothing("io_error_uncategorized");
910

1011
// Don't rerun this on changes other than build.rs, as we only depend on
1112
// the rustc version.

cap-primitives/src/fs/metadata.rs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ impl Metadata {
105105
self.file_type.is_file()
106106
}
107107

108+
/// Returns `true` if this metadata is for a symbolic link.
109+
///
110+
/// This corresponds to [`std::fs::Metadata::is_symlink`].
111+
#[inline]
112+
pub fn is_symlink(&self) -> bool {
113+
self.file_type.is_symlink()
114+
}
115+
108116
/// Returns the size of the file, in bytes, this metadata is for.
109117
///
110118
/// This corresponds to [`std::fs::Metadata::len`].
@@ -126,38 +134,74 @@ impl Metadata {
126134
/// This corresponds to [`std::fs::Metadata::modified`].
127135
#[inline]
128136
pub fn modified(&self) -> io::Result<SystemTime> {
129-
self.modified.ok_or_else(|| {
130-
io::Error::new(
131-
io::ErrorKind::Other,
132-
"modified time metadata not available on this platform",
133-
)
134-
})
137+
#[cfg(io_error_uncategorized)]
138+
{
139+
self.modified.ok_or_else(|| {
140+
io::Error::new(
141+
io::ErrorKind::Unsupported,
142+
"modified time metadata not available on this platform",
143+
)
144+
})
145+
}
146+
#[cfg(not(io_error_uncategorized))]
147+
{
148+
self.modified.ok_or_else(|| {
149+
io::Error::new(
150+
io::ErrorKind::Other,
151+
"modified time metadata not available on this platform",
152+
)
153+
})
154+
}
135155
}
136156

137157
/// Returns the last access time of this metadata.
138158
///
139159
/// This corresponds to [`std::fs::Metadata::accessed`].
140160
#[inline]
141161
pub fn accessed(&self) -> io::Result<SystemTime> {
142-
self.accessed.ok_or_else(|| {
143-
io::Error::new(
144-
io::ErrorKind::Other,
145-
"accessed time metadata not available on this platform",
146-
)
147-
})
162+
#[cfg(io_error_uncategorized)]
163+
{
164+
self.accessed.ok_or_else(|| {
165+
io::Error::new(
166+
io::ErrorKind::Unsupported,
167+
"accessed time metadata not available on this platform",
168+
)
169+
})
170+
}
171+
#[cfg(not(io_error_uncategorized))]
172+
{
173+
self.accessed.ok_or_else(|| {
174+
io::Error::new(
175+
io::ErrorKind::Other,
176+
"accessed time metadata not available on this platform",
177+
)
178+
})
179+
}
148180
}
149181

150182
/// Returns the creation time listed in this metadata.
151183
///
152184
/// This corresponds to [`std::fs::Metadata::created`].
153185
#[inline]
154186
pub fn created(&self) -> io::Result<SystemTime> {
155-
self.created.ok_or_else(|| {
156-
io::Error::new(
157-
io::ErrorKind::Other,
158-
"created time metadata not available on this platform",
159-
)
160-
})
187+
#[cfg(io_error_uncategorized)]
188+
{
189+
self.created.ok_or_else(|| {
190+
io::Error::new(
191+
io::ErrorKind::Unsupported,
192+
"created time metadata not available on this platform",
193+
)
194+
})
195+
}
196+
#[cfg(not(io_error_uncategorized))]
197+
{
198+
self.created.ok_or_else(|| {
199+
io::Error::new(
200+
io::ErrorKind::Other,
201+
"created time metadata not available on this platform",
202+
)
203+
})
204+
}
161205
}
162206

163207
/// Determine if `self` and `other` refer to the same inode on the same

cap-primitives/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
)]
1414
#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]
1515
#![cfg_attr(io_error_more, feature(io_error_more))]
16+
#![cfg_attr(io_error_uncategorized, feature(io_error_uncategorized))]
1617

1718
#[cfg(not(windows))]
1819
mod rustix;

cap-std/src/fs/file.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ impl File {
146146
let std = open_ambient(path.as_ref(), options, ambient_authority)?;
147147
Ok(Self::from_std(std))
148148
}
149+
150+
/// Returns a new `OpenOptions` object.
151+
///
152+
/// This corresponds to [`std::fs::File::options`].
153+
#[must_use]
154+
#[inline]
155+
pub fn options() -> OpenOptions {
156+
OpenOptions::new()
157+
}
149158
}
150159

151160
#[inline]

cap-std/src/fs_utf8/file.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ impl File {
155155
ambient_authority,
156156
)?))
157157
}
158+
159+
/// Returns a new `OpenOptions` object.
160+
///
161+
/// This corresponds to [`std::fs::File::options`].
162+
#[must_use]
163+
#[inline]
164+
pub fn options() -> OpenOptions {
165+
OpenOptions::new()
166+
}
158167
}
159168

160169
#[cfg(not(windows))]

tests/fs.rs

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
// This file is derived from Rust's library/std/src/fs.rs at revision
2-
// 108e90ca78f052c0c1c49c42a22c85620be19712.
3-
//
4-
// This is the contents of the `tests` module, ported to use `cap_std`.
1+
// This file is derived from Rust's library/std/src/fs/tests.rs at revision
2+
// e4b1d5841494d6eb7f4944c91a057e16b0f0a9ea.
3+
4+
#![cfg_attr(io_error_uncategorized, feature(io_error_uncategorized))]
55

66
#[macro_use]
77
mod sys_common;
8+
#[macro_use]
9+
mod sys;
810

911
use std::io::prelude::*;
1012

13+
#[cfg(target_os = "macos")]
14+
use crate::sys::weak::weak;
1115
use cap_std::ambient_authority;
1216
use cap_std::fs::{self, Dir, OpenOptions};
17+
#[cfg(target_os = "macos")]
18+
use libc::{c_char, c_int};
1319
use std::io::{self, ErrorKind, SeekFrom};
1420
use std::path::{Path, PathBuf};
1521
use std::str;
@@ -59,13 +65,23 @@ pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
5965
let link = "some_hopefully_unique_link_name";
6066

6167
match symlink_file(r"nonexisting_target", tmpdir, link) {
62-
Ok(_) => true,
6368
// ERROR_PRIVILEGE_NOT_HELD = 1314
6469
Err(ref err) if err.raw_os_error() == Some(1314) => false,
65-
Err(_) => true,
70+
Ok(_) | Err(_) => true,
6671
}
6772
}
6873

74+
#[cfg(target_os = "macos")]
75+
fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
76+
weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
77+
linkat.get().is_some()
78+
}
79+
80+
#[cfg(not(target_os = "macos"))]
81+
fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
82+
return true;
83+
}
84+
6985
#[test]
7086
fn file_test_io_smoke_test() {
7187
let message = "it's alright. have a good time";
@@ -838,7 +854,7 @@ fn symlink_noexist() {
838854
};
839855

840856
// Use a relative path for testing. Symlinks get normalized by Windows,
841-
// so we may not get the same path back for absolute paths
857+
// so we might not get the same path back for absolute paths
842858
check!(symlink_file(&"foo", &tmpdir, "bar"));
843859
assert_eq!(check!(tmpdir.read_link("bar")).to_str().unwrap(), "foo");
844860
}
@@ -1386,8 +1402,15 @@ fn metadata_access_times() {
13861402
// Not always available
13871403
match (a.created(), b.created()) {
13881404
(Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1405+
#[cfg(not(io_error_uncategorized))]
13891406
(Err(e1), Err(e2))
13901407
if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {}
1408+
#[cfg(io_error_uncategorized)]
1409+
(Err(e1), Err(e2))
1410+
if e1.kind() == ErrorKind::Uncategorized
1411+
&& e2.kind() == ErrorKind::Uncategorized
1412+
|| e1.kind() == ErrorKind::Unsupported
1413+
&& e2.kind() == ErrorKind::Unsupported => {}
13911414
(a, b) => panic!(
13921415
"creation time must be always supported or not supported: {:?} {:?}",
13931416
a, b,
@@ -1403,6 +1426,9 @@ fn symlink_hard_link() {
14031426
if !got_symlink_permission(&tmpdir) {
14041427
return;
14051428
}
1429+
if !able_to_not_follow_symlinks_while_hard_linking() {
1430+
return;
1431+
}
14061432

14071433
// Create "file", a file.
14081434
check!(tmpdir.create("file"));
@@ -1453,3 +1479,41 @@ fn symlink_hard_link() {
14531479
.file_type()
14541480
.is_symlink());
14551481
}
1482+
1483+
/// Ensure `fs::create_dir` works on Windows with longer paths.
1484+
#[test]
1485+
#[cfg(windows)]
1486+
fn create_dir_long_paths() {
1487+
use std::ffi::OsStr;
1488+
use std::iter;
1489+
use std::os::windows::ffi::OsStrExt;
1490+
const PATH_LEN: usize = 247;
1491+
1492+
let tmpdir = tmpdir();
1493+
let mut path = PathBuf::new();
1494+
path.push("a");
1495+
let mut path = path.into_os_string();
1496+
1497+
let utf16_len = path.encode_wide().count();
1498+
if utf16_len >= PATH_LEN {
1499+
// Skip the test in the unlikely event the local user has a long temp directory
1500+
// path. This should not affect CI.
1501+
return;
1502+
}
1503+
// Increase the length of the path.
1504+
path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1505+
1506+
// This should succeed.
1507+
tmpdir.create_dir(&path).unwrap();
1508+
1509+
// This will fail if the path isn't converted to verbatim.
1510+
path.push("a");
1511+
tmpdir.create_dir(&path).unwrap();
1512+
1513+
// #90940: Ensure an empty path returns the "Not Found" error.
1514+
let path = Path::new("");
1515+
assert_eq!(
1516+
tmpdir.canonicalize(path).unwrap_err().kind(),
1517+
std::io::ErrorKind::NotFound
1518+
);
1519+
}

0 commit comments

Comments
 (0)