Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/base/src/sys/windows/file_traits.rs
5394 views
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 {
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
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
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
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 {
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
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
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
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 {
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
212