Skip to content

Commit 0e44897

Browse files
committed
Add a maybe_dir extension trait for OpenOptions.
On Windows, a special flag is needed to open directories. This supports setting that flag, to allow opening directories on Windows.
1 parent 991dfaf commit 0e44897

File tree

6 files changed

+70
-4
lines changed

6 files changed

+70
-4
lines changed

cap-fs-ext/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod file_type_ext;
1414
mod is_file_read_write;
1515
mod metadata_ext;
1616
mod open_options_follow_ext;
17+
mod open_options_maybe_dir_ext;
1718
mod reopen;
1819

1920
#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
@@ -22,8 +23,9 @@ pub use dir_ext::{DirExt, SystemTimeSpec};
2223
pub use file_type_ext::FileTypeExt;
2324
pub use is_file_read_write::IsFileReadWrite;
2425
pub use metadata_ext::MetadataExt;
25-
pub use open_options_follow_ext::{FollowSymlinks, OpenOptionsFollowExt};
26+
pub use open_options_follow_ext::OpenOptionsFollowExt;
27+
pub use open_options_maybe_dir_ext::OpenOptionsMaybeDirExt;
2628
pub use reopen::Reopen;
2729

2830
/// Re-export this to allow it to be used with `Reuse`.
29-
pub use cap_primitives::fs::OpenOptions;
31+
pub use cap_primitives::fs::{FollowSymlinks, OpenOptions};

cap-fs-ext/src/open_options_follow_ext.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pub use cap_primitives::fs::FollowSymlinks;
1+
use crate::FollowSymlinks;
22

33
/// Extension trait for `cap_primitives::fs::OpenOptions` which adds
44
/// `follow`, a function for controlling whether a symlink in the last
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// Extension trait for `cap_primitives::fs::OpenOptions` which adds
2+
/// `maybe_dir`, a function for controlling whether an open should
3+
/// attempt to succeed on a directory. On Posix-ish platforms, opening
4+
/// a directory always succeeds, but on Windows, opening a directory
5+
/// needs this option.
6+
pub trait OpenOptionsMaybeDirExt {
7+
/// Sets the option for disabling an error that might be generated
8+
/// by the opened object being a directory.
9+
fn maybe_dir(&mut self, maybe_dir: bool) -> &mut Self;
10+
}
11+
12+
impl OpenOptionsMaybeDirExt for cap_primitives::fs::OpenOptions {
13+
#[inline]
14+
fn maybe_dir(&mut self, maybe_dir: bool) -> &mut Self {
15+
// `maybe_dir` functionality is implemented within `cap_primitives`; we're
16+
// just exposing it here since `OpenOptions` is re-exported by `cap_std`
17+
// etc. and `maybe_dir` isn't in `std`.
18+
unsafe { self._cap_fs_ext_maybe_dir(maybe_dir) }
19+
}
20+
}

cap-primitives/src/fs/open_options.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct OpenOptions {
2525
pub(crate) create: bool,
2626
pub(crate) create_new: bool,
2727
pub(crate) dir_required: bool,
28+
pub(crate) maybe_dir: bool,
2829
pub(crate) readdir_required: bool,
2930
pub(crate) follow: FollowSymlinks,
3031

@@ -49,6 +50,7 @@ impl OpenOptions {
4950
create: false,
5051
create_new: false,
5152
dir_required: false,
53+
maybe_dir: false,
5254
readdir_required: false,
5355
follow: FollowSymlinks::Yes,
5456

@@ -137,6 +139,13 @@ impl OpenOptions {
137139
self
138140
}
139141

142+
/// Sets the option to disable an error if the opened object is a directory.
143+
#[inline]
144+
pub(crate) fn maybe_dir(&mut self, maybe_dir: bool) -> &mut Self {
145+
self.maybe_dir = maybe_dir;
146+
self
147+
}
148+
140149
/// Sets the option to request the ability to read directory entries.
141150
#[inline]
142151
pub(crate) fn readdir_required(&mut self, readdir_required: bool) -> &mut Self {
@@ -155,6 +164,18 @@ impl OpenOptions {
155164
pub unsafe fn _cap_fs_ext_follow(&mut self, follow: FollowSymlinks) -> &mut Self {
156165
self.follow(follow)
157166
}
167+
168+
/// Wrapper to allow `maybe_dir` to be exposed by the `cap-fs-ext` crate.
169+
///
170+
/// # Safety
171+
///
172+
/// This is hidden from the main API since this functionality isn't present in `std`.
173+
/// Use `cap_fs_ext::OpenOptionsMaybeDirExt` instead of calling this directly.
174+
#[doc(hidden)]
175+
#[inline]
176+
pub unsafe fn _cap_fs_ext_maybe_dir(&mut self, maybe_dir: bool) -> &mut Self {
177+
self.maybe_dir(maybe_dir)
178+
}
158179
}
159180

160181
#[cfg(unix)]
@@ -249,6 +270,7 @@ impl arbitrary::Arbitrary for OpenOptions {
249270
.create(<bool as Arbitrary>::arbitrary(u)?)
250271
.create_new(<bool as Arbitrary>::arbitrary(u)?)
251272
.dir_required(<bool as Arbitrary>::arbitrary(u)?)
273+
.maybe_dir(<bool as Arbitrary>::arbitrary(u)?)
252274
.readdir_required(<bool as Arbitrary>::arbitrary(u)?)
253275
.follow(<FollowSymlinks as Arbitrary>::arbitrary(u)?)
254276
.clone())

cap-primitives/src/windows/fs/oflags.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub(in super::super) fn open_options_to_std(opts: &OpenOptions) -> (fs::OpenOpti
99
let mut trunc = opts.truncate;
1010
let mut manually_trunc = false;
1111

12-
let custom_flags = match opts.follow {
12+
let mut custom_flags = match opts.follow {
1313
FollowSymlinks::Yes => opts.ext.custom_flags,
1414
FollowSymlinks::No => {
1515
if trunc && !opts.create_new && !opts.append && opts.write {
@@ -21,6 +21,9 @@ pub(in super::super) fn open_options_to_std(opts: &OpenOptions) -> (fs::OpenOpti
2121
opts.ext.custom_flags | Flags::FILE_FLAG_OPEN_REPARSE_POINT.bits()
2222
}
2323
};
24+
if opts.maybe_dir {
25+
custom_flags |= Flags::FILE_FLAG_BACKUP_SEMANTICS.bits();
26+
}
2427
let mut std_opts = fs::OpenOptions::new();
2528
std_opts
2629
.read(opts.read)

tests/fs_additional.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,3 +752,22 @@ fn readdir_write() {
752752
.is_err());
753753
}
754754
}
755+
756+
#[test]
757+
fn maybe_dir() {
758+
use cap_fs_ext::OpenOptionsMaybeDirExt;
759+
760+
let tmpdir = tmpdir();
761+
check!(tmpdir.create_dir("dir"));
762+
763+
// Opening directories works on non-Windows platforms.
764+
#[cfg(not(windows))]
765+
check!(tmpdir.open("dir"));
766+
767+
// Opening directories fails on Windows.
768+
#[cfg(windows)]
769+
assert!(tmpdir.open("dir").is_err());
770+
771+
// Opening directories works on all platforms with `maybe_dir`.
772+
check!(tmpdir.open_with("dir", OpenOptions::new().read(true).maybe_dir(true)));
773+
}

0 commit comments

Comments
 (0)