qcow2_rs/
sync_io.rs

1use crate::error::Qcow2Result;
2use crate::ops::*;
3#[rustversion::before(1.75)]
4use async_trait::async_trait;
5#[cfg(target_os = "linux")]
6use nix::fcntl::{fallocate, FallocateFlags};
7use std::cell::RefCell;
8use std::fs::{File, OpenOptions};
9use std::os::unix::io::AsRawFd;
10use std::path::Path;
11
12#[derive(Debug)]
13pub struct Qcow2IoSync {
14    _file: RefCell<File>,
15    fd: i32,
16}
17
18impl Qcow2IoSync {
19    pub fn new(path: &Path, ro: bool, dio: bool) -> Qcow2IoSync {
20        #[cfg(target_os = "macos")]
21        fn set_dio(_file: &File) {}
22
23        #[cfg(not(target_os = "macos"))]
24        fn set_dio(file: &File) {
25            unsafe {
26                libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_DIRECT);
27            }
28        }
29
30        let file = OpenOptions::new().read(true).write(!ro).open(path).unwrap();
31
32        if dio {
33            set_dio(&file);
34        }
35
36        let fd = file.as_raw_fd();
37        Qcow2IoSync {
38            _file: RefCell::new(file),
39            fd,
40        }
41    }
42
43    #[inline(always)]
44    async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Qcow2Result<usize> {
45        let res = unsafe {
46            libc::pread(
47                self.fd,
48                buf.as_mut_ptr() as *mut libc::c_void,
49                buf.len(),
50                offset as libc::off_t,
51            )
52        };
53
54        if res < 0 {
55            Err("libc::pread failed".into())
56        } else {
57            if (res as usize) != buf.len() {
58                eprintln!(
59                    "short read: ask for {}, read {}, offset {:x}",
60                    buf.len(),
61                    res,
62                    offset
63                );
64            }
65            Ok(res as usize)
66        }
67    }
68
69    #[inline(always)]
70    async fn write_at(&self, offset: u64, buf: &[u8]) -> Qcow2Result<()> {
71        let res = unsafe {
72            libc::pwrite(
73                self.fd,
74                buf.as_ptr() as *const libc::c_void,
75                buf.len(),
76                offset as libc::off_t,
77            )
78        };
79
80        if res < 0 {
81            Err("libc::pwrite failed".into())
82        } else {
83            if (res as usize) != buf.len() {
84                eprintln!(
85                    "short write: ask for {}, read {}, offset {:x}",
86                    buf.len(),
87                    res,
88                    offset
89                );
90            }
91            Ok(())
92        }
93    }
94}
95
96#[rustversion::attr(before(1.75), async_trait(?Send))]
97impl Qcow2IoOps for Qcow2IoSync {
98    async fn read_to(&self, offset: u64, buf: &mut [u8]) -> Qcow2Result<usize> {
99        self.read_at(offset, buf).await
100    }
101
102    async fn write_from(&self, offset: u64, buf: &[u8]) -> Qcow2Result<()> {
103        self.write_at(offset, buf).await
104    }
105
106    #[cfg(target_os = "linux")]
107    async fn fallocate(&self, offset: u64, len: usize, flags: u32) -> Qcow2Result<()> {
108        let f = if (flags & Qcow2OpsFlags::FALLOCATE_ZERO_RAGE) != 0 {
109            FallocateFlags::FALLOC_FL_PUNCH_HOLE | FallocateFlags::FALLOC_FL_ZERO_RANGE
110        } else {
111            FallocateFlags::FALLOC_FL_PUNCH_HOLE
112        };
113
114        Ok(fallocate(
115            self.fd,
116            f,
117            offset as libc::off_t,
118            len as libc::off_t,
119        )?)
120    }
121    #[cfg(not(target_os = "linux"))]
122    async fn fallocate(&self, offset: u64, len: usize, _flags: u32) -> Qcow2Result<()> {
123        let mut data = crate::helpers::Qcow2IoBuf::<u8>::new(len);
124
125        data.zero_buf();
126        self.write_at(offset, &data).await
127    }
128
129    #[cfg(not(target_os = "windows"))]
130    async fn fsync(&self, _offset: u64, _len: usize, _flags: u32) -> Qcow2Result<()> {
131        Ok(nix::unistd::fsync(self.fd)?)
132    }
133    #[cfg(target_os = "windows")]
134    async fn fsync(&self, _offset: u64, _len: usize, _flags: u32) -> Qcow2Result<()> {
135        let res = unsafe { libc::fsync(self.fd) };
136
137        Ok(res)
138    }
139}