Skip to main content

compio_fs\open_options/
mod.rs

1#[cfg(unix)]
2#[path = "unix.rs"]
3mod sys;
4
5#[cfg(windows)]
6#[path = "windows.rs"]
7mod sys;
8
9use std::{io, path::Path};
10
11use crate::File;
12
13/// Options and flags which can be used to configure how a file is opened.
14///
15/// This builder exposes the ability to configure how a [`File`] is opened and
16/// what operations are permitted on the open file. The [`File::open`] and
17/// [`File::create`] methods are aliases for commonly used options using this
18/// builder.
19///
20/// Generally speaking, when using `OpenOptions`, you'll first call
21/// [`OpenOptions::new`], then chain calls to methods to set each option, then
22/// call [`OpenOptions::open`], passing the path of the file you're trying to
23/// open. This will give you a [`std::io::Result`] with a [`File`] inside that
24/// you can further operate on.
25///
26/// # Examples
27///
28/// Opening a file to read:
29///
30/// ```no_run
31/// use compio_fs::OpenOptions;
32///
33/// # compio_runtime::Runtime::new().unwrap().block_on(async {
34/// let file = OpenOptions::new().read(true).open("foo.txt").await.unwrap();
35/// # });
36/// ```
37///
38/// Opening a file for both reading and writing, as well as creating it if it
39/// doesn't exist:
40///
41/// ```no_run
42/// use compio_fs::OpenOptions;
43///
44/// # compio_runtime::Runtime::new().unwrap().block_on(async {
45/// let file = OpenOptions::new()
46///     .read(true)
47///     .write(true)
48///     .create(true)
49///     .open("foo.txt")
50///     .await
51///     .unwrap();
52/// # });
53/// ```
54#[derive(Debug, Clone)]
55pub struct OpenOptions(sys::OpenOptions);
56
57impl OpenOptions {
58    /// Creates a blank new set of options ready for configuration.
59    #[allow(clippy::new_without_default)]
60    #[must_use]
61    pub fn new() -> Self {
62        Self(sys::OpenOptions::new())
63    }
64
65    /// Sets the option for read access.
66    ///
67    /// This option, when true, will indicate that the file should be
68    /// `read`-able if opened.
69    pub fn read(&mut self, read: bool) -> &mut Self {
70        self.0.read(read);
71        self
72    }
73
74    /// Sets the option for write access.
75    ///
76    /// This option, when true, will indicate that the file should be
77    /// `write`-able if opened.
78    pub fn write(&mut self, write: bool) -> &mut Self {
79        self.0.write(write);
80        self
81    }
82
83    /// Sets the option for truncating a previous file.
84    ///
85    /// If a file is successfully opened with this option set it will truncate
86    /// the file to 0 length if it already exists.
87    ///
88    /// The file must be opened with write access for truncate to work.
89    pub fn truncate(&mut self, truncate: bool) -> &mut Self {
90        self.0.truncate(truncate);
91        self
92    }
93
94    /// Sets the option to create a new file, or open it if it already exists.
95    ///
96    /// In order for the file to be created, [`OpenOptions::write`] access must
97    /// be used.
98    pub fn create(&mut self, create: bool) -> &mut Self {
99        self.0.create(create);
100        self
101    }
102
103    /// Sets the option to create a new file, failing if it already exists.
104    ///
105    /// No file is allowed to exist at the target location, also no (dangling)
106    /// symlink. In this way, if the call succeeds, the file returned is
107    /// guaranteed to be new.
108    ///
109    /// This option is useful because it is atomic. Otherwise between checking
110    /// whether a file exists and creating a new one, the file may have been
111    /// created by another process (a TOCTOU race condition / attack).
112    ///
113    /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
114    /// ignored.
115    ///
116    /// The file must be opened with write or append access in order to create
117    /// a new file.
118    ///
119    /// [`.create()`]: OpenOptions::create
120    /// [`.truncate()`]: OpenOptions::truncate
121    pub fn create_new(&mut self, create_new: bool) -> &mut Self {
122        self.0.create_new(create_new);
123        self
124    }
125
126    /// Opens a file at `path` with the options specified by `self`.
127    pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
128        self.0.open(path).await
129    }
130
131    #[cfg(dirfd)]
132    pub(crate) async fn open_at(&self, dir: &File, path: impl AsRef<Path>) -> io::Result<File> {
133        self.0.open_at(dir, path).await
134    }
135}
136
137#[cfg(unix)]
138impl OpenOptions {
139    /// Pass custom flags to the `flags` argument of `open`.
140    ///
141    /// The bits that define the access mode are masked out with `O_ACCMODE`, to
142    /// ensure they do not interfere with the access mode set by Rusts options.
143    ///
144    /// Custom flags can only set flags, not remove flags set by Rusts options.
145    /// This options overwrites any previously set custom flags.
146    pub fn custom_flags(&mut self, flags: i32) -> &mut Self {
147        self.0.custom_flags(flags);
148        self
149    }
150
151    /// Sets the mode bits that a new file will be created with.
152    ///
153    /// If a new file is created as part of an `OpenOptions::open` call then
154    /// this specified `mode` will be used as the permission bits for the
155    /// new file. If no `mode` is set, the default of `0o666` will be used.
156    /// The operating system masks out bits with the system's `umask`, to
157    /// produce the final permissions.
158    pub fn mode(&mut self, mode: u32) -> &mut Self {
159        self.0.mode(mode);
160        self
161    }
162}
163
164#[cfg(windows)]
165impl OpenOptions {
166    /// Combines it with
167    /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
168    /// for [`CreateFile`].
169    ///
170    /// Custom flags can only set flags, not remove flags set by Rust's options.
171    /// This option overwrites any previously set custom flags.
172    ///
173    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
174    pub fn custom_flags(&mut self, flags: u32) -> &mut Self {
175        self.0.custom_flags(flags);
176        self
177    }
178
179    /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
180    /// with the specified value.
181    ///
182    /// This will override the `read`, `write`, and `append` flags on the
183    /// `OpenOptions` structure. This method provides fine-grained control over
184    /// the permissions to read, write and append data, attributes (like hidden
185    /// and system), and extended attributes.
186    ///
187    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
188    pub fn access_mode(&mut self, access_mode: u32) -> &mut Self {
189        self.0.access_mode(access_mode);
190        self
191    }
192
193    /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
194    /// the specified value.
195    ///
196    /// By default `share_mode` is set to
197    /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
198    /// other processes to read, write, and delete/rename the same file
199    /// while it is open. Removing any of the flags will prevent other
200    /// processes from performing the corresponding operation until the file
201    /// handle is closed.
202    ///
203    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
204    pub fn share_mode(&mut self, share_mode: u32) -> &mut Self {
205        self.0.share_mode(share_mode);
206        self
207    }
208
209    /// Combines it with `custom_flags` and
210    /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
211    /// [`CreateFile`].
212    ///
213    /// If a _new_ file is created because it does not yet exist and
214    /// `.create(true)` or `.create_new(true)` are specified, the new file is
215    /// given the attributes declared with `.attributes()`.
216    ///
217    /// If an _existing_ file is opened with `.create(true).truncate(true)`, its
218    /// existing attributes are preserved and combined with the ones declared
219    /// with `.attributes()`.
220    ///
221    /// In all other cases the attributes get ignored.
222    ///
223    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
224    pub fn attributes(&mut self, attrs: u32) -> &mut Self {
225        self.0.attributes(attrs);
226        self
227    }
228
229    /// Combines it with `custom_flags` and `attributes`
230    /// to set the `dwFlagsAndAttributes` for [`CreateFile`].
231    ///
232    /// By default `security_qos_flags` is not set. It should be specified when
233    /// opening a named pipe, to control to which degree a server process can
234    /// act on behalf of a client process (security impersonation level).
235    ///
236    /// When `security_qos_flags` is not set, a malicious program can gain the
237    /// elevated privileges of a privileged Rust process when it allows opening
238    /// user-specified paths, by tricking it into opening a named pipe. So
239    /// arguably `security_qos_flags` should also be set when opening arbitrary
240    /// paths. However the bits can then conflict with other flags, specifically
241    /// `FILE_FLAG_OPEN_NO_RECALL`.
242    ///
243    /// For information about possible values, see [Impersonation Levels] on the
244    /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
245    /// automatically when using this method.
246    ///
247    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
248    /// [Impersonation Levels]:
249    ///     https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
250    pub fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
251        self.0.security_qos_flags(flags);
252        self
253    }
254}