Skip to content

Commit bbc75f9

Browse files
authored
fix: windows implement of op::Write is incorrect (#294)
* fix: fix windows position read/write bug Signed-off-by: Lzzzt <[email protected]> * ci: make clippy happy Signed-off-by: lzzzt <[email protected]> --------- Signed-off-by: Lzzzt <[email protected]> Signed-off-by: lzzzt <[email protected]>
1 parent 5f33e2c commit bbc75f9

File tree

7 files changed

+99
-19
lines changed

7 files changed

+99
-19
lines changed

monoio/src/driver/op/open.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,15 @@ impl OpAble for Open {
7979

8080
#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
8181
fn legacy_call(&mut self) -> io::Result<u32> {
82+
use std::{ffi::OsString, os::windows::ffi::OsStrExt};
83+
84+
let os_str = OsString::from(self.path.to_string_lossy().into_owned());
85+
86+
// Convert OsString to wide character format (Vec<u16>).
87+
let wide_path: Vec<u16> = os_str.encode_wide().chain(Some(0)).collect();
8288
syscall!(
8389
CreateFileW(
84-
self.path.as_c_str().as_ptr().cast(),
90+
wide_path.as_ptr(),
8591
self.opts.access_mode()?,
8692
self.opts.share_mode,
8793
self.opts.security_attributes,

monoio/src/driver/op/read.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use {
1010
windows_sys::Win32::{
1111
Foundation::TRUE,
1212
Networking::WinSock::{WSAGetLastError, WSARecv, WSAESHUTDOWN},
13-
Storage::FileSystem::{ReadFile, SetFilePointer, FILE_CURRENT, INVALID_SET_FILE_POINTER},
13+
Storage::FileSystem::{ReadFile, SetFilePointer, FILE_BEGIN, INVALID_SET_FILE_POINTER},
1414
},
1515
};
1616

@@ -111,7 +111,9 @@ impl<T: IoBufMut> OpAble for Read<T> {
111111
let ret = unsafe {
112112
// see https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-setfilepointer
113113
if seek_offset != 0 {
114-
let r = SetFilePointer(fd, seek_offset, std::ptr::null_mut(), FILE_CURRENT);
114+
// We use `FILE_BEGIN` because this behavior should be the same with unix syscall
115+
// `pwrite`, which uses the offset from the begin of the file.
116+
let r = SetFilePointer(fd, seek_offset, std::ptr::null_mut(), FILE_BEGIN);
115117
if INVALID_SET_FILE_POINTER == r {
116118
return Err(io::Error::last_os_error());
117119
}

monoio/src/driver/op/write.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use io_uring::{opcode, types};
88
use windows_sys::Win32::{
99
Foundation::TRUE,
1010
Networking::WinSock::WSASend,
11-
Storage::FileSystem::{SetFilePointer, WriteFile, FILE_CURRENT, INVALID_SET_FILE_POINTER},
11+
Storage::FileSystem::{SetFilePointer, WriteFile, FILE_BEGIN, INVALID_SET_FILE_POINTER},
1212
};
1313

1414
use super::{super::shared_fd::SharedFd, Op, OpAble};
@@ -95,7 +95,9 @@ impl<T: IoBuf> OpAble for Write<T> {
9595
let ret = unsafe {
9696
// see https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-setfilepointer
9797
if seek_offset != 0 {
98-
let r = SetFilePointer(fd, seek_offset, std::ptr::null_mut(), FILE_CURRENT);
98+
// We use `FILE_BEGIN` because this behavior should be the same with unix syscall
99+
// `pwrite`, which uses the offset from the begin of the file.
100+
let r = SetFilePointer(fd, seek_offset, std::ptr::null_mut(), FILE_BEGIN);
99101
if INVALID_SET_FILE_POINTER == r {
100102
return Err(io::Error::last_os_error());
101103
}

monoio/src/fs/mod.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub use file_type::FileType;
3030

3131
#[cfg(unix)]
3232
mod permissions;
33+
#[cfg(windows)]
34+
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
35+
3336
#[cfg(unix)]
3437
pub use permissions::Permissions;
3538

@@ -38,16 +41,20 @@ use crate::buf::IoBuf;
3841
use crate::driver::op::Op;
3942

4043
/// Read the entire contents of a file into a bytes vector.
41-
#[cfg(unix)]
4244
pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
43-
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd};
44-
4545
use crate::buf::IoBufMut;
4646

4747
let file = File::open(path).await?;
48-
let sys_file = unsafe { std::fs::File::from_raw_fd(file.as_raw_fd()) };
48+
49+
#[cfg(windows)]
50+
let sys_file = unsafe { std::fs::File::from_raw_handle(file.as_raw_handle()) };
51+
#[cfg(windows)]
4952
let size = sys_file.metadata()?.len() as usize;
50-
let _ = sys_file.into_raw_fd();
53+
#[cfg(windows)]
54+
let _ = sys_file.into_raw_handle();
55+
56+
#[cfg(unix)]
57+
let size = file.metadata().await?.len() as usize;
5158

5259
let (res, buf) = file
5360
.read_exact_at(Vec::with_capacity(size).slice_mut(0..size), 0)

monoio/src/time/interval.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ use crate::{
99
time::{sleep_until, Duration, Instant, Sleep},
1010
};
1111

12-
/// Creates new [`Interval`] that yields with interval of `period`. The first
13-
/// tick completes immediately. The default [`MissedTickBehavior`] is
12+
/// Creates new [`Interval`] that yields with interval of `period`.
13+
///
14+
/// The first tick completes immediately. The default [`MissedTickBehavior`] is
1415
/// [`Burst`](MissedTickBehavior::Burst), but this can be configured
1516
/// by calling [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
1617
///
@@ -79,9 +80,11 @@ pub fn interval(period: Duration) -> Interval {
7980
}
8081

8182
/// Creates new [`Interval`] that yields with interval of `period` with the
82-
/// first tick completing at `start`. The default [`MissedTickBehavior`] is
83-
/// [`Burst`](MissedTickBehavior::Burst), but this can be configured
84-
/// by calling [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
83+
/// first tick completing at `start`.
84+
///
85+
/// The default [`MissedTickBehavior`] is [`Burst`](MissedTickBehavior::Burst),
86+
/// but this can be configured by calling
87+
/// [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
8588
///
8689
/// An interval will tick indefinitely. At any time, the [`Interval`] value can
8790
/// be dropped. This cancels the interval.

monoio/tests/fs_file.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// todo fix these CI in windows
2-
#![cfg(not(windows))]
3-
use std::io::prelude::*;
1+
use std::io::{prelude::*, SeekFrom};
42
#[cfg(unix)]
53
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
64
#[cfg(windows)]
@@ -55,7 +53,7 @@ async fn basic_write() {
5553
file.write_at(HELLO, 0).await.0.unwrap();
5654
file.sync_all().await.unwrap();
5755

58-
let file = std::fs::read(tempfile.path()).unwrap();
56+
let file = monoio::fs::read(tempfile.path()).await.unwrap();
5957
assert_eq!(file, HELLO);
6058
}
6159

@@ -167,6 +165,7 @@ async fn poll_once(future: impl std::future::Future) {
167165
.await;
168166
}
169167

168+
#[allow(unused, clippy::needless_return)]
170169
fn assert_invalid_fd(fd: RawFd, base: std::fs::Metadata) {
171170
use std::fs::File;
172171
#[cfg(unix)]
@@ -194,6 +193,7 @@ fn assert_invalid_fd(fd: RawFd, base: std::fs::Metadata) {
194193
}
195194
}
196195

196+
#[cfg(unix)]
197197
#[monoio::test_all]
198198
async fn file_from_std() {
199199
let tempfile = tempfile();
@@ -208,3 +208,44 @@ async fn file_from_std() {
208208
file.sync_all().await.unwrap();
209209
read_hello(&file).await;
210210
}
211+
212+
#[monoio::test_all]
213+
async fn position_read() {
214+
let mut tempfile = tempfile();
215+
tempfile.write_all(HELLO).unwrap();
216+
tempfile.as_file_mut().sync_data().unwrap();
217+
218+
let file = File::open(tempfile.path()).await.unwrap();
219+
220+
// Modify the file pointer.
221+
let mut std_file = std::fs::File::open(tempfile.path()).unwrap();
222+
std_file.seek(SeekFrom::Start(8)).unwrap();
223+
224+
let buf = Vec::with_capacity(1024);
225+
let (res, buf) = file.read_at(buf, 4).await;
226+
let n = res.unwrap();
227+
228+
assert!(n > 0 && n <= HELLO.len() - 4);
229+
assert_eq!(&buf, &HELLO[4..4 + n]);
230+
}
231+
232+
#[monoio::test_all]
233+
async fn position_write() {
234+
let tempfile = tempfile();
235+
236+
let file = File::create(tempfile.path()).await.unwrap();
237+
file.write_at(HELLO, 0).await.0.unwrap();
238+
file.sync_all().await.unwrap();
239+
240+
// Modify the file pointer.
241+
let mut std_file = std::fs::File::open(tempfile.path()).unwrap();
242+
std_file.seek(SeekFrom::Start(8)).unwrap();
243+
244+
file.write_at(b"monoio...", 6).await.0.unwrap();
245+
file.sync_all().await.unwrap();
246+
247+
assert_eq!(
248+
monoio::fs::read(tempfile.path()).await.unwrap(),
249+
b"hello monoio..."
250+
)
251+
}

monoio/tests/fs_read.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use tempfile::NamedTempFile;
2+
3+
const HELLO: &[u8] = b"hello world...";
4+
5+
fn tempfile() -> NamedTempFile {
6+
NamedTempFile::new().expect("unable to create tempfile")
7+
}
8+
9+
#[monoio::test_all]
10+
async fn read_file_all() {
11+
use std::io::Write;
12+
13+
let mut tempfile = tempfile();
14+
tempfile.write_all(HELLO).unwrap();
15+
tempfile.as_file_mut().sync_data().unwrap();
16+
17+
let res = monoio::fs::read(tempfile.path()).await.unwrap();
18+
assert_eq!(res, HELLO);
19+
}

0 commit comments

Comments
 (0)