Skip to main content

zng_task/
fs.rs

1//! Async filesystem primitives.
2//!
3//! This module is the [async-fs](https://docs.rs/async-fs) crate re-exported for convenience.
4//!
5
6#[doc(inline)]
7pub use async_fs::*;
8
9//
10// Module contains patched version of File
11// TODO(breaking) replace with reexport again after  https://github.com/smol-rs/async-fs/pull/55 is released
12//
13
14use std::fmt;
15use std::future::Future;
16use std::io::{self, SeekFrom};
17use std::path::Path;
18use std::pin::Pin;
19use std::sync::Arc;
20use std::task::{Context, Poll};
21
22#[cfg(unix)]
23use std::os::unix::fs::OpenOptionsExt as _;
24
25#[cfg(windows)]
26use std::os::windows::fs::OpenOptionsExt as _;
27
28use async_lock::Mutex;
29use blocking::{Unblock, unblock};
30use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt};
31use futures_lite::ready;
32
33#[doc(no_inline)]
34pub use std::fs::{FileType, Metadata, Permissions};
35
36/// An open file on the filesystem.
37///
38/// Depending on what options the file was opened with, this type can be used for reading and/or
39/// writing.
40///
41/// Files are automatically closed when they get dropped and any errors detected on closing are
42/// ignored. Use the [`sync_all()`][`File::sync_all()`] method before dropping a file if such
43/// errors need to be handled.
44///
45/// **NOTE:** If writing to a file, make sure to call
46/// [`flush()`][`futures_lite::io::AsyncWriteExt::flush()`], [`sync_data()`][`File::sync_data()`],
47/// or [`sync_all()`][`File::sync_all()`] before dropping the file or else some written data
48/// might get lost!
49///
50/// # Examples
51///
52/// Create a new file and write some bytes to it:
53///
54/// ```no_run
55/// use futures_lite::io::AsyncWriteExt;
56/// use zng_task::fs::File;
57///
58/// # futures_lite::future::block_on(async {
59/// let mut file = File::create("a.txt").await?;
60///
61/// file.write_all(b"Hello, world!").await?;
62/// file.flush().await?;
63/// # std::io::Result::Ok(()) });
64/// ```
65///
66/// Read the contents of a file into a vector of bytes:
67///
68/// ```no_run
69/// use futures_lite::io::AsyncReadExt;
70/// use zng_task::fs::File;
71///
72/// # futures_lite::future::block_on(async {
73/// let mut file = File::open("a.txt").await?;
74///
75/// let mut contents = Vec::new();
76/// file.read_to_end(&mut contents).await?;
77/// # std::io::Result::Ok(()) });
78/// ```
79pub struct File {
80    /// Always accessible reference to the file.
81    file: Arc<std::fs::File>,
82
83    /// Performs blocking I/O operations on a thread pool.
84    unblock: Mutex<Unblock<ArcFile>>,
85
86    /// Logical file cursor, tracked when reading from the file.
87    ///
88    /// This will be set to an error if the file is not seekable.
89    read_pos: Option<io::Result<u64>>,
90
91    /// Set to `true` if the file needs flushing.
92    is_dirty: bool,
93}
94
95impl File {
96    /// Creates an async file from a blocking file.
97    fn new(inner: std::fs::File, is_dirty: bool) -> File {
98        let file = Arc::new(inner);
99        let unblock = Mutex::new(Unblock::new(ArcFile(file.clone())));
100        let read_pos = None;
101        File {
102            file,
103            unblock,
104            read_pos,
105            is_dirty,
106        }
107    }
108
109    /// Opens a file in read-only mode.
110    ///
111    /// See the [`OpenOptions::open()`] function for more options.
112    ///
113    /// # Errors
114    ///
115    /// An error will be returned in the following situations:
116    ///
117    /// * `path` does not point to an existing file.
118    /// * The current process lacks permissions to read the file.
119    /// * Some other I/O error occurred.
120    ///
121    /// For more details, see the list of errors documented by [`OpenOptions::open()`].
122    ///
123    /// # Examples
124    ///
125    /// ```no_run
126    /// use zng_task::fs::File;
127    ///
128    /// # futures_lite::future::block_on(async {
129    /// let file = File::open("a.txt").await?;
130    /// # std::io::Result::Ok(()) });
131    /// ```
132    pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
133        let path = path.as_ref().to_owned();
134        let file = unblock(move || std::fs::File::open(path)).await?;
135        Ok(File::new(file, false))
136    }
137
138    /// Opens a file in write-only mode.
139    ///
140    /// This method will create a file if it does not exist, and will truncate it if it does.
141    ///
142    /// See the [`OpenOptions::open`] function for more options.
143    ///
144    /// # Errors
145    ///
146    /// An error will be returned in the following situations:
147    ///
148    /// * The file's parent directory does not exist.
149    /// * The current process lacks permissions to write to the file.
150    /// * Some other I/O error occurred.
151    ///
152    /// For more details, see the list of errors documented by [`OpenOptions::open()`].
153    ///
154    /// # Examples
155    ///
156    /// ```no_run
157    /// use zng_task::fs::File;
158    ///
159    /// # futures_lite::future::block_on(async {
160    /// let file = File::create("a.txt").await?;
161    /// # std::io::Result::Ok(()) });
162    /// ```
163    pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
164        let path = path.as_ref().to_owned();
165        let file = unblock(move || std::fs::File::create(path)).await?;
166        Ok(File::new(file, false))
167    }
168
169    /// Synchronizes OS-internal buffered contents and metadata to disk.
170    ///
171    /// This function will ensure that all in-memory data reaches the filesystem.
172    ///
173    /// This can be used to handle errors that would otherwise only be caught by closing the file.
174    /// When a file is dropped, errors in synchronizing this in-memory data are ignored.
175    ///
176    /// # Examples
177    ///
178    /// ```no_run
179    /// use futures_lite::io::AsyncWriteExt;
180    /// use zng_task::fs::File;
181    ///
182    /// # futures_lite::future::block_on(async {
183    /// let mut file = File::create("a.txt").await?;
184    ///
185    /// file.write_all(b"Hello, world!").await?;
186    /// file.sync_all().await?;
187    /// # std::io::Result::Ok(()) });
188    /// ```
189    pub async fn sync_all(&self) -> io::Result<()> {
190        let mut inner = self.unblock.lock().await;
191        inner.flush().await?;
192        let file = self.file.clone();
193        unblock(move || file.sync_all()).await
194    }
195
196    /// Synchronizes OS-internal buffered contents to disk.
197    ///
198    /// This is similar to [`sync_all()`][`File::sync_all()`], except that file metadata may not
199    /// be synchronized.
200    ///
201    /// This is intended for use cases that must synchronize the contents of the file, but don't
202    /// need the file metadata synchronized to disk.
203    ///
204    /// Note that some platforms may simply implement this in terms of
205    /// [`sync_all()`][`File::sync_all()`].
206    ///
207    /// # Examples
208    ///
209    /// ```no_run
210    /// use futures_lite::io::AsyncWriteExt;
211    /// use zng_task::fs::File;
212    ///
213    /// # futures_lite::future::block_on(async {
214    /// let mut file = File::create("a.txt").await?;
215    ///
216    /// file.write_all(b"Hello, world!").await?;
217    /// file.sync_data().await?;
218    /// # std::io::Result::Ok(()) });
219    /// ```
220    pub async fn sync_data(&self) -> io::Result<()> {
221        let mut inner = self.unblock.lock().await;
222        inner.flush().await?;
223        let file = self.file.clone();
224        unblock(move || file.sync_data()).await
225    }
226
227    /// Truncates or extends the file.
228    ///
229    /// If `size` is less than the current file size, then the file will be truncated. If it is
230    /// greater than the current file size, then the file will be extended to `size` and have all
231    /// intermediate data filled with zeros.
232    ///
233    /// The file's cursor stays at the same position, even if the cursor ends up being past the end
234    /// of the file after this operation.
235    ///
236    /// # Examples
237    ///
238    /// ```no_run
239    /// use zng_task::fs::File;
240    ///
241    /// # futures_lite::future::block_on(async {
242    /// let mut file = File::create("a.txt").await?;
243    /// file.set_len(10).await?;
244    /// # std::io::Result::Ok(()) });
245    /// ```
246    pub async fn set_len(&self, size: u64) -> io::Result<()> {
247        let mut inner = self.unblock.lock().await;
248        inner.flush().await?;
249        let file = self.file.clone();
250        unblock(move || file.set_len(size)).await
251    }
252
253    /// Reads the file's metadata.
254    ///
255    /// # Examples
256    ///
257    /// ```no_run
258    /// use zng_task::fs::File;
259    ///
260    /// # futures_lite::future::block_on(async {
261    /// let file = File::open("a.txt").await?;
262    /// let metadata = file.metadata().await?;
263    /// # std::io::Result::Ok(()) });
264    /// ```
265    pub async fn metadata(&self) -> io::Result<Metadata> {
266        let file = self.file.clone();
267        unblock(move || file.metadata()).await
268    }
269
270    /// Changes the permissions on the file.
271    ///
272    /// # Errors
273    ///
274    /// An error will be returned in the following situations:
275    ///
276    /// * The current process lacks permissions to change attributes on the file.
277    /// * Some other I/O error occurred.
278    ///
279    /// # Examples
280    ///
281    /// ```no_run
282    /// use zng_task::fs::File;
283    ///
284    /// # futures_lite::future::block_on(async {
285    /// let file = File::create("a.txt").await?;
286    ///
287    /// let mut perms = file.metadata().await?.permissions();
288    /// perms.set_readonly(true);
289    /// file.set_permissions(perms).await?;
290    /// # std::io::Result::Ok(()) });
291    /// ```
292    pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
293        let file = self.file.clone();
294        unblock(move || file.set_permissions(perm)).await
295    }
296
297    /// Repositions the cursor after reading.
298    ///
299    /// When reading from a file, actual file reads run asynchronously in the background, which
300    /// means the real file cursor is usually ahead of the logical cursor, and the data between
301    /// them is buffered in memory. This kind of buffering is an important optimization.
302    ///
303    /// After reading ends, if we decide to perform a write or a seek operation, the real file
304    /// cursor must first be repositioned back to the correct logical position.
305    fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
306        if let Some(Ok(read_pos)) = self.read_pos {
307            ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Start(read_pos)))?;
308        }
309        self.read_pos = None;
310        Poll::Ready(Ok(()))
311    }
312
313    /// Returns the inner blocking file, if no task is running.
314    ///
315    /// This will flush any pending data I/O tasks before attempting to unwrap, it will fail
316    /// if there are pending metadata tasks. Note that dropping futures does not cancel file
317    /// tasks, you must await all pending futures for this conversion to succeed.
318    pub async fn try_unwrap(self) -> Result<std::fs::File, Self> {
319        // flush Unblock and drop its reference
320        let _ = self.unblock.into_inner().into_inner().await;
321
322        match Arc::try_unwrap(self.file) {
323            Ok(ready) => Ok(ready),
324            Err(pending) => {
325                // task associated with dropped future is still running
326                Err(Self {
327                    file: pending.clone(),
328                    unblock: Mutex::new(Unblock::new(ArcFile(pending))),
329                    is_dirty: false,
330                    read_pos: self.read_pos,
331                })
332            }
333        }
334    }
335}
336
337impl fmt::Debug for File {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        self.file.fmt(f)
340    }
341}
342
343impl From<std::fs::File> for File {
344    fn from(inner: std::fs::File) -> File {
345        File::new(inner, true)
346    }
347}
348
349#[cfg(unix)]
350impl std::os::unix::io::AsRawFd for File {
351    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
352        self.file.as_raw_fd()
353    }
354}
355
356#[cfg(windows)]
357impl std::os::windows::io::AsRawHandle for File {
358    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
359        self.file.as_raw_handle()
360    }
361}
362
363#[cfg(unix)]
364impl From<std::os::unix::io::OwnedFd> for File {
365    fn from(fd: std::os::unix::io::OwnedFd) -> Self {
366        File::from(std::fs::File::from(fd))
367    }
368}
369
370#[cfg(windows)]
371impl From<std::os::windows::io::OwnedHandle> for File {
372    fn from(fd: std::os::windows::io::OwnedHandle) -> Self {
373        File::from(std::fs::File::from(fd))
374    }
375}
376
377#[cfg(unix)]
378impl std::os::unix::io::AsFd for File {
379    fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
380        self.file.as_fd()
381    }
382}
383
384#[cfg(windows)]
385impl std::os::windows::io::AsHandle for File {
386    fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> {
387        self.file.as_handle()
388    }
389}
390
391impl AsyncRead for File {
392    fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
393        // Before reading begins, remember the current cursor position.
394        if self.read_pos.is_none() {
395            // Initialize the logical cursor to the current position in the file.
396            self.read_pos = Some(ready!(self.as_mut().poll_seek(cx, SeekFrom::Current(0))));
397        }
398
399        let n = ready!(Pin::new(self.unblock.get_mut()).poll_read(cx, buf))?;
400
401        // Update the logical cursor if the file is seekable.
402        if let Some(Ok(pos)) = self.read_pos.as_mut() {
403            *pos += n as u64;
404        }
405
406        Poll::Ready(Ok(n))
407    }
408}
409
410impl AsyncWrite for File {
411    fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
412        ready!(self.poll_reposition(cx))?;
413        self.is_dirty = true;
414        Pin::new(self.unblock.get_mut()).poll_write(cx, buf)
415    }
416
417    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
418        if self.is_dirty {
419            ready!(Pin::new(self.unblock.get_mut()).poll_flush(cx))?;
420            self.is_dirty = false;
421        }
422        Poll::Ready(Ok(()))
423    }
424
425    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
426        Pin::new(self.unblock.get_mut()).poll_close(cx)
427    }
428}
429
430impl AsyncSeek for File {
431    fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<io::Result<u64>> {
432        ready!(self.poll_reposition(cx))?;
433        Pin::new(self.unblock.get_mut()).poll_seek(cx, pos)
434    }
435}
436
437/// A wrapper around `Arc<std::fs::File>` that implements `Read`, `Write`, and `Seek`.
438struct ArcFile(Arc<std::fs::File>);
439
440impl io::Read for ArcFile {
441    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
442        (&*self.0).read(buf)
443    }
444}
445
446impl io::Write for ArcFile {
447    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
448        (&*self.0).write(buf)
449    }
450
451    fn flush(&mut self) -> io::Result<()> {
452        (&*self.0).flush()
453    }
454}
455
456impl io::Seek for ArcFile {
457    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
458        (&*self.0).seek(pos)
459    }
460}
461
462/// A builder for opening files with configurable options.
463///
464/// Files can be opened in [`read`][`OpenOptions::read()`] and/or
465/// [`write`][`OpenOptions::write()`] mode.
466///
467/// The [`append`][`OpenOptions::append()`] option opens files in a special writing mode that
468/// moves the file cursor to the end of file before every write operation.
469///
470/// It is also possible to [`truncate`][`OpenOptions::truncate()`] the file right after opening,
471/// to [`create`][`OpenOptions::create()`] a file if it doesn't exist yet, or to always create a
472/// new file with [`create_new`][`OpenOptions::create_new()`].
473///
474/// # Examples
475///
476/// Open a file for reading:
477///
478/// ```no_run
479/// use zng_task::fs::OpenOptions;
480///
481/// # futures_lite::future::block_on(async {
482/// let file = OpenOptions::new().read(true).open("a.txt").await?;
483/// # std::io::Result::Ok(()) });
484/// ```
485///
486/// Open a file for both reading and writing, and create it if it doesn't exist yet:
487///
488/// ```no_run
489/// use zng_task::fs::OpenOptions;
490///
491/// # futures_lite::future::block_on(async {
492/// let file = OpenOptions::new().read(true).write(true).create(true).open("a.txt").await?;
493/// # std::io::Result::Ok(()) });
494/// ```
495#[derive(Clone, Debug)]
496pub struct OpenOptions(std::fs::OpenOptions);
497
498impl OpenOptions {
499    /// Creates a blank set of options.
500    ///
501    /// All options are initially set to `false`.
502    ///
503    /// # Examples
504    ///
505    /// ```no_run
506    /// use zng_task::fs::OpenOptions;
507    ///
508    /// # futures_lite::future::block_on(async {
509    /// let file = OpenOptions::new().read(true).open("a.txt").await?;
510    /// # std::io::Result::Ok(()) });
511    /// ```
512    pub fn new() -> OpenOptions {
513        OpenOptions(std::fs::OpenOptions::new())
514    }
515
516    /// Configures the option for read mode.
517    ///
518    /// When set to `true`, this option means the file will be readable after opening.
519    ///
520    /// # Examples
521    ///
522    /// ```no_run
523    /// use zng_task::fs::OpenOptions;
524    ///
525    /// # futures_lite::future::block_on(async {
526    /// let file = OpenOptions::new().read(true).open("a.txt").await?;
527    /// # std::io::Result::Ok(()) });
528    /// ```
529    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
530        self.0.read(read);
531        self
532    }
533
534    /// Configures the option for write mode.
535    ///
536    /// When set to `true`, this option means the file will be writable after opening.
537    ///
538    /// If the file already exists, write calls on it will overwrite the previous contents without
539    /// truncating it.
540    ///
541    /// # Examples
542    ///
543    /// ```no_run
544    /// use zng_task::fs::OpenOptions;
545    ///
546    /// # futures_lite::future::block_on(async {
547    /// let file = OpenOptions::new().write(true).open("a.txt").await?;
548    /// # std::io::Result::Ok(()) });
549    /// ```
550    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
551        self.0.write(write);
552        self
553    }
554
555    /// Configures the option for append mode.
556    ///
557    /// When set to `true`, this option means the file will be writable after opening and the file
558    /// cursor will be moved to the end of file before every write operation.
559    ///
560    /// # Examples
561    ///
562    /// ```no_run
563    /// use zng_task::fs::OpenOptions;
564    ///
565    /// # futures_lite::future::block_on(async {
566    /// let file = OpenOptions::new().append(true).open("a.txt").await?;
567    /// # std::io::Result::Ok(()) });
568    /// ```
569    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
570        self.0.append(append);
571        self
572    }
573
574    /// Configures the option for truncating the previous file.
575    ///
576    /// When set to `true`, the file will be truncated to the length of 0 bytes.
577    ///
578    /// The file must be opened in [`write`][`OpenOptions::write()`] or
579    /// [`append`][`OpenOptions::append()`] mode for truncation to work.
580    ///
581    /// # Examples
582    ///
583    /// ```no_run
584    /// use zng_task::fs::OpenOptions;
585    ///
586    /// # futures_lite::future::block_on(async {
587    /// let file = OpenOptions::new().write(true).truncate(true).open("a.txt").await?;
588    /// # std::io::Result::Ok(()) });
589    /// ```
590    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
591        self.0.truncate(truncate);
592        self
593    }
594
595    /// Configures the option for creating a new file if it doesn't exist.
596    ///
597    /// When set to `true`, this option means a new file will be created if it doesn't exist.
598    ///
599    /// The file must be opened in [`write`][`OpenOptions::write()`] or
600    /// [`append`][`OpenOptions::append()`] mode for file creation to work.
601    ///
602    /// # Examples
603    ///
604    /// ```no_run
605    /// use zng_task::fs::OpenOptions;
606    ///
607    /// # futures_lite::future::block_on(async {
608    /// let file = OpenOptions::new().write(true).create(true).open("a.txt").await?;
609    /// # std::io::Result::Ok(()) });
610    /// ```
611    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
612        self.0.create(create);
613        self
614    }
615
616    /// Configures the option for creating a new file or failing if it already exists.
617    ///
618    /// When set to `true`, this option means a new file will be created, or the open operation
619    /// will fail if the file already exists.
620    ///
621    /// The file must be opened in [`write`][`OpenOptions::write()`] or
622    /// [`append`][`OpenOptions::append()`] mode for file creation to work.
623    ///
624    /// # Examples
625    ///
626    /// ```no_run
627    /// use zng_task::fs::OpenOptions;
628    ///
629    /// # futures_lite::future::block_on(async {
630    /// let file = OpenOptions::new().write(true).create_new(true).open("a.txt").await?;
631    /// # std::io::Result::Ok(()) });
632    /// ```
633    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
634        self.0.create_new(create_new);
635        self
636    }
637
638    /// Opens a file with the configured options.
639    ///
640    /// # Errors
641    ///
642    /// An error will be returned in the following situations:
643    ///
644    /// * The file does not exist and neither [`create`] nor [`create_new`] were set.
645    /// * The file's parent directory does not exist.
646    /// * The current process lacks permissions to open the file in the configured mode.
647    /// * The file already exists and [`create_new`] was set.
648    /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't,
649    ///   or none of [`read`], [`write`], and [`append`] modes was set.
650    /// * An OS-level occurred, like too many files are open or the file name is too long.
651    /// * Some other I/O error occurred.
652    ///
653    /// [`read`]: `OpenOptions::read()`
654    /// [`write`]: `OpenOptions::write()`
655    /// [`append`]: `OpenOptions::append()`
656    /// [`truncate`]: `OpenOptions::truncate()`
657    /// [`create`]: `OpenOptions::create()`
658    /// [`create_new`]: `OpenOptions::create_new()`
659    ///
660    /// # Examples
661    ///
662    /// ```no_run
663    /// use zng_task::fs::OpenOptions;
664    ///
665    /// # futures_lite::future::block_on(async {
666    /// let file = OpenOptions::new().read(true).open("a.txt").await?;
667    /// # std::io::Result::Ok(()) });
668    /// ```
669    pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
670        let path = path.as_ref().to_owned();
671        let options = self.0.clone();
672        async move {
673            let file = unblock(move || options.open(path)).await?;
674            Ok(File::new(file, false))
675        }
676    }
677}
678
679impl Default for OpenOptions {
680    fn default() -> Self {
681        Self::new()
682    }
683}
684
685#[cfg(unix)]
686impl unix::OpenOptionsExt for OpenOptions {
687    fn mode(&mut self, mode: u32) -> &mut Self {
688        self.0.mode(mode);
689        self
690    }
691
692    fn custom_flags(&mut self, flags: i32) -> &mut Self {
693        self.0.custom_flags(flags);
694        self
695    }
696}
697
698#[cfg(windows)]
699impl windows::OpenOptionsExt for OpenOptions {
700    fn access_mode(&mut self, access: u32) -> &mut Self {
701        self.0.access_mode(access);
702        self
703    }
704
705    fn share_mode(&mut self, val: u32) -> &mut Self {
706        self.0.share_mode(val);
707        self
708    }
709
710    fn custom_flags(&mut self, flags: u32) -> &mut Self {
711        self.0.custom_flags(flags);
712        self
713    }
714
715    fn attributes(&mut self, val: u32) -> &mut Self {
716        self.0.attributes(val);
717        self
718    }
719
720    fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
721        self.0.security_qos_flags(flags);
722        self
723    }
724}
725
726#[cfg_attr(not(any(windows, unix)), allow(dead_code))]
727mod __private {
728    #[doc(hidden)]
729    pub trait Sealed {}
730
731    impl Sealed for super::OpenOptions {}
732    impl Sealed for super::File {}
733}
734
735/// Unix-specific extensions.
736#[cfg(unix)]
737pub mod unix {
738    use super::__private::Sealed;
739
740    #[doc(inline)]
741    pub use async_fs::unix::*;
742
743    /// Unix-specific extensions to [`OpenOptions`].
744    ///
745    /// [`OpenOptions`]: crate::fs::OpenOptions
746    pub trait OpenOptionsExt: Sealed {
747        /// Sets the mode bits that a new file will be created with.
748        ///
749        /// If a new file is created as part of an [`OpenOptions::open()`] call then this
750        /// specified `mode` will be used as the permission bits for the new file.
751        ///
752        /// If no `mode` is set, the default of `0o666` will be used.
753        /// The operating system masks out bits with the system's `umask`, to produce
754        /// the final permissions.
755        ///
756        /// [`OpenOptions::open()`]: crate::fs::OpenOptions::open
757        ///
758        /// # Examples
759        ///
760        /// ```no_run
761        /// use zng_task::fs::{OpenOptions, unix::OpenOptionsExt};
762        ///
763        /// # futures_lite::future::block_on(async {
764        /// let mut options = OpenOptions::new();
765        /// // Read/write permissions for owner and read permissions for others.
766        /// options.mode(0o644);
767        /// let file = options.open("foo.txt").await?;
768        /// # std::io::Result::Ok(()) });
769        /// ```
770        fn mode(&mut self, mode: u32) -> &mut Self;
771
772        /// Passes custom flags to the `flags` argument of `open`.
773        ///
774        /// The bits that define the access mode are masked out with `O_ACCMODE`, to
775        /// ensure they do not interfere with the access mode set by Rust's options.
776        ///
777        /// Custom flags can only set flags, not remove flags set by Rust's options.
778        /// This options overwrites any previously set custom flags.
779        ///
780        /// # Examples
781        ///
782        /// ```no_run
783        /// # mod libc { pub const O_NOFOLLOW: i32 = 0x40000; }
784        /// use zng_task::fs::{OpenOptions, unix::OpenOptionsExt};
785        ///
786        /// # futures_lite::future::block_on(async {
787        /// let mut options = OpenOptions::new();
788        /// options.write(true);
789        /// options.custom_flags(libc::O_NOFOLLOW);
790        /// let file = options.open("foo.txt").await?;
791        /// # std::io::Result::Ok(()) });
792        /// ```
793        fn custom_flags(&mut self, flags: i32) -> &mut Self;
794    }
795}
796
797/// Windows-specific extensions.
798#[cfg(windows)]
799pub mod windows {
800    use super::__private::Sealed;
801
802    #[doc(inline)]
803    pub use async_fs::windows::*;
804
805    /// Windows-specific extensions to [`OpenOptions`].
806    ///
807    /// [`OpenOptions`]: crate::fs::OpenOptions
808    pub trait OpenOptionsExt: Sealed {
809        /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
810        /// with the specified value.
811        ///
812        /// This will override the `read`, `write`, and `append` flags on the
813        /// [`OpenOptions`] structure. This method provides fine-grained control over
814        /// the permissions to read, write and append data, attributes (like hidden
815        /// and system), and extended attributes.
816        ///
817        /// # Examples
818        ///
819        /// ```no_run
820        /// use zng_task::fs::{OpenOptions, windows::OpenOptionsExt};
821        ///
822        /// # futures_lite::future::block_on(async {
823        /// // Open without read and write permission, for example if you only need
824        /// // to call `stat` on the file
825        /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
826        /// # std::io::Result::Ok(()) });
827        /// ```
828        ///
829        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
830        /// [`OpenOptions`]: crate::fs::OpenOptions
831        fn access_mode(&mut self, access: u32) -> &mut Self;
832
833        /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
834        /// the specified value.
835        ///
836        /// By default `share_mode` is set to
837        /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
838        /// other processes to read, write, and delete/rename the same file
839        /// while it is open. Removing any of the flags will prevent other
840        /// processes from performing the corresponding operation until the file
841        /// handle is closed.
842        ///
843        /// # Examples
844        ///
845        /// ```no_run
846        /// use zng_task::fs::{OpenOptions, windows::OpenOptionsExt};
847        ///
848        /// # futures_lite::future::block_on(async {
849        /// // Do not allow others to read or modify this file while we have it open
850        /// // for writing.
851        /// let file = OpenOptions::new().write(true).share_mode(0).open("foo.txt").await?;
852        /// # std::io::Result::Ok(()) });
853        /// ```
854        ///
855        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
856        fn share_mode(&mut self, val: u32) -> &mut Self;
857
858        /// Sets extra flags for the `dwFileFlags` argument to the call to
859        /// [`CreateFile2`] to the specified value (or combines it with
860        /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
861        /// for [`CreateFile`]).
862        ///
863        /// Custom flags can only set flags, not remove flags set by Rust's options.
864        /// This option overwrites any previously set custom flags.
865        ///
866        /// # Examples
867        ///
868        /// ```no_run
869        /// use zng_task::fs::{OpenOptions, windows::OpenOptionsExt};
870        ///
871        /// # futures_lite::future::block_on(async {
872        /// let file = OpenOptions::new()
873        ///     .create(true)
874        ///     .write(true)
875        ///     .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE)
876        ///     .open("foo.txt")
877        ///     .await?;
878        /// # std::io::Result::Ok(()) });
879        /// ```
880        ///
881        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
882        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
883        fn custom_flags(&mut self, flags: u32) -> &mut Self;
884
885        /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
886        /// the specified value (or combines it with `custom_flags` and
887        /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
888        /// [`CreateFile`]).
889        ///
890        /// If a _new_ file is created because it does not yet exist and
891        /// `.create(true)` or `.create_new(true)` are specified, the new file is
892        /// given the attributes declared with `.attributes()`.
893        ///
894        /// If an _existing_ file is opened with `.create(true).truncate(true)`, its
895        /// existing attributes are preserved and combined with the ones declared
896        /// with `.attributes()`.
897        ///
898        /// In all other cases the attributes get ignored.
899        ///
900        /// # Examples
901        ///
902        /// ```no_run
903        /// use zng_task::fs::{OpenOptions, windows::OpenOptionsExt};
904        ///
905        /// # futures_lite::future::block_on(async {
906        /// let file = OpenOptions::new()
907        ///     .write(true)
908        ///     .create(true)
909        ///     .attributes(windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN)
910        ///     .open("foo.txt")
911        ///     .await?;
912        /// # std::io::Result::Ok(()) });
913        /// ```
914        ///
915        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
916        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
917        fn attributes(&mut self, val: u32) -> &mut Self;
918
919        /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
920        /// the specified value (or combines it with `custom_flags` and `attributes`
921        /// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
922        ///
923        /// By default `security_qos_flags` is not set. It should be specified when
924        /// opening a named pipe, to control to which degree a server process can
925        /// act on behalf of a client process (security impersonation level).
926        ///
927        /// When `security_qos_flags` is not set, a malicious program can gain the
928        /// elevated privileges of a privileged Rust process when it allows opening
929        /// user-specified paths, by tricking it into opening a named pipe. So
930        /// arguably `security_qos_flags` should also be set when opening arbitrary
931        /// paths. However the bits can then conflict with other flags, specifically
932        /// `FILE_FLAG_OPEN_NO_RECALL`.
933        ///
934        /// For information about possible values, see [Impersonation Levels] on the
935        /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
936        /// automatically when using this method.
937        ///
938        /// # Examples
939        ///
940        /// ```no_run
941        /// use zng_task::fs::{OpenOptions, windows::OpenOptionsExt};
942        ///
943        /// # futures_lite::future::block_on(async {
944        /// let file = OpenOptions::new()
945        ///     .write(true)
946        ///     .create(true)
947        ///     .security_qos_flags(windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION)
948        ///     .open(r"\\.\pipe\MyPipe")
949        ///     .await?;
950        /// # std::io::Result::Ok(()) });
951        /// ```
952        ///
953        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
954        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
955        /// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
956        fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
957    }
958}