arcbox_virtio_blk/
direct_io.rs1#![cfg(target_os = "linux")]
4
5use std::os::unix::io::RawFd;
6
7pub struct DirectIoBackend {
9 fd: RawFd,
11 capacity: u64,
13 block_size: u32,
15 read_only: bool,
17}
18
19impl DirectIoBackend {
20 pub fn new(path: &std::path::Path, read_only: bool) -> std::io::Result<Self> {
26 let flags = if read_only {
27 libc::O_RDONLY | libc::O_DIRECT | libc::O_CLOEXEC
28 } else {
29 libc::O_RDWR | libc::O_DIRECT | libc::O_CLOEXEC
30 };
31
32 let path_cstr = std::ffi::CString::new(path.to_string_lossy().as_bytes())
33 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
34
35 let fd = unsafe { libc::open(path_cstr.as_ptr(), flags, 0o644) };
38
39 if fd < 0 {
40 return Err(std::io::Error::last_os_error());
41 }
42
43 let mut stat: libc::stat = unsafe { std::mem::zeroed() };
46 let ret = unsafe { libc::fstat(fd, &mut stat) };
47 if ret < 0 {
48 unsafe { libc::close(fd) };
49 return Err(std::io::Error::last_os_error());
50 }
51
52 let capacity = stat.st_size as u64 / 512;
53
54 tracing::info!(
55 "Opened {} with O_DIRECT, capacity={} sectors",
56 path.display(),
57 capacity
58 );
59
60 Ok(Self {
61 fd,
62 capacity,
63 block_size: 512,
64 read_only,
65 })
66 }
67
68 pub fn pread(&self, offset: u64, buf: &mut [u8]) -> std::io::Result<usize> {
70 let ret = unsafe {
72 libc::pread(
73 self.fd,
74 buf.as_mut_ptr() as *mut libc::c_void,
75 buf.len(),
76 offset as libc::off_t,
77 )
78 };
79
80 if ret < 0 {
81 Err(std::io::Error::last_os_error())
82 } else {
83 Ok(ret as usize)
84 }
85 }
86
87 pub fn pwrite(&self, offset: u64, buf: &[u8]) -> std::io::Result<usize> {
89 if self.read_only {
90 return Err(std::io::Error::new(
91 std::io::ErrorKind::PermissionDenied,
92 "Device is read-only",
93 ));
94 }
95
96 let ret = unsafe {
98 libc::pwrite(
99 self.fd,
100 buf.as_ptr() as *const libc::c_void,
101 buf.len(),
102 offset as libc::off_t,
103 )
104 };
105
106 if ret < 0 {
107 Err(std::io::Error::last_os_error())
108 } else {
109 Ok(ret as usize)
110 }
111 }
112
113 pub fn sync(&self) -> std::io::Result<()> {
115 let ret = unsafe { libc::fdatasync(self.fd) };
117 if ret < 0 {
118 Err(std::io::Error::last_os_error())
119 } else {
120 Ok(())
121 }
122 }
123}
124
125impl Drop for DirectIoBackend {
126 fn drop(&mut self) {
127 if self.fd >= 0 {
128 unsafe { libc::close(self.fd) };
130 }
131 }
132}