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}