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