1use std::{future::Future, io, mem::ManuallyDrop, path::Path};
2
3use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut};
4#[cfg(unix)]
5use compio_driver::op::FileStat;
6use compio_driver::{
7 BufferRef, ResultTakeBuffer, ToSharedFd, impl_raw_fd,
8 op::{BufResultExt, CloseFile, ReadAt, ReadManagedAt, Sync, WriteAt},
9};
10use compio_io::{AsyncReadAt, AsyncReadManagedAt, AsyncWriteAt, util::Splittable};
11use compio_runtime::{Runtime, fd::AsyncFd};
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#[derive(Debug, Clone)]
51pub struct File {
52 pub(crate) inner: AsyncFd<std::fs::File>,
53}
54
55impl File {
56 pub(crate) fn from_std(file: std::fs::File) -> io::Result<Self> {
57 Ok(Self {
58 inner: AsyncFd::new(file)?,
59 })
60 }
61
62 pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
66 OpenOptions::new().read(true).open(path).await
67 }
68
69 pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
76 OpenOptions::new()
77 .create(true)
78 .write(true)
79 .truncate(true)
80 .open(path)
81 .await
82 }
83
84 pub fn close(self) -> impl Future<Output = io::Result<()>> {
95 let this = ManuallyDrop::new(self);
99 async move {
100 let fd = ManuallyDrop::into_inner(this)
101 .inner
102 .into_inner()
103 .take()
104 .await;
105 if let Some(fd) = fd {
106 let op = CloseFile::new(fd.into());
107 compio_runtime::submit(op).await.0?;
108 }
109 Ok(())
110 }
111 }
112
113 #[cfg(windows)]
115 pub async fn metadata(&self) -> io::Result<Metadata> {
116 crate::spawn_blocking_with(self.to_shared_fd(), |file| {
117 file.metadata().map(Metadata::from_std)
118 })
119 .await
120 }
121
122 #[cfg(windows)]
123 pub async fn set_len(&self, size: u64) -> io::Result<()> {
126 crate::spawn_blocking_with(self.to_shared_fd(), move |file| file.set_len(size)).await
127 }
128
129 #[cfg(unix)]
130 pub async fn set_len(&self, size: u64) -> io::Result<()> {
136 use compio_driver::op::TruncateFile;
137
138 let op = TruncateFile::new(self.to_shared_fd(), size);
139 compio_runtime::submit(op).await.0.map(|_| ())
140 }
141
142 #[cfg(unix)]
144 pub async fn metadata(&self) -> io::Result<Metadata> {
145 let op = FileStat::new(self.to_shared_fd());
146 let BufResult(res, op) = compio_runtime::submit(op).await;
147 res.map(|_| Metadata::from_attr(op.into_inner()))
148 }
149
150 #[cfg(any(target_os = "linux", target_os = "android"))]
173 pub async fn get_xattr<T: IoBufMut>(
174 &self,
175 name: impl AsRef<std::ffi::OsStr>,
176 buffer: T,
177 ) -> BufResult<usize, T> {
178 use std::{ffi::CString, os::unix::ffi::OsStrExt};
179
180 use compio_buf::{IoBufMutExt, buf_try};
181 use compio_driver::op::FGetXattr;
182
183 let (name, mut buffer) = buf_try!(
184 CString::new(name.as_ref().as_bytes()).map_err(io::Error::from),
185 buffer
186 );
187 let query_size = buffer.buf_capacity() == 0;
188 let op = FGetXattr::new(self.to_shared_fd(), name, buffer);
189 let res = compio_runtime::submit(op).await.into_inner();
190 if query_size {
191 res
192 } else {
193 unsafe { res.map_advanced() }
198 }
199 }
200
201 #[cfg(windows)]
203 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
204 crate::spawn_blocking_with(self.to_shared_fd(), move |file| {
205 if let Some(p) = perm.0.original {
206 file.set_permissions(p)
207 } else {
208 let mut p = file.metadata()?.permissions();
209 p.set_readonly(perm.readonly());
210 file.set_permissions(p)
211 }
212 })
213 .await
214 }
215
216 #[cfg(unix)]
218 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
219 crate::spawn_blocking_with(self.to_shared_fd(), |file| file.set_permissions(perm.0)).await
220 }
221
222 async fn sync_impl(&self, datasync: bool) -> io::Result<()> {
223 let op = Sync::new(self.to_shared_fd(), datasync);
224 compio_runtime::submit(op).await.0?;
225 Ok(())
226 }
227
228 pub async fn sync_all(&self) -> io::Result<()> {
233 self.sync_impl(false).await
234 }
235
236 pub async fn sync_data(&self) -> io::Result<()> {
248 self.sync_impl(true).await
249 }
250}
251
252impl AsyncReadAt for File {
253 async fn read_at<T: IoBufMut>(&self, buffer: T, pos: u64) -> BufResult<usize, T> {
254 let fd = self.inner.to_shared_fd();
255 let op = ReadAt::new(fd, pos, buffer);
256 let res = compio_runtime::submit(op).await.into_inner();
257 unsafe { res.map_advanced() }
258 }
259
260 #[cfg(all(unix, not(solarish)))]
261 async fn read_vectored_at<T: IoVectoredBufMut>(
262 &self,
263 buffer: T,
264 pos: u64,
265 ) -> BufResult<usize, T> {
266 use compio_driver::op::VecBufResultExt;
267
268 let fd = self.inner.to_shared_fd();
269 let op = ReadVectoredAt::new(fd, pos, buffer);
270 let res = compio_runtime::submit(op).await.into_inner();
271 unsafe { res.map_vec_advanced() }
272 }
273}
274
275impl AsyncReadManagedAt for File {
276 type Buffer = BufferRef;
277
278 async fn read_managed_at(&self, len: usize, pos: u64) -> io::Result<Option<Self::Buffer>> {
279 let fd = self.inner.to_shared_fd();
280 let res = Runtime::with_current(|rt| {
281 let buffer_pool = rt.buffer_pool()?;
282 let op = ReadManagedAt::new(fd, pos, &buffer_pool, len)?;
283 io::Result::Ok(rt.submit(op))
284 })?
285 .await;
286 unsafe { res.take_buffer() }
287 }
288}
289
290impl AsyncWriteAt for File {
291 #[inline]
292 async fn write_at<T: IoBuf>(&mut self, buf: T, pos: u64) -> BufResult<usize, T> {
293 (&*self).write_at(buf, pos).await
294 }
295
296 #[cfg(all(unix, not(solarish)))]
297 #[inline]
298 async fn write_vectored_at<T: IoVectoredBuf>(
299 &mut self,
300 buf: T,
301 pos: u64,
302 ) -> BufResult<usize, T> {
303 (&*self).write_vectored_at(buf, pos).await
304 }
305}
306
307impl AsyncWriteAt for &File {
308 async fn write_at<T: IoBuf>(&mut self, buffer: T, pos: u64) -> BufResult<usize, T> {
309 let fd = self.inner.to_shared_fd();
310 let op = WriteAt::new(fd, pos, buffer);
311 compio_runtime::submit(op).await.into_inner()
312 }
313
314 #[cfg(all(unix, not(solarish)))]
315 async fn write_vectored_at<T: IoVectoredBuf>(
316 &mut self,
317 buffer: T,
318 pos: u64,
319 ) -> BufResult<usize, T> {
320 let fd = self.inner.to_shared_fd();
321 let op = WriteVectoredAt::new(fd, pos, buffer);
322 compio_runtime::submit(op).await.into_inner()
323 }
324}
325
326impl Splittable for File {
327 type ReadHalf = File;
328 type WriteHalf = File;
329
330 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
331 (self.clone(), self)
332 }
333}
334
335impl Splittable for &File {
336 type ReadHalf = File;
337 type WriteHalf = File;
338
339 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
340 (self.clone(), self.clone())
341 }
342}
343
344impl Splittable for &mut File {
345 type ReadHalf = File;
346 type WriteHalf = File;
347
348 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
349 (self.clone(), self.clone())
350 }
351}
352
353impl_raw_fd!(File, std::fs::File, inner, file);