cap_primitives/fs/open_options.rs
1use crate::fs::{FollowSymlinks, ImplOpenOptionsExt};
2
3/// Options and flags which can be used to configure how a file is opened.
4///
5/// This corresponds to [`std::fs::OpenOptions`].
6///
7/// This `OpenOptions` has no `open` method. To open a file with an
8/// `OptionOptions`, first obtain a [`Dir`] containing the path, and then call
9/// [`Dir::open_with`].
10///
11/// [`Dir`]: https://docs.rs/cap-std/latest/cap_std/fs/struct.Dir.html
12/// [`Dir::open_with`]: https://docs.rs/cap-std/latest/cap_std/fs/struct.Dir.html#method.open_with
13///
14/// <details>
15/// We need to define our own version because the libstd `OpenOptions` doesn't
16/// have public accessors that we can use.
17/// </details>
18#[derive(Debug, Clone)]
19pub struct OpenOptions {
20 pub(crate) read: bool,
21 pub(crate) write: bool,
22 pub(crate) append: bool,
23 pub(crate) truncate: bool,
24 pub(crate) create: bool,
25 pub(crate) create_new: bool,
26 pub(crate) dir_required: bool,
27 pub(crate) maybe_dir: bool,
28 pub(crate) sync: bool,
29 pub(crate) dsync: bool,
30 pub(crate) rsync: bool,
31 pub(crate) nonblock: bool,
32 pub(crate) readdir_required: bool,
33 pub(crate) follow: FollowSymlinks,
34
35 #[cfg(any(unix, windows, target_os = "vxworks"))]
36 pub(crate) ext: ImplOpenOptionsExt,
37}
38
39impl OpenOptions {
40 /// Creates a blank new set of options ready for configuration.
41 ///
42 /// This corresponds to [`std::fs::OpenOptions::new`].
43 #[allow(clippy::new_without_default)]
44 #[inline]
45 pub const fn new() -> Self {
46 Self {
47 read: false,
48 write: false,
49 append: false,
50 truncate: false,
51 create: false,
52 create_new: false,
53 dir_required: false,
54 maybe_dir: false,
55 sync: false,
56 dsync: false,
57 rsync: false,
58 nonblock: false,
59 readdir_required: false,
60 follow: FollowSymlinks::Yes,
61
62 #[cfg(any(unix, windows, target_os = "vxworks"))]
63 ext: ImplOpenOptionsExt::new(),
64 }
65 }
66
67 /// Sets the option for read access.
68 ///
69 /// This corresponds to [`std::fs::OpenOptions::read`].
70 #[inline]
71 pub fn read(&mut self, read: bool) -> &mut Self {
72 self.read = read;
73 self
74 }
75
76 /// Sets the option for write access.
77 ///
78 /// This corresponds to [`std::fs::OpenOptions::write`].
79 #[inline]
80 pub fn write(&mut self, write: bool) -> &mut Self {
81 self.write = write;
82 self
83 }
84
85 /// Sets the option for the append mode.
86 ///
87 /// This corresponds to [`std::fs::OpenOptions::append`].
88 #[inline]
89 pub fn append(&mut self, append: bool) -> &mut Self {
90 self.append = append;
91 self
92 }
93
94 /// Sets the option for truncating a previous file.
95 ///
96 /// This corresponds to [`std::fs::OpenOptions::truncate`].
97 #[inline]
98 pub fn truncate(&mut self, truncate: bool) -> &mut Self {
99 self.truncate = truncate;
100 self
101 }
102
103 /// Sets the option to create a new file.
104 ///
105 /// This corresponds to [`std::fs::OpenOptions::create`].
106 #[inline]
107 pub fn create(&mut self, create: bool) -> &mut Self {
108 self.create = create;
109 self
110 }
111
112 /// Sets the option to always create a new file.
113 ///
114 /// This corresponds to [`std::fs::OpenOptions::create_new`].
115 #[inline]
116 pub fn create_new(&mut self, create_new: bool) -> &mut Self {
117 self.create_new = create_new;
118 self
119 }
120
121 /// Sets the option to enable or suppress following of symlinks.
122 #[inline]
123 pub(crate) fn follow(&mut self, follow: FollowSymlinks) -> &mut Self {
124 self.follow = follow;
125 self
126 }
127
128 /// Sets the option to enable an error if the opened object is not a
129 /// directory.
130 #[inline]
131 pub(crate) fn dir_required(&mut self, dir_required: bool) -> &mut Self {
132 self.dir_required = dir_required;
133 self
134 }
135
136 /// Sets the option to disable an error if the opened object is a
137 /// directory.
138 #[inline]
139 pub(crate) fn maybe_dir(&mut self, maybe_dir: bool) -> &mut Self {
140 self.maybe_dir = maybe_dir;
141 self
142 }
143
144 /// Requests write operations complete as defined by synchronized I/O file
145 /// integrity completion.
146 #[inline]
147 pub(crate) fn sync(&mut self, enable: bool) -> &mut Self {
148 self.sync = enable;
149 self
150 }
151
152 /// Requests write operations complete as defined by synchronized I/O data
153 /// integrity completion.
154 #[inline]
155 pub(crate) fn dsync(&mut self, enable: bool) -> &mut Self {
156 self.dsync = enable;
157 self
158 }
159
160 /// Requests read operations complete as defined by the level of integrity
161 /// specified by `sync` and `dsync`.
162 #[inline]
163 pub(crate) fn rsync(&mut self, enable: bool) -> &mut Self {
164 self.rsync = enable;
165 self
166 }
167
168 /// Requests that I/O operations fail with `std::io::ErrorKind::WouldBlock`
169 /// if they would otherwise block.
170 ///
171 /// This option is commonly not implemented for regular files, so blocking
172 /// may still occur.
173 #[inline]
174 pub(crate) fn nonblock(&mut self, enable: bool) -> &mut Self {
175 self.nonblock = enable;
176 self
177 }
178
179 /// Sets the option to request the ability to read directory entries.
180 #[inline]
181 pub(crate) fn readdir_required(&mut self, readdir_required: bool) -> &mut Self {
182 self.readdir_required = readdir_required;
183 self
184 }
185
186 /// Wrapper to allow `follow` to be exposed by the `cap-fs-ext` crate.
187 ///
188 /// This is hidden from the main API since this functionality isn't present
189 /// in `std`. Use `cap_fs_ext::OpenOptionsFollowExt` instead of calling
190 /// this directly.
191 #[doc(hidden)]
192 #[inline]
193 pub fn _cap_fs_ext_follow(&mut self, follow: FollowSymlinks) -> &mut Self {
194 self.follow(follow)
195 }
196
197 /// Wrapper to allow `maybe_dir` to be exposed by the `cap-fs-ext` crate.
198 ///
199 /// This is hidden from the main API since this functionality isn't present
200 /// in `std`. Use `cap_fs_ext::OpenOptionsMaybeDirExt` instead of
201 /// calling this directly.
202 #[doc(hidden)]
203 #[inline]
204 pub fn _cap_fs_ext_maybe_dir(&mut self, maybe_dir: bool) -> &mut Self {
205 self.maybe_dir(maybe_dir)
206 }
207
208 /// Wrapper to allow `sync` to be exposed by the `cap-fs-ext` crate.
209 ///
210 /// This is hidden from the main API since this functionality isn't present
211 /// in `std`. Use `cap_fs_ext::OpenOptionsSyncExt` instead of
212 /// calling this directly.
213 #[doc(hidden)]
214 #[inline]
215 pub fn _cap_fs_ext_sync(&mut self, enable: bool) -> &mut Self {
216 self.sync(enable)
217 }
218
219 /// Wrapper to allow `dsync` to be exposed by the `cap-fs-ext` crate.
220 ///
221 /// This is hidden from the main API since this functionality isn't present
222 /// in `std`. Use `cap_fs_ext::OpenOptionsSyncExt` instead of
223 /// calling this directly.
224 #[doc(hidden)]
225 #[inline]
226 pub fn _cap_fs_ext_dsync(&mut self, enable: bool) -> &mut Self {
227 self.dsync(enable)
228 }
229
230 /// Wrapper to allow `rsync` to be exposed by the `cap-fs-ext` crate.
231 ///
232 /// This is hidden from the main API since this functionality isn't present
233 /// in `std`. Use `cap_fs_ext::OpenOptionsSyncExt` instead of
234 /// calling this directly.
235 #[doc(hidden)]
236 #[inline]
237 pub fn _cap_fs_ext_rsync(&mut self, enable: bool) -> &mut Self {
238 self.rsync(enable)
239 }
240
241 /// Wrapper to allow `nonblock` to be exposed by the `cap-fs-ext` crate.
242 ///
243 /// This is hidden from the main API since this functionality isn't present
244 /// in `std`. Use `cap_fs_ext::OpenOptionsSyncExt` instead of
245 /// calling this directly.
246 #[doc(hidden)]
247 #[inline]
248 pub fn _cap_fs_ext_nonblock(&mut self, enable: bool) -> &mut Self {
249 self.nonblock(enable)
250 }
251}
252
253/// Unix-specific extensions to [`fs::OpenOptions`].
254#[cfg(unix)]
255pub trait OpenOptionsExt {
256 /// Sets the mode bits that a new file will be created with.
257 fn mode(&mut self, mode: u32) -> &mut Self;
258
259 /// Pass custom flags to the `flags` argument of `open`.
260 fn custom_flags(&mut self, flags: i32) -> &mut Self;
261}
262
263/// WASI-specific extensions to [`fs::OpenOptions`].
264#[cfg(target_os = "wasi")]
265pub trait OpenOptionsExt {
266 /// Pass custom `dirflags` argument to `path_open`.
267 fn lookup_flags(&mut self, flags: u32) -> &mut Self;
268
269 /// Indicates whether `OpenOptions` must open a directory or not.
270 fn directory(&mut self, dir: bool) -> &mut Self;
271
272 /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags`
273 /// field of `path_open`.
274 fn dsync(&mut self, dsync: bool) -> &mut Self;
275
276 /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags`
277 /// field of `path_open`.
278 fn nonblock(&mut self, nonblock: bool) -> &mut Self;
279
280 /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags`
281 /// field of `path_open`.
282 fn rsync(&mut self, rsync: bool) -> &mut Self;
283
284 /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags`
285 /// field of `path_open`.
286 fn sync(&mut self, sync: bool) -> &mut Self;
287
288 /// Indicates the value that should be passed in for the `fs_rights_base`
289 /// parameter of `path_open`.
290 fn fs_rights_base(&mut self, rights: u64) -> &mut Self;
291
292 /// Indicates the value that should be passed in for the
293 /// `fs_rights_inheriting` parameter of `path_open`.
294 fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self;
295
296 /// Open a file or directory.
297 fn open_at<P: AsRef<std::path::Path>>(
298 &self,
299 file: &std::fs::File,
300 path: P,
301 ) -> std::io::Result<std::fs::File>;
302}
303
304/// Windows-specific extensions to [`fs::OpenOptions`].
305#[cfg(windows)]
306pub trait OpenOptionsExt {
307 /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
308 /// with the specified value.
309 fn access_mode(&mut self, access: u32) -> &mut Self;
310
311 /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
312 /// the specified value.
313 fn share_mode(&mut self, val: u32) -> &mut Self;
314
315 /// Sets extra flags for the `dwFileFlags` argument to the call to
316 /// [`CreateFile2`] to the specified value (or combines it with
317 /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
318 /// for [`CreateFile`]).
319 ///
320 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
321 /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
322 fn custom_flags(&mut self, flags: u32) -> &mut Self;
323
324 /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
325 /// the specified value (or combines it with `custom_flags` and
326 /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
327 /// [`CreateFile`]).
328 ///
329 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
330 /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
331 fn attributes(&mut self, val: u32) -> &mut Self;
332
333 /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
334 /// the specified value (or combines it with `custom_flags` and `attributes`
335 /// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
336 ///
337 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
338 /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
339 /// [Impersonation Levels]:
340 /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
341 fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
342}
343
344#[cfg(unix)]
345impl OpenOptionsExt for OpenOptions {
346 #[inline]
347 fn mode(&mut self, mode: u32) -> &mut Self {
348 self.ext.mode(mode);
349 self
350 }
351
352 #[inline]
353 fn custom_flags(&mut self, flags: i32) -> &mut Self {
354 self.ext.custom_flags(flags);
355 self
356 }
357}
358
359#[cfg(target_os = "wasi")]
360impl OpenOptionsExt for OpenOptions {
361 fn lookup_flags(&mut self, _: u32) -> &mut Self {
362 todo!()
363 }
364
365 fn directory(&mut self, dir_required: bool) -> &mut Self {
366 self.dir_required = dir_required;
367 self
368 }
369
370 fn dsync(&mut self, _: bool) -> &mut Self {
371 todo!()
372 }
373
374 fn nonblock(&mut self, _: bool) -> &mut Self {
375 todo!()
376 }
377
378 fn rsync(&mut self, _: bool) -> &mut Self {
379 todo!()
380 }
381
382 fn sync(&mut self, _: bool) -> &mut Self {
383 todo!()
384 }
385
386 fn fs_rights_base(&mut self, _: u64) -> &mut Self {
387 todo!()
388 }
389
390 fn fs_rights_inheriting(&mut self, _: u64) -> &mut Self {
391 todo!()
392 }
393
394 fn open_at<P>(&self, dirfd: &std::fs::File, path: P) -> Result<std::fs::File, std::io::Error>
395 where
396 P: AsRef<std::path::Path>,
397 {
398 crate::fs::open(dirfd, path.as_ref(), self)
399 }
400}
401
402#[cfg(target_os = "vxworks")]
403impl OpenOptionsExt for OpenOptions {
404 #[inline]
405 fn mode(&mut self, mode: u32) -> &mut Self {
406 self.ext.mode(mode);
407 self
408 }
409
410 #[inline]
411 fn custom_flags(&mut self, flags: i32) -> &mut Self {
412 self.ext.custom_flags(flags);
413 self
414 }
415}
416
417#[cfg(windows)]
418impl OpenOptionsExt for OpenOptions {
419 #[inline]
420 fn access_mode(&mut self, access: u32) -> &mut Self {
421 self.ext.access_mode(access);
422 self
423 }
424
425 /// To prevent race conditions on Windows, handles for directories must be
426 /// opened without `FILE_SHARE_DELETE`.
427 #[inline]
428 fn share_mode(&mut self, val: u32) -> &mut Self {
429 self.ext.share_mode(val);
430 self
431 }
432
433 #[inline]
434 fn custom_flags(&mut self, flags: u32) -> &mut Self {
435 self.ext.custom_flags(flags);
436 self
437 }
438
439 #[inline]
440 fn attributes(&mut self, val: u32) -> &mut Self {
441 self.ext.attributes(val);
442 self
443 }
444
445 #[inline]
446 fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
447 self.ext.security_qos_flags(flags);
448 self
449 }
450}
451
452#[cfg(feature = "arbitrary")]
453impl arbitrary::Arbitrary<'_> for OpenOptions {
454 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
455 use arbitrary::Arbitrary;
456 let (read, write) = match u.int_in_range(0..=2)? {
457 0 => (true, false),
458 1 => (false, true),
459 2 => (true, true),
460 _ => panic!(),
461 };
462 // TODO: `OpenOptionsExt` options.
463 Ok(Self::new()
464 .read(read)
465 .write(write)
466 .create(<bool as Arbitrary>::arbitrary(u)?)
467 .append(<bool as Arbitrary>::arbitrary(u)?)
468 .truncate(<bool as Arbitrary>::arbitrary(u)?)
469 .create(<bool as Arbitrary>::arbitrary(u)?)
470 .create_new(<bool as Arbitrary>::arbitrary(u)?)
471 .dir_required(<bool as Arbitrary>::arbitrary(u)?)
472 .maybe_dir(<bool as Arbitrary>::arbitrary(u)?)
473 .sync(<bool as Arbitrary>::arbitrary(u)?)
474 .dsync(<bool as Arbitrary>::arbitrary(u)?)
475 .rsync(<bool as Arbitrary>::arbitrary(u)?)
476 .nonblock(<bool as Arbitrary>::arbitrary(u)?)
477 .readdir_required(<bool as Arbitrary>::arbitrary(u)?)
478 .follow(<FollowSymlinks as Arbitrary>::arbitrary(u)?)
479 .clone())
480 }
481}