1use crate::{fs::asyncify, util::as_ref::OwnedBuf};
2
3use std::{io, path::Path};
4
5pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
27 let path = path.as_ref();
28 let contents = crate::util::as_ref::upgrade(contents);
29
30 #[cfg(all(
31 tokio_unstable,
32 feature = "io-uring",
33 feature = "rt",
34 feature = "fs",
35 target_os = "linux"
36 ))]
37 {
38 if let Ok(handle) = crate::runtime::Handle::try_current() {
39 if let Some(driver_handle) = handle.inner.driver().io.as_ref() {
40 if driver_handle
41 .check_and_init(io_uring::opcode::Write::CODE)
42 .await?
43 {
44 return write_uring(path, contents).await;
45 }
46 }
47 }
48 }
49
50 write_spawn_blocking(path, contents).await
51}
52
53#[cfg(all(
54 tokio_unstable,
55 feature = "io-uring",
56 feature = "rt",
57 feature = "fs",
58 target_os = "linux"
59))]
60async fn write_uring(path: &Path, mut buf: OwnedBuf) -> io::Result<()> {
61 use crate::{fs::OpenOptions, runtime::driver::op::Op};
62 use std::os::fd::OwnedFd;
63
64 let file = OpenOptions::new()
65 .write(true)
66 .create(true)
67 .truncate(true)
68 .open(path)
69 .await?;
70
71 let mut fd: OwnedFd = file
72 .try_into_std()
73 .expect("unexpected in-flight operation detected")
74 .into();
75
76 let total: usize = buf.as_ref().len();
77 let mut buf_offset: usize = 0;
78 let mut file_offset: u64 = 0;
79 while buf_offset < total {
80 let (res, r_buf, r_fd) = Op::write_at(fd, buf, buf_offset, file_offset)?.await;
81
82 let (Some(r_buf), Some(r_fd)) = (r_buf, r_fd) else {
85 return Err(res.expect_err("a cancelled op completes with an error"));
86 };
87
88 let n = match res {
89 Ok(0) => return Err(io::ErrorKind::WriteZero.into()),
90 Ok(n) => n,
91 Err(e) if e.kind() == io::ErrorKind::Interrupted => 0,
92 Err(e) => return Err(e),
93 };
94
95 buf = r_buf;
96 fd = r_fd;
97 buf_offset += n as usize;
98 file_offset += n as u64;
99 }
100
101 Ok(())
102}
103
104async fn write_spawn_blocking(path: &Path, contents: OwnedBuf) -> io::Result<()> {
105 let path = path.to_owned();
106 asyncify(move || std::fs::write(path, contents)).await
107}