xref: /aosp_15_r20/external/crosvm/base/src/sys/windows/file_traits.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::fs::File;
6 use std::io::Error;
7 use std::io::Result;
8 
9 use crate::descriptor::AsRawDescriptor;
10 use crate::FileAllocate;
11 use crate::FileReadWriteAtVolatile;
12 use crate::FileReadWriteVolatile;
13 use crate::VolatileSlice;
14 use crate::WriteZeroesAt;
15 
16 impl FileReadWriteVolatile for File {
read_volatile(&mut self, slice: VolatileSlice) -> Result<usize>17     fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
18         let mut bytes = 0;
19         // SAFETY:
20         // Safe because only bytes inside the slice are accessed and the kernel is expected
21         // to handle arbitrary memory for I/O.
22         let ret = unsafe {
23             winapi::um::fileapi::ReadFile(
24                 self.as_raw_descriptor(),
25                 slice.as_ptr() as *mut libc::c_void,
26                 slice.size().try_into().unwrap(),
27                 &mut bytes,
28                 std::ptr::null_mut(),
29             )
30         };
31 
32         if ret > 0 {
33             Ok(bytes as usize)
34         } else {
35             Err(Error::last_os_error())
36         }
37     }
38 
read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize>39     fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
40         if bufs.is_empty() {
41             return Ok(0);
42         }
43 
44         // Windows has ReadFileScatter, but that requires the buffers to all be the same
45         // size and aligned to a page size boundary.
46         // readv makes some guarantees that we can't guarantee in windows, like atomicity.
47         let mut ret = 0usize;
48         for buf in bufs.iter() {
49             match self.read_volatile(*buf) {
50                 Ok(bytes) => ret += bytes,
51                 Err(_) => return Err(Error::last_os_error()),
52             }
53         }
54 
55         Ok(ret)
56     }
57 
write_volatile(&mut self, slice: VolatileSlice) -> Result<usize>58     fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
59         let mut bytes = 0;
60         // SAFETY:
61         // Safe because only bytes inside the slice are accessed and the kernel is expected
62         // to handle arbitrary memory for I/O.
63         let ret = unsafe {
64             winapi::um::fileapi::WriteFile(
65                 self.as_raw_descriptor(),
66                 slice.as_ptr() as *mut libc::c_void,
67                 slice.size().try_into().unwrap(),
68                 &mut bytes,
69                 std::ptr::null_mut(),
70             )
71         };
72 
73         if ret > 0 {
74             Ok(bytes as usize)
75         } else {
76             Err(Error::last_os_error())
77         }
78     }
79 
write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize>80     fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
81         if bufs.is_empty() {
82             return Ok(0);
83         }
84 
85         // Windows has WriteFileGather, but that requires the buffers to all be the same
86         // size and aligned to a page size boundary, and only writes one page of data
87         // from each buffer.
88         // writev makes some guarantees that we can't guarantee in windows, like atomicity.
89         let mut ret = 0usize;
90         for buf in bufs.iter() {
91             match self.write_volatile(*buf) {
92                 Ok(bytes) => ret += bytes,
93                 Err(_) => return Err(Error::last_os_error()),
94             }
95         }
96 
97         Ok(ret)
98     }
99 }
100 
101 impl FileReadWriteAtVolatile for File {
read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> Result<usize>102     fn read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> Result<usize> {
103         // The unix implementation uses pread, which doesn't modify the file
104         // pointer. Windows doesn't have an option for that, unfortunately.
105 
106         let mut bytes = 0;
107 
108         // SAFETY:
109         // Safe because only bytes inside the slice are accessed and the kernel is expected
110         // to handle arbitrary memory for I/O.
111         let ret = unsafe {
112             let mut overlapped: winapi::um::minwinbase::OVERLAPPED = std::mem::zeroed();
113             overlapped.u.s_mut().Offset = offset as u32;
114             overlapped.u.s_mut().OffsetHigh = (offset >> 32) as u32;
115 
116             winapi::um::fileapi::ReadFile(
117                 self.as_raw_descriptor(),
118                 slice.as_ptr() as *mut libc::c_void,
119                 slice.size().try_into().unwrap(),
120                 &mut bytes,
121                 &mut overlapped,
122             )
123         };
124 
125         if ret > 0 {
126             Ok(bytes as usize)
127         } else {
128             Err(Error::last_os_error())
129         }
130     }
131 
read_vectored_at_volatile(&self, bufs: &[VolatileSlice], offset: u64) -> Result<usize>132     fn read_vectored_at_volatile(&self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
133         if bufs.is_empty() {
134             return Ok(0);
135         }
136 
137         // Windows has ReadFileScatter, but that requires the buffers to all be the same
138         // size and aligned to a page size boundary.
139         // preadv makes some guarantees that we can't guarantee in windows, like atomicity.
140         let mut ret: usize = 0;
141         for buf in bufs.iter() {
142             match self.read_at_volatile(*buf, offset + ret as u64) {
143                 Ok(bytes) => ret += bytes,
144                 Err(_) => return Err(Error::last_os_error()),
145             }
146         }
147 
148         Ok(ret)
149     }
150 
write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> Result<usize>151     fn write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> Result<usize> {
152         // The unix implementation uses pwrite, which doesn't modify the file
153         // pointer. Windows doesn't have an option for that, unfortunately.
154 
155         let mut bytes = 0;
156 
157         // SAFETY:
158         // Safe because only bytes inside the slice are accessed and the kernel is expected
159         // to handle arbitrary memory for I/O.
160         let ret = unsafe {
161             let mut overlapped: winapi::um::minwinbase::OVERLAPPED = std::mem::zeroed();
162             overlapped.u.s_mut().Offset = offset as u32;
163             overlapped.u.s_mut().OffsetHigh = (offset >> 32) as u32;
164 
165             winapi::um::fileapi::WriteFile(
166                 self.as_raw_descriptor(),
167                 slice.as_ptr() as *mut libc::c_void,
168                 slice.size().try_into().unwrap(),
169                 &mut bytes,
170                 &mut overlapped,
171             )
172         };
173 
174         if ret > 0 {
175             Ok(bytes as usize)
176         } else {
177             Err(Error::last_os_error())
178         }
179     }
180 
write_vectored_at_volatile(&self, bufs: &[VolatileSlice], offset: u64) -> Result<usize>181     fn write_vectored_at_volatile(&self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
182         if bufs.is_empty() {
183             return Ok(0);
184         }
185 
186         // Windows has WriteFileGather, but that requires the buffers to all be the same
187         // size and aligned to a page size boundary, and only writes one page of data
188         // from each buffer.
189         // pwritev makes some guarantees that we can't guarantee in windows, like atomicity.
190         let mut ret: usize = 0;
191         for buf in bufs.iter() {
192             match self.write_at_volatile(*buf, offset + ret as u64) {
193                 Ok(bytes) => ret += bytes,
194                 Err(_) => return Err(Error::last_os_error()),
195             }
196         }
197 
198         Ok(ret)
199     }
200 }
201 
202 impl FileAllocate for File {
allocate(&self, offset: u64, len: u64) -> Result<()>203     fn allocate(&self, offset: u64, len: u64) -> Result<()> {
204         // The Windows equivalent of fallocate's default mode (allocate a zeroed block of space in a
205         // file) is to just call write_zeros_at. There are not, at the time of writing, any syscalls
206         // that will extend the file and zero the range while maintaining the disk allocation in a
207         // more efficient manner.
208         self.write_zeroes_all_at(offset, len as usize)
209     }
210 }
211