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
132#[cfg(unix)]
133impl OpenOptions {
134    /// Pass custom flags to the `flags` argument of `open`.
135    ///
136    /// The bits that define the access mode are masked out with `O_ACCMODE`, to
137    /// ensure they do not interfere with the access mode set by Rusts options.
138    ///
139    /// Custom flags can only set flags, not remove flags set by Rusts options.
140    /// This options overwrites any previously set custom flags.
141    pub fn custom_flags(&mut self, flags: i32) -> &mut Self {
142        self.0.custom_flags(flags);
143        self
144    }
145
146    /// Sets the mode bits that a new file will be created with.
147    ///
148    /// If a new file is created as part of an `OpenOptions::open` call then
149    /// this specified `mode` will be used as the permission bits for the
150    /// new file. If no `mode` is set, the default of `0o666` will be used.
151    /// The operating system masks out bits with the system's `umask`, to
152    /// produce the final permissions.
153    pub fn mode(&mut self, mode: u32) -> &mut Self {
154        self.0.mode(mode);
155        self
156    }
157}
158
159#[cfg(windows)]
160impl OpenOptions {
161    /// Combines it with
162    /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
163    /// for [`CreateFile`].
164    ///
165    /// Custom flags can only set flags, not remove flags set by Rust's options.
166    /// This option overwrites any previously set custom flags.
167    ///
168    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
169    pub fn custom_flags(&mut self, flags: u32) -> &mut Self {
170        self.0.custom_flags(flags);
171        self
172    }
173
174    /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
175    /// with the specified value.
176    ///
177    /// This will override the `read`, `write`, and `append` flags on the
178    /// `OpenOptions` structure. This method provides fine-grained control over
179    /// the permissions to read, write and append data, attributes (like hidden
180    /// and system), and extended attributes.
181    ///
182    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
183    pub fn access_mode(&mut self, access_mode: u32) -> &mut Self {
184        self.0.access_mode(access_mode);
185        self
186    }
187
188    /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
189    /// the specified value.
190    ///
191    /// By default `share_mode` is set to
192    /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
193    /// other processes to read, write, and delete/rename the same file
194    /// while it is open. Removing any of the flags will prevent other
195    /// processes from performing the corresponding operation until the file
196    /// handle is closed.
197    ///
198    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
199    pub fn share_mode(&mut self, share_mode: u32) -> &mut Self {
200        self.0.share_mode(share_mode);
201        self
202    }
203
204    /// Combines it with `custom_flags` and
205    /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
206    /// [`CreateFile`].
207    ///
208    /// If a _new_ file is created because it does not yet exist and
209    /// `.create(true)` or `.create_new(true)` are specified, the new file is
210    /// given the attributes declared with `.attributes()`.
211    ///
212    /// If an _existing_ file is opened with `.create(true).truncate(true)`, its
213    /// existing attributes are preserved and combined with the ones declared
214    /// with `.attributes()`.
215    ///
216    /// In all other cases the attributes get ignored.
217    ///
218    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
219    pub fn attributes(&mut self, attrs: u32) -> &mut Self {
220        self.0.attributes(attrs);
221        self
222    }
223
224    /// Combines it with `custom_flags` and `attributes`
225    /// to set the `dwFlagsAndAttributes` for [`CreateFile`].
226    ///
227    /// By default `security_qos_flags` is not set. It should be specified when
228    /// opening a named pipe, to control to which degree a server process can
229    /// act on behalf of a client process (security impersonation level).
230    ///
231    /// When `security_qos_flags` is not set, a malicious program can gain the
232    /// elevated privileges of a privileged Rust process when it allows opening
233    /// user-specified paths, by tricking it into opening a named pipe. So
234    /// arguably `security_qos_flags` should also be set when opening arbitrary
235    /// paths. However the bits can then conflict with other flags, specifically
236    /// `FILE_FLAG_OPEN_NO_RECALL`.
237    ///
238    /// For information about possible values, see [Impersonation Levels] on the
239    /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
240    /// automatically when using this method.
241    ///
242    /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
243    /// [Impersonation Levels]:
244    ///     https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
245    pub fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
246        self.0.security_qos_flags(flags);
247        self
248    }
249}