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)]
58pub struct File {
59 pub(crate) inner: AsyncFd<std::fs::File>,
60}
61
62impl File {
63 pub(crate) fn from_std(file: std::fs::File) -> io::Result<Self> {
64 Ok(Self {
65 inner: AsyncFd::new(file)?,
66 })
67 }
68
69 pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
73 OpenOptions::new().read(true).open(path).await
74 }
75
76 pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
83 OpenOptions::new()
84 .create(true)
85 .write(true)
86 .truncate(true)
87 .open(path)
88 .await
89 }
90
91 pub fn close(self) -> impl Future<Output = io::Result<()>> {
102 let this = ManuallyDrop::new(self);
106 async move {
107 let fd = ManuallyDrop::into_inner(this)
108 .inner
109 .into_inner()
110 .take()
111 .await;
112 if let Some(fd) = fd {
113 let op = CloseFile::new(fd.into());
114 compio_runtime::submit(op).await.0?;
115 }
116 Ok(())
117 }
118 }
119
120 #[cfg(windows)]
122 pub async fn metadata(&self) -> io::Result<Metadata> {
123 crate::spawn_blocking_with(self.to_shared_fd(), |file| {
124 file.metadata().map(Metadata::from_std)
125 })
126 .await
127 }
128
129 #[cfg(windows)]
130 pub async fn set_len(&self, size: u64) -> io::Result<()> {
133 crate::spawn_blocking_with(self.to_shared_fd(), move |file| file.set_len(size)).await
134 }
135
136 #[cfg(unix)]
137 pub async fn set_len(&self, size: u64) -> io::Result<()> {
143 use compio_driver::op::TruncateFile;
144
145 let op = TruncateFile::new(self.to_shared_fd(), size);
146 compio_runtime::submit(op).await.0.map(|_| ())
147 }
148
149 #[cfg(unix)]
151 pub async fn metadata(&self) -> io::Result<Metadata> {
152 let op = FileStat::new(self.to_shared_fd());
153 let BufResult(res, op) = compio_runtime::submit(op).await;
154 res.map(|_| Metadata::from_attr(op.into_inner()))
155 }
156
157 #[cfg(any(target_os = "linux", target_os = "android"))]
180 pub async fn get_xattr<T: IoBufMut>(
181 &self,
182 name: impl AsRef<std::ffi::OsStr>,
183 buffer: T,
184 ) -> BufResult<usize, T> {
185 use std::{ffi::CString, os::unix::ffi::OsStrExt};
186
187 use compio_buf::{IoBufMutExt, buf_try};
188 use compio_driver::op::FGetXattr;
189
190 let (name, mut buffer) = buf_try!(
191 CString::new(name.as_ref().as_bytes()).map_err(io::Error::from),
192 buffer
193 );
194 let query_size = buffer.buf_capacity() == 0;
195 let op = FGetXattr::new(self.to_shared_fd(), name, buffer);
196 let res = compio_runtime::submit(op).await.into_inner();
197 if query_size {
198 res
199 } else {
200 unsafe { res.map_advanced() }
205 }
206 }
207
208 #[cfg(windows)]
210 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
211 crate::spawn_blocking_with(self.to_shared_fd(), move |file| {
212 if let Some(p) = perm.0.original {
213 file.set_permissions(p)
214 } else {
215 let mut p = file.metadata()?.permissions();
216 p.set_readonly(perm.readonly());
217 file.set_permissions(p)
218 }
219 })
220 .await
221 }
222
223 #[cfg(unix)]
225 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
226 crate::spawn_blocking_with(self.to_shared_fd(), |file| file.set_permissions(perm.0)).await
227 }
228
229 async fn sync_impl(&self, datasync: bool) -> io::Result<()> {
230 let op = Sync::new(self.to_shared_fd(), datasync);
231 compio_runtime::submit(op).await.0?;
232 Ok(())
233 }
234
235 pub async fn sync_all(&self) -> io::Result<()> {
240 self.sync_impl(false).await
241 }
242
243 pub async fn sync_data(&self) -> io::Result<()> {
255 self.sync_impl(true).await
256 }
257}
258
259impl AsyncReadAt for File {
260 async fn read_at<T: IoBufMut>(&self, buffer: T, pos: u64) -> BufResult<usize, T> {
261 let fd = self.inner.to_shared_fd();
262 let op = ReadAt::new(fd, pos, buffer);
263 let res = compio_runtime::submit(op).await.into_inner();
264 unsafe { res.map_advanced() }
265 }
266
267 #[cfg(all(unix, not(solarish)))]
268 async fn read_vectored_at<T: IoVectoredBufMut>(
269 &self,
270 buffer: T,
271 pos: u64,
272 ) -> BufResult<usize, T> {
273 use compio_driver::op::VecBufResultExt;
274
275 let fd = self.inner.to_shared_fd();
276 let op = ReadVectoredAt::new(fd, pos, buffer);
277 let res = compio_runtime::submit(op).await.into_inner();
278 unsafe { res.map_vec_advanced() }
279 }
280}
281
282impl AsyncReadManagedAt for File {
283 type Buffer = BufferRef;
284
285 async fn read_managed_at(&self, len: usize, pos: u64) -> io::Result<Option<Self::Buffer>> {
286 let fd = self.inner.to_shared_fd();
287 let res = Runtime::with_current(|rt| {
288 let buffer_pool = rt.buffer_pool()?;
289 let op = ReadManagedAt::new(fd, pos, &buffer_pool, len)?;
290 io::Result::Ok(rt.submit(op))
291 })?
292 .await;
293 unsafe { res.take_buffer() }
294 }
295}
296
297impl AsyncWriteAt for File {
298 #[inline]
299 async fn write_at<T: IoBuf>(&mut self, buf: T, pos: u64) -> BufResult<usize, T> {
300 (&*self).write_at(buf, pos).await
301 }
302
303 #[cfg(all(unix, not(solarish)))]
304 #[inline]
305 async fn write_vectored_at<T: IoVectoredBuf>(
306 &mut self,
307 buf: T,
308 pos: u64,
309 ) -> BufResult<usize, T> {
310 (&*self).write_vectored_at(buf, pos).await
311 }
312}
313
314impl AsyncWriteAt for &File {
315 async fn write_at<T: IoBuf>(&mut self, buffer: T, pos: u64) -> BufResult<usize, T> {
316 let fd = self.inner.to_shared_fd();
317 let op = WriteAt::new(fd, pos, buffer);
318 compio_runtime::submit(op).await.into_inner()
319 }
320
321 #[cfg(all(unix, not(solarish)))]
322 async fn write_vectored_at<T: IoVectoredBuf>(
323 &mut self,
324 buffer: T,
325 pos: u64,
326 ) -> BufResult<usize, T> {
327 let fd = self.inner.to_shared_fd();
328 let op = WriteVectoredAt::new(fd, pos, buffer);
329 compio_runtime::submit(op).await.into_inner()
330 }
331}
332
333impl Splittable for File {
334 type ReadHalf = File;
335 type WriteHalf = File;
336
337 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
338 (self.clone(), self)
339 }
340}
341
342impl Splittable for &File {
343 type ReadHalf = File;
344 type WriteHalf = File;
345
346 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
347 (self.clone(), self.clone())
348 }
349}
350
351impl Splittable for &mut File {
352 type ReadHalf = File;
353 type WriteHalf = File;
354
355 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
356 (self.clone(), self.clone())
357 }
358}
359
360impl_raw_fd!(File, std::fs::File, inner, file);