1use crate::error::Qcow2Result;
2use crate::helpers::slice_to_vec;
3use crate::ops::*;
4#[rustversion::before(1.75)]
5use async_trait::async_trait;
6use nix::fcntl::{fallocate, FallocateFlags};
7use std::os::unix::io::AsRawFd;
8use std::path::Path;
9use tokio_uring::fs::{File, OpenOptions};
10
11#[derive(Debug)]
12pub struct Qcow2IoUring {
13 file: File,
14}
15
16impl Qcow2IoUring {
17 pub async fn new(path: &Path, ro: bool, dio: bool) -> Qcow2IoUring {
18 let file = OpenOptions::new()
19 .read(true)
20 .write(!ro)
21 .open(path.to_path_buf())
22 .await
23 .unwrap();
24
25 if dio {
26 unsafe {
27 libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_DIRECT);
28 }
29 }
30 Qcow2IoUring { file }
31 }
32}
33
34#[rustversion::attr(before(1.75), async_trait(?Send))]
35impl Qcow2IoOps for Qcow2IoUring {
36 async fn read_to(&self, offset: u64, buf: &mut [u8]) -> Qcow2Result<usize> {
37 let ubuf = slice_to_vec::<u8>(buf);
38 let (res, ubuf) = self.file.read_at(ubuf, offset).await;
39
40 std::mem::forget(ubuf);
41 match res {
42 Err(_) => Err("tokio-uring read failed".into()),
43 Ok(r) => Ok(r),
44 }
45 }
46
47 async fn write_from(&self, offset: u64, buf: &[u8]) -> Qcow2Result<()> {
48 let ubuf = slice_to_vec::<u8>(buf);
49
50 let (res, ubuf) = self.file.write_at(ubuf, offset).await;
52
53 std::mem::forget(ubuf);
54 match res {
55 Err(_) => Err("tokio-uring write failed".into()),
56 Ok(_) => Ok(()),
57 }
58 }
59
60 async fn fallocate(&self, offset: u64, len: usize, flags: u32) -> Qcow2Result<()> {
61 let f = if (flags & Qcow2OpsFlags::FALLOCATE_ZERO_RAGE) != 0 {
75 FallocateFlags::FALLOC_FL_PUNCH_HOLE | FallocateFlags::FALLOC_FL_ZERO_RANGE
76 } else {
77 FallocateFlags::FALLOC_FL_PUNCH_HOLE
78 };
79
80 Ok(fallocate(
81 self.file.as_raw_fd(),
82 f,
83 offset as libc::off_t,
84 len as libc::off_t,
85 )?)
86 }
87
88 async fn fsync(&self, _offset: u64, _len: usize, _flags: u32) -> Qcow2Result<()> {
89 self.file.sync_all().await?;
90 Ok(())
91 }
92}