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}