compio_fs/
file.rs

1use std::{future::Future, io, mem::ManuallyDrop, panic::resume_unwind, path::Path};
2
3use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut};
4#[cfg(unix)]
5use compio_driver::op::FileStat;
6use compio_driver::{
7    ToSharedFd, impl_raw_fd,
8    op::{BufResultExt, CloseFile, ReadAt, Sync, WriteAt},
9};
10use compio_io::{AsyncReadAt, AsyncWriteAt};
11use compio_runtime::Attacher;
12#[cfg(all(unix, not(solarish)))]
13use {
14    compio_buf::{IoVectoredBuf, IoVectoredBufMut},
15    compio_driver::op::{ReadVectoredAt, WriteVectoredAt},
16};
17
18use crate::{Metadata, OpenOptions, Permissions};
19
20/// A reference to an open file on the filesystem.
21///
22/// An instance of a `File` can be read and/or written depending on what options
23/// it was opened with. The `File` type provides **positional** read and write
24/// operations. The file does not maintain an internal cursor. The caller is
25/// required to specify an offset when issuing an operation.
26#[derive(Debug, Clone)]
27pub struct File {
28    inner: Attacher<std::fs::File>,
29}
30
31impl File {
32    pub(crate) fn from_std(file: std::fs::File) -> io::Result<Self> {
33        Ok(Self {
34            inner: Attacher::new(file)?,
35        })
36    }
37
38    /// Attempts to open a file in read-only mode.
39    ///
40    /// See the [`OpenOptions::open`] method for more details.
41    pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
42        OpenOptions::new().read(true).open(path).await
43    }
44
45    /// Opens a file in write-only mode.
46    ///
47    /// This function will create a file if it does not exist,
48    /// and will truncate it if it does.
49    ///
50    /// See the [`OpenOptions::open`] function for more details.
51    pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
52        OpenOptions::new()
53            .create(true)
54            .write(true)
55            .truncate(true)
56            .open(path)
57            .await
58    }
59
60    /// Close the file. If the returned future is dropped before polling, the
61    /// file won't be closed.
62    pub fn close(self) -> impl Future<Output = io::Result<()>> {
63        // Make sure that fd won't be dropped after `close` called.
64        // Users may call this method and drop the future immediately. In that way
65        // `close` should be cancelled.
66        let this = ManuallyDrop::new(self);
67        async move {
68            let fd = ManuallyDrop::into_inner(this)
69                .inner
70                .into_inner()
71                .take()
72                .await;
73            if let Some(fd) = fd {
74                let op = CloseFile::new(fd.into());
75                compio_runtime::submit(op).await.0?;
76            }
77            Ok(())
78        }
79    }
80
81    /// Queries metadata about the underlying file.
82    #[cfg(windows)]
83    pub async fn metadata(&self) -> io::Result<Metadata> {
84        let file = self.inner.clone();
85        compio_runtime::spawn_blocking(move || file.metadata().map(Metadata::from_std))
86            .await
87            .unwrap_or_else(|e| resume_unwind(e))
88    }
89
90    /// Queries metadata about the underlying file.
91    #[cfg(unix)]
92    pub async fn metadata(&self) -> io::Result<Metadata> {
93        let op = FileStat::new(self.to_shared_fd());
94        let BufResult(res, op) = compio_runtime::submit(op).await;
95        res.map(|_| Metadata::from_stat(op.into_inner()))
96    }
97
98    /// Changes the permissions on the underlying file.
99    #[cfg(windows)]
100    pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
101        let file = self.inner.clone();
102        compio_runtime::spawn_blocking(move || file.set_permissions(perm.0))
103            .await
104            .unwrap_or_else(|e| resume_unwind(e))
105    }
106
107    /// Changes the permissions on the underlying file.
108    #[cfg(unix)]
109    pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
110        use std::os::unix::fs::PermissionsExt;
111
112        use compio_driver::{AsRawFd, syscall};
113
114        let file = self.inner.clone();
115        compio_runtime::spawn_blocking(move || {
116            syscall!(libc::fchmod(file.as_raw_fd(), perm.mode() as libc::mode_t))?;
117            Ok(())
118        })
119        .await
120        .unwrap_or_else(|e| resume_unwind(e))
121    }
122
123    async fn sync_impl(&self, datasync: bool) -> io::Result<()> {
124        let op = Sync::new(self.to_shared_fd(), datasync);
125        compio_runtime::submit(op).await.0?;
126        Ok(())
127    }
128
129    /// Attempts to sync all OS-internal metadata to disk.
130    ///
131    /// This function will attempt to ensure that all in-memory data reaches the
132    /// filesystem before returning.
133    pub async fn sync_all(&self) -> io::Result<()> {
134        self.sync_impl(false).await
135    }
136
137    /// This function is similar to [`sync_all`], except that it might not
138    /// synchronize file metadata to the filesystem.
139    ///
140    /// This is intended for use cases that must synchronize content, but don't
141    /// need the metadata on disk. The goal of this method is to reduce disk
142    /// operations.
143    ///
144    /// Note that some platforms may simply implement this in terms of
145    /// [`sync_all`].
146    ///
147    /// [`sync_all`]: File::sync_all
148    pub async fn sync_data(&self) -> io::Result<()> {
149        self.sync_impl(true).await
150    }
151}
152
153impl AsyncReadAt for File {
154    async fn read_at<T: IoBufMut>(&self, buffer: T, pos: u64) -> BufResult<usize, T> {
155        let fd = self.inner.to_shared_fd();
156        let op = ReadAt::new(fd, pos, buffer);
157        compio_runtime::submit(op).await.into_inner().map_advanced()
158    }
159
160    #[cfg(all(unix, not(solarish)))]
161    async fn read_vectored_at<T: IoVectoredBufMut>(
162        &self,
163        buffer: T,
164        pos: u64,
165    ) -> BufResult<usize, T> {
166        let fd = self.inner.to_shared_fd();
167        let op = ReadVectoredAt::new(fd, pos, buffer);
168        compio_runtime::submit(op).await.into_inner().map_advanced()
169    }
170}
171
172impl AsyncWriteAt for File {
173    #[inline]
174    async fn write_at<T: IoBuf>(&mut self, buf: T, pos: u64) -> BufResult<usize, T> {
175        (&*self).write_at(buf, pos).await
176    }
177
178    #[cfg(all(unix, not(solarish)))]
179    #[inline]
180    async fn write_vectored_at<T: IoVectoredBuf>(
181        &mut self,
182        buf: T,
183        pos: u64,
184    ) -> BufResult<usize, T> {
185        (&*self).write_vectored_at(buf, pos).await
186    }
187}
188
189impl AsyncWriteAt for &File {
190    async fn write_at<T: IoBuf>(&mut self, buffer: T, pos: u64) -> BufResult<usize, T> {
191        let fd = self.inner.to_shared_fd();
192        let op = WriteAt::new(fd, pos, buffer);
193        compio_runtime::submit(op).await.into_inner()
194    }
195
196    #[cfg(all(unix, not(solarish)))]
197    async fn write_vectored_at<T: IoVectoredBuf>(
198        &mut self,
199        buffer: T,
200        pos: u64,
201    ) -> BufResult<usize, T> {
202        let fd = self.inner.to_shared_fd();
203        let op = WriteVectoredAt::new(fd, pos, buffer);
204        compio_runtime::submit(op).await.into_inner()
205    }
206}
207
208impl_raw_fd!(File, std::fs::File, inner, file);