Path: blob/main/base/src/sys/windows/read_write_wrappers.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::io;56use win_util::fail_if_zero;7use winapi::shared::minwindef::DWORD;8use winapi::shared::minwindef::LPCVOID;9use winapi::shared::minwindef::LPVOID;10use winapi::shared::minwindef::TRUE;11use winapi::shared::winerror::ERROR_IO_PENDING;12use winapi::um::fileapi::ReadFile;13use winapi::um::fileapi::WriteFile;14use winapi::um::ioapiset::GetOverlappedResult;15use winapi::um::minwinbase::OVERLAPPED;1617use crate::AsRawDescriptor;18use crate::Event;19use crate::RawDescriptor;2021/// # Safety22/// 1. buf points to memory that will not be freed until the write operation completes.23/// 2. buf points to at least buf_len bytes.24pub unsafe fn write_file(25handle: &dyn AsRawDescriptor,26buf: *const u8,27buf_len: usize,28overlapped: Option<&mut OVERLAPPED>,29) -> io::Result<usize> {30let is_overlapped = overlapped.is_some();3132// Safe because buf points to a valid region of memory whose size we have computed,33// pipe has not been closed (as it's managed by this object), and we check the return34// value for any errors35let mut bytes_written: DWORD = 0;36let success_flag = WriteFile(37handle.as_raw_descriptor(),38buf as LPCVOID,39buf_len40.try_into()41.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?,42match overlapped {43Some(_) => std::ptr::null_mut(),44None => &mut bytes_written,45},46match overlapped {47Some(v) => v,48None => std::ptr::null_mut(),49},50);5152if success_flag == 0 {53let err = io::Error::last_os_error();54if Some(ERROR_IO_PENDING as i32) == err.raw_os_error() && is_overlapped {55Ok(0)56} else {57Err(err)58}59} else {60Ok(bytes_written as usize)61}62}6364/// # Safety65/// 1. buf points to memory that will not be freed until the read operation completes.66/// 2. buf points to at least buf_len bytes.67pub unsafe fn read_file(68handle: &dyn AsRawDescriptor,69buf: *mut u8,70buf_len: usize,71overlapped: Option<&mut OVERLAPPED>,72) -> io::Result<usize> {73// Used to verify if ERROR_IO_PENDING should be an error.74let is_overlapped = overlapped.is_some();7576// Safe because we cap the size of the read to the size of the buffer77// and check the return code78let mut bytes_read: DWORD = 0;79let success_flag = ReadFile(80handle.as_raw_descriptor(),81buf as LPVOID,82buf_len as DWORD,83match overlapped {84Some(_) => std::ptr::null_mut(),85None => &mut bytes_read,86},87match overlapped {88Some(v) => v,89None => std::ptr::null_mut(),90},91);9293if success_flag == 0 {94let e = io::Error::last_os_error();95match e.raw_os_error() {96// ERROR_IO_PENDING, according the to docs, isn't really an error. This just means97// that the ReadFile operation hasn't completed. In this case,98// `get_overlapped_result` will wait until the operation is completed.99Some(error_code) if error_code == ERROR_IO_PENDING as i32 && is_overlapped => Ok(0),100_ => Err(e),101}102} else {103Ok(bytes_read as usize)104}105}106107fn set_overlapped_offset(overlapped: &mut OVERLAPPED, offset: u64) {108// # Safety: Safe because overlapped is allocated, and we are manipulating non-overlapping109// fields.110unsafe {111overlapped.u.s_mut().Offset = (offset & 0xffffffff) as DWORD;112overlapped.u.s_mut().OffsetHigh = (offset >> 32) as DWORD;113}114}115116// Creates a new `OVERLAPPED` struct with given, if any, offset and event117pub fn create_overlapped(offset: Option<u64>, event: Option<RawDescriptor>) -> OVERLAPPED {118let mut overlapped = OVERLAPPED::default();119if let Some(offset) = offset {120set_overlapped_offset(&mut overlapped, offset);121}122if let Some(event) = event {123overlapped.hEvent = event;124}125overlapped126}127128/// Reads buf from given handle from offset in a blocking mode.129/// handle is expected to be opened in overlapped mode.130pub fn read_overlapped_blocking(131handle: &dyn AsRawDescriptor,132offset: u64,133buf: &mut [u8],134) -> io::Result<usize> {135let mut size_transferred = 0;136let event = Event::new()?;137let mut overlapped = create_overlapped(Some(offset), Some(event.as_raw_descriptor()));138139// Safety: Safe because we check return values after the calls.140unsafe {141let _ = read_file(handle, buf.as_mut_ptr(), buf.len(), Some(&mut overlapped))?;142fail_if_zero!(GetOverlappedResult(143handle.as_raw_descriptor(),144&mut overlapped,145&mut size_transferred,146TRUE,147));148}149Ok(size_transferred as usize)150}151152#[cfg(test)]153mod tests {154use std::fs::File;155use std::fs::OpenOptions;156use std::os::windows::fs::OpenOptionsExt;157use std::path::PathBuf;158159use tempfile::TempDir;160use winapi::um::winbase::FILE_FLAG_OVERLAPPED;161162use super::*;163fn tempfile_path() -> (PathBuf, TempDir) {164let dir = tempfile::TempDir::new().unwrap();165let mut file_path = PathBuf::from(dir.path());166file_path.push("test");167(file_path, dir)168}169170fn open_overlapped(path: &PathBuf) -> File {171OpenOptions::new()172.read(true)173.write(true)174.custom_flags(FILE_FLAG_OVERLAPPED)175.open(path)176.unwrap()177}178179#[test]180fn test_read_overlapped() {181let (file_path, _tmpdir) = tempfile_path();182let data: [u8; 6] = [0, 1, 2, 3, 5, 6];183std::fs::write(&file_path, data).unwrap();184185let of = open_overlapped(&file_path);186let mut buf: [u8; 3] = [0; 3];187read_overlapped_blocking(&of, 3, &mut buf).unwrap();188assert_eq!(buf, data[3..6]);189}190}191192193