monoio/fs/
open_options.rs

1#[cfg(unix)]
2use std::os::unix::prelude::OpenOptionsExt;
3use std::{io, path::Path};
4
5#[cfg(windows)]
6use windows_sys::Win32::{
7    Foundation::{ERROR_INVALID_PARAMETER, GENERIC_READ, GENERIC_WRITE},
8    Security::SECURITY_ATTRIBUTES,
9    Storage::FileSystem::{
10        CREATE_ALWAYS, CREATE_NEW, FILE_FLAG_OPEN_REPARSE_POINT, FILE_GENERIC_WRITE,
11        FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_WRITE_DATA, OPEN_ALWAYS,
12        OPEN_EXISTING, TRUNCATE_EXISTING,
13    },
14};
15
16use crate::{
17    driver::{op::Op, shared_fd::SharedFd},
18    fs::File,
19};
20
21/// Options and flags which can be used to configure how a file is opened.
22///
23/// This builder exposes the ability to configure how a [`File`] is opened and
24/// what operations are permitted on the open file. The [`File::open`] and
25/// [`File::create`] methods are aliases for commonly used options using this
26/// builder.
27///
28/// Generally speaking, when using `OpenOptions`, you'll first call
29/// [`OpenOptions::new`], then chain calls to methods to set each option, then
30/// call [`OpenOptions::open`], passing the path of the file you're trying to
31/// open. This will give you a [`io::Result`] with a [`File`] inside that you
32/// can further operate on.
33///
34/// # Examples
35///
36/// Opening a file to read:
37///
38/// ```no_run
39/// use monoio::fs::OpenOptions;
40///
41/// #[monoio::main]
42/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
43///     let file = OpenOptions::new().read(true).open("foo.txt").await?;
44///     Ok(())
45/// }
46/// ```
47///
48/// Opening a file for both reading and writing, as well as creating it if it
49/// doesn't exist:
50///
51/// ```no_run
52/// use monoio::fs::OpenOptions;
53///
54/// #[monoio::main]
55/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
56///     let file = OpenOptions::new()
57///         .read(true)
58///         .write(true)
59///         .create(true)
60///         .open("foo.txt")
61///         .await?;
62///     Ok(())
63/// }
64/// ```
65#[derive(Debug, Clone)]
66pub struct OpenOptions {
67    read: bool,
68    write: bool,
69    append: bool,
70    truncate: bool,
71    create: bool,
72    create_new: bool,
73    #[cfg(unix)]
74    pub(crate) mode: libc::mode_t,
75    #[cfg(unix)]
76    pub(crate) custom_flags: libc::c_int,
77    #[cfg(windows)]
78    pub(crate) custom_flags: u32,
79    #[cfg(windows)]
80    pub(crate) access_mode: Option<u32>,
81    #[cfg(windows)]
82    pub(crate) attributes: u32,
83    #[cfg(windows)]
84    pub(crate) share_mode: u32,
85    #[cfg(windows)]
86    pub(crate) security_qos_flags: u32,
87    #[cfg(windows)]
88    pub(crate) security_attributes: *mut SECURITY_ATTRIBUTES,
89}
90
91impl OpenOptions {
92    /// Creates a blank new set of options ready for configuration.
93    ///
94    /// All options are initially set to `false`.
95    ///
96    /// # Examples
97    ///
98    /// ```no_run
99    /// use monoio::fs::OpenOptions;
100    ///
101    /// #[monoio::main]
102    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
103    ///     let file = OpenOptions::new().read(true).open("foo.txt").await?;
104    ///     Ok(())
105    /// }
106    /// ```
107    #[allow(clippy::new_without_default)]
108    pub fn new() -> OpenOptions {
109        OpenOptions {
110            // generic
111            read: false,
112            write: false,
113            append: false,
114            truncate: false,
115            create: false,
116            create_new: false,
117            #[cfg(unix)]
118            mode: 0o666,
119            #[cfg(unix)]
120            custom_flags: 0,
121            #[cfg(windows)]
122            custom_flags: 0,
123            #[cfg(windows)]
124            access_mode: None,
125            #[cfg(windows)]
126            share_mode: FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
127            #[cfg(windows)]
128            attributes: 0,
129            #[cfg(windows)]
130            security_qos_flags: 0,
131            #[cfg(windows)]
132            security_attributes: std::ptr::null_mut(),
133        }
134    }
135
136    /// Sets the option for read access.
137    ///
138    /// This option, when true, will indicate that the file should be
139    /// `read`-able if opened.
140    ///
141    /// # Examples
142    ///
143    /// ```no_run
144    /// use monoio::fs::OpenOptions;
145    ///
146    /// #[monoio::main]
147    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
148    ///     let file = OpenOptions::new().read(true).open("foo.txt").await?;
149    ///     Ok(())
150    /// }
151    /// ```
152    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
153        self.read = read;
154        self
155    }
156
157    /// Sets the option for write access.
158    ///
159    /// This option, when true, will indicate that the file should be
160    /// `write`-able if opened.
161    ///
162    /// If the file already exists, any write calls on it will overwrite its
163    /// contents, without truncating it.
164    ///
165    /// # Examples
166    ///
167    /// ```no_run
168    /// use monoio::fs::OpenOptions;
169    ///
170    /// #[monoio::main]
171    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
172    ///     let file = OpenOptions::new().write(true).open("foo.txt").await?;
173    ///     Ok(())
174    /// }
175    /// ```
176    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
177        self.write = write;
178        self
179    }
180
181    /// Sets the option for the append mode.
182    ///
183    /// This option, when true, means that writes will append to a file instead
184    /// of overwriting previous contents. Note that setting
185    /// `.write(true).append(true)` has the same effect as setting only
186    /// `.append(true)`.
187    ///
188    /// For most filesystems, the operating system guarantees that all writes
189    /// are atomic: no writes get mangled because another process writes at the
190    /// same time.
191    ///
192    /// ## Note
193    ///
194    /// This function doesn't create the file if it doesn't exist. Use the
195    /// [`OpenOptions::create`] method to do so.
196    ///
197    /// # Examples
198    ///
199    /// ```no_run
200    /// use monoio::fs::OpenOptions;
201    ///
202    /// #[monoio::main]
203    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
204    ///     let file = OpenOptions::new().append(true).open("foo.txt").await?;
205    ///     Ok(())
206    /// }
207    /// ```
208    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
209        self.append = append;
210        self
211    }
212
213    /// Sets the option for truncating a previous file.
214    ///
215    /// If a file is successfully opened with this option set it will truncate
216    /// the file to 0 length if it already exists.
217    ///
218    /// The file must be opened with write access for truncate to work.
219    ///
220    /// # Examples
221    ///
222    /// ```no_run
223    /// use monoio::fs::OpenOptions;
224    ///
225    /// #[monoio::main]
226    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
227    ///     let file = OpenOptions::new()
228    ///         .write(true)
229    ///         .truncate(true)
230    ///         .open("foo.txt")
231    ///         .await?;
232    ///     Ok(())
233    /// }
234    /// ```
235    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
236        self.truncate = truncate;
237        self
238    }
239
240    /// Sets the option to create a new file, or open it if it already exists.
241    ///
242    /// In order for the file to be created, [`OpenOptions::write`] or
243    /// [`OpenOptions::append`] access must be used.
244    ///
245    /// # Examples
246    ///
247    /// ```no_run
248    /// use monoio::fs::OpenOptions;
249    ///
250    /// #[monoio::main]
251    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
252    ///     let file = OpenOptions::new()
253    ///         .write(true)
254    ///         .create(true)
255    ///         .open("foo.txt")
256    ///         .await?;
257    ///     Ok(())
258    /// }
259    /// ```
260    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
261        self.create = create;
262        self
263    }
264
265    /// Sets the option to create a new file, failing if it already exists.
266    ///
267    /// No file is allowed to exist at the target location, also no (dangling)
268    /// symlink. In this way, if the call succeeds, the file returned is
269    /// guaranteed to be new.
270    ///
271    /// This option is useful because it is atomic. Otherwise between checking
272    /// whether a file exists and creating a new one, the file may have been
273    /// created by another process (a TOCTOU race condition / attack).
274    ///
275    /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
276    /// ignored.
277    ///
278    /// The file must be opened with write or append access in order to create
279    /// a new file.
280    ///
281    /// [`.create()`]: OpenOptions::create
282    /// [`.truncate()`]: OpenOptions::truncate
283    ///
284    /// # Examples
285    ///
286    /// ```no_run
287    /// use monoio::fs::OpenOptions;
288    ///
289    /// #[monoio::main]
290    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
291    ///     let file = OpenOptions::new()
292    ///         .write(true)
293    ///         .create_new(true)
294    ///         .open("foo.txt")
295    ///         .await?;
296    ///     Ok(())
297    /// }
298    /// ```
299    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
300        self.create_new = create_new;
301        self
302    }
303
304    /// Opens a file at `path` with the options specified by `self`.
305    ///
306    /// # Errors
307    ///
308    /// This function will return an error under a number of different
309    /// circumstances. Some of these error conditions are listed here, together
310    /// with their [`io::ErrorKind`]. The mapping to [`io::ErrorKind`]s is not
311    /// part of the compatibility contract of the function, especially the
312    /// [`Other`] kind might change to more specific kinds in the future.
313    ///
314    /// * [`NotFound`]: The specified file does not exist and neither `create` or `create_new` is
315    ///   set.
316    /// * [`NotFound`]: One of the directory components of the file path does not exist.
317    /// * [`PermissionDenied`]: The user lacks permission to get the specified access rights for the
318    ///   file.
319    /// * [`PermissionDenied`]: The user lacks permission to open one of the directory components of
320    ///   the specified path.
321    /// * [`AlreadyExists`]: `create_new` was specified and the file already exists.
322    /// * [`InvalidInput`]: Invalid combinations of open options (truncate without write access, no
323    ///   access mode set, etc.).
324    /// * [`Other`]: One of the directory components of the specified file path was not, in fact, a
325    ///   directory.
326    /// * [`Other`]: Filesystem-level errors: full disk, write permission requested on a read-only
327    ///   file system, exceeded disk quota, too many open files, too long filename, too many
328    ///   symbolic links in the specified path (Unix-like systems only), etc.
329    ///
330    /// # Examples
331    ///
332    /// ```no_run
333    /// use monoio::fs::OpenOptions;
334    ///
335    /// #[monoio::main]
336    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
337    ///     let file = OpenOptions::new().read(true).open("foo.txt").await?;
338    ///     Ok(())
339    /// }
340    /// ```
341    ///
342    /// [`AlreadyExists`]: io::ErrorKind::AlreadyExists
343    /// [`InvalidInput`]: io::ErrorKind::InvalidInput
344    /// [`NotFound`]: io::ErrorKind::NotFound
345    /// [`Other`]: io::ErrorKind::Other
346    /// [`PermissionDenied`]: io::ErrorKind::PermissionDenied
347    pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
348        let op = Op::open(path.as_ref(), self)?;
349
350        // Await the completion of the event
351        let completion = op.await;
352
353        // The file is open
354        Ok(File::from_shared_fd(SharedFd::new_without_register(
355            completion.meta.result?.into_inner() as _,
356        )))
357    }
358
359    #[cfg(unix)]
360    pub(crate) fn access_mode(&self) -> io::Result<libc::c_int> {
361        match (self.read, self.write, self.append) {
362            (true, false, false) => Ok(libc::O_RDONLY),
363            (false, true, false) => Ok(libc::O_WRONLY),
364            (true, true, false) => Ok(libc::O_RDWR),
365            (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
366            (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
367            (false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
368        }
369    }
370
371    #[cfg(windows)]
372    pub(crate) fn access_mode(&self) -> io::Result<u32> {
373        match (self.read, self.write, self.append, self.access_mode) {
374            (.., Some(mode)) => Ok(mode),
375            (true, false, false, None) => Ok(GENERIC_READ),
376            (false, true, false, None) => Ok(GENERIC_WRITE),
377            (true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE),
378            (false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA),
379            (true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)),
380            (false, false, false, None) => {
381                Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER as _))
382            }
383        }
384    }
385
386    #[cfg(unix)]
387    pub(crate) fn creation_mode(&self) -> io::Result<libc::c_int> {
388        match (self.write, self.append) {
389            (true, false) => {}
390            (false, false) => {
391                if self.truncate || self.create || self.create_new {
392                    return Err(io::Error::from_raw_os_error(libc::EINVAL));
393                }
394            }
395            (_, true) => {
396                if self.truncate && !self.create_new {
397                    return Err(io::Error::from_raw_os_error(libc::EINVAL));
398                }
399            }
400        }
401
402        Ok(match (self.create, self.truncate, self.create_new) {
403            (false, false, false) => 0,
404            (true, false, false) => libc::O_CREAT,
405            (false, true, false) => libc::O_TRUNC,
406            (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
407            (_, _, true) => libc::O_CREAT | libc::O_EXCL,
408        })
409    }
410
411    #[cfg(windows)]
412    pub(crate) fn creation_mode(&self) -> io::Result<u32> {
413        match (self.write, self.append) {
414            (true, false) => {}
415            (false, false) => {
416                if self.truncate || self.create || self.create_new {
417                    return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER as _));
418                }
419            }
420            (_, true) => {
421                if self.truncate && !self.create_new {
422                    return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER as _));
423                }
424            }
425        }
426
427        Ok(match (self.create, self.truncate, self.create_new) {
428            (false, false, false) => OPEN_EXISTING,
429            (true, false, false) => OPEN_ALWAYS,
430            (false, true, false) => TRUNCATE_EXISTING,
431            (true, true, false) => CREATE_ALWAYS,
432            (_, _, true) => CREATE_NEW,
433        })
434    }
435
436    #[cfg(windows)]
437    pub(crate) fn get_flags_and_attributes(&self) -> u32 {
438        self.custom_flags
439            | self.attributes
440            | self.security_qos_flags
441            | if self.create_new {
442                FILE_FLAG_OPEN_REPARSE_POINT as _
443            } else {
444                0
445            }
446    }
447}
448
449#[cfg(unix)]
450impl OpenOptionsExt for OpenOptions {
451    fn mode(&mut self, mode: u32) -> &mut Self {
452        self.mode = mode as libc::mode_t;
453        self
454    }
455
456    fn custom_flags(&mut self, flags: i32) -> &mut Self {
457        self.custom_flags = flags as libc::c_int;
458        self
459    }
460}