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}