Skip to content

Commit 996c3cb

Browse files
committed
fix(driver): expose FileMetadata on Windows
1 parent 005259b commit 996c3cb

File tree

3 files changed

+126
-113
lines changed

3 files changed

+126
-113
lines changed

compio-driver/src/iocp/op.rs

+103-51
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use windows_sys::{
2121
Foundation::{
2222
CloseHandle, GetLastError, ERROR_ACCESS_DENIED, ERROR_HANDLE_EOF, ERROR_IO_INCOMPLETE,
2323
ERROR_IO_PENDING, ERROR_NOT_FOUND, ERROR_NO_DATA, ERROR_PIPE_CONNECTED,
24-
ERROR_SHARING_VIOLATION,
24+
ERROR_SHARING_VIOLATION, FILETIME,
2525
},
2626
Networking::WinSock::{
2727
closesocket, setsockopt, shutdown, socklen_t, WSAIoctl, WSARecv, WSARecvFrom, WSASend,
@@ -207,20 +207,85 @@ impl OpCode for CloseFile {
207207
}
208208
}
209209

210+
/// A mixture of [`BY_HANDLE_FILE_INFORMATION`], [`FILE_ATTRIBUTE_TAG_INFO`] and
211+
/// [`WIN32_FIND_DATAW`]. The field names follows Hungarian case, to make it
212+
/// look like Windows API.
213+
#[derive(Default, Clone)]
214+
#[allow(non_snake_case, missing_docs)]
215+
pub struct FileMetadata {
216+
pub dwFileAttributes: u32,
217+
pub ftCreationTime: u64,
218+
pub ftLastAccessTime: u64,
219+
pub ftLastWriteTime: u64,
220+
pub nFileSize: u64,
221+
pub dwReparseTag: u32,
222+
pub dwVolumeSerialNumber: Option<u32>,
223+
pub nNumberOfLinks: Option<u32>,
224+
pub nFileIndex: Option<u64>,
225+
}
226+
227+
impl FileMetadata {
228+
fn is_reparse_point(&self) -> bool {
229+
self.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0
230+
}
231+
}
232+
233+
const fn create_u64(high: u32, low: u32) -> u64 {
234+
((high as u64) << 32) | (low as u64)
235+
}
236+
237+
const fn filetime_u64(t: FILETIME) -> u64 {
238+
create_u64(t.dwHighDateTime, t.dwLowDateTime)
239+
}
240+
241+
impl From<BY_HANDLE_FILE_INFORMATION> for FileMetadata {
242+
fn from(value: BY_HANDLE_FILE_INFORMATION) -> Self {
243+
Self {
244+
dwFileAttributes: value.dwFileAttributes,
245+
ftCreationTime: filetime_u64(value.ftCreationTime),
246+
ftLastAccessTime: filetime_u64(value.ftLastAccessTime),
247+
ftLastWriteTime: filetime_u64(value.ftLastWriteTime),
248+
nFileSize: create_u64(value.nFileSizeHigh, value.nFileSizeLow),
249+
dwReparseTag: 0,
250+
dwVolumeSerialNumber: Some(value.dwVolumeSerialNumber),
251+
nNumberOfLinks: Some(value.nNumberOfLinks),
252+
nFileIndex: Some(create_u64(value.nFileIndexHigh, value.nFileIndexLow)),
253+
}
254+
}
255+
}
256+
257+
impl From<WIN32_FIND_DATAW> for FileMetadata {
258+
fn from(value: WIN32_FIND_DATAW) -> Self {
259+
let mut this = Self {
260+
dwFileAttributes: value.dwFileAttributes,
261+
ftCreationTime: filetime_u64(value.ftCreationTime),
262+
ftLastAccessTime: filetime_u64(value.ftLastAccessTime),
263+
ftLastWriteTime: filetime_u64(value.ftLastWriteTime),
264+
nFileSize: create_u64(value.nFileSizeHigh, value.nFileSizeLow),
265+
dwReparseTag: 0,
266+
dwVolumeSerialNumber: None,
267+
nNumberOfLinks: None,
268+
nFileIndex: None,
269+
};
270+
if this.is_reparse_point() {
271+
this.dwReparseTag = value.dwReserved0;
272+
}
273+
this
274+
}
275+
}
276+
210277
/// Get metadata of an opened file.
211278
pub struct FileStat {
212279
pub(crate) fd: RawFd,
213-
pub(crate) stat: BY_HANDLE_FILE_INFORMATION,
214-
pub(crate) reparse_tag: u32,
280+
pub(crate) stat: FileMetadata,
215281
}
216282

217283
impl FileStat {
218284
/// Create [`FileStat`].
219285
pub fn new(fd: RawFd) -> Self {
220286
Self {
221287
fd,
222-
stat: unsafe { std::mem::zeroed() },
223-
reparse_tag: 0,
288+
stat: Default::default(),
224289
}
225290
}
226291
}
@@ -231,11 +296,10 @@ impl OpCode for FileStat {
231296
}
232297

233298
unsafe fn operate(mut self: Pin<&mut Self>, _optr: *mut OVERLAPPED) -> Poll<io::Result<usize>> {
234-
syscall!(
235-
BOOL,
236-
GetFileInformationByHandle(self.fd as _, &mut self.stat)
237-
)?;
238-
if self.stat.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
299+
let mut stat = unsafe { std::mem::zeroed() };
300+
syscall!(BOOL, GetFileInformationByHandle(self.fd as _, &mut stat))?;
301+
self.stat = stat.into();
302+
if self.stat.is_reparse_point() {
239303
let mut tag: FILE_ATTRIBUTE_TAG_INFO = std::mem::zeroed();
240304
syscall!(
241305
BOOL,
@@ -246,9 +310,8 @@ impl OpCode for FileStat {
246310
std::mem::size_of::<FILE_ATTRIBUTE_TAG_INFO>() as _
247311
)
248312
)?;
249-
if tag.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
250-
self.reparse_tag = tag.ReparseTag;
251-
}
313+
debug_assert_eq!(self.stat.dwFileAttributes, tag.FileAttributes);
314+
self.stat.dwReparseTag = tag.ReparseTag;
252315
}
253316
Poll::Ready(Ok(0))
254317
}
@@ -259,20 +322,18 @@ impl OpCode for FileStat {
259322
}
260323

261324
impl IntoInner for FileStat {
262-
type Inner = (BY_HANDLE_FILE_INFORMATION, u32);
325+
type Inner = FileMetadata;
263326

264327
fn into_inner(self) -> Self::Inner {
265-
(self.stat, self.reparse_tag)
328+
self.stat
266329
}
267330
}
268331

269332
/// Get metadata from path.
270333
pub struct PathStat {
271334
pub(crate) path: U16CString,
272335
pub(crate) follow_symlink: bool,
273-
pub(crate) stat: BY_HANDLE_FILE_INFORMATION,
274-
pub(crate) reparse_tag: u32,
275-
pub(crate) handle_info: bool,
336+
pub(crate) stat: FileMetadata,
276337
}
277338

278339
impl PathStat {
@@ -281,19 +342,11 @@ impl PathStat {
281342
Self {
282343
path,
283344
follow_symlink,
284-
stat: unsafe { std::mem::zeroed() },
285-
reparse_tag: 0,
286-
handle_info: true,
345+
stat: Default::default(),
287346
}
288347
}
289-
}
290-
291-
impl OpCode for PathStat {
292-
fn is_overlapped(&self) -> bool {
293-
false
294-
}
295348

296-
unsafe fn operate(mut self: Pin<&mut Self>, optr: *mut OVERLAPPED) -> Poll<io::Result<usize>> {
349+
unsafe fn open_and_stat(&self, optr: *mut OVERLAPPED) -> io::Result<FileMetadata> {
297350
let mut flags = FILE_FLAG_BACKUP_SEMANTICS;
298351
if !self.follow_symlink {
299352
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
@@ -313,12 +366,24 @@ impl OpCode for PathStat {
313366
let handle = OwnedHandle::from_raw_handle(handle as _);
314367
let mut op = FileStat::new(handle.as_raw_handle());
315368
let op_pin = std::pin::Pin::new(&mut op);
316-
let res = match std::task::ready!(op_pin.operate(optr)) {
317-
Ok(_) => {
318-
let (stat, reparse_tag) = op.into_inner();
369+
let res = op_pin.operate(optr);
370+
if let Poll::Ready(res) = res {
371+
res.map(|_| op.into_inner())
372+
} else {
373+
unreachable!("FileStat could not return Poll::Pending")
374+
}
375+
}
376+
}
377+
378+
impl OpCode for PathStat {
379+
fn is_overlapped(&self) -> bool {
380+
false
381+
}
382+
383+
unsafe fn operate(mut self: Pin<&mut Self>, optr: *mut OVERLAPPED) -> Poll<io::Result<usize>> {
384+
let res = match self.open_and_stat(optr) {
385+
Ok(stat) => {
319386
self.stat = stat;
320-
self.handle_info = true;
321-
self.reparse_tag = reparse_tag;
322387
Ok(0)
323388
}
324389
Err(e)
@@ -331,19 +396,9 @@ impl OpCode for PathStat {
331396
let mut wfd: WIN32_FIND_DATAW = std::mem::zeroed();
332397
let handle = syscall!(HANDLE, FindFirstFileW(self.path.as_ptr(), &mut wfd))?;
333398
FindClose(handle);
334-
self.stat = BY_HANDLE_FILE_INFORMATION {
335-
dwFileAttributes: wfd.dwFileAttributes,
336-
ftCreationTime: wfd.ftCreationTime,
337-
ftLastAccessTime: wfd.ftLastAccessTime,
338-
ftLastWriteTime: wfd.ftLastWriteTime,
339-
nFileSizeHigh: wfd.nFileSizeHigh,
340-
nFileSizeLow: wfd.nFileSizeLow,
341-
..self.stat
342-
};
343-
self.handle_info = false;
344-
let is_reparse = self.stat.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0;
345-
self.reparse_tag = if is_reparse { wfd.dwReserved0 } else { 0 };
346-
let surrogate = self.reparse_tag & 0x20000000 != 0;
399+
self.stat = wfd.into();
400+
let is_reparse = self.stat.is_reparse_point();
401+
let surrogate = self.stat.dwReparseTag & 0x20000000 != 0;
347402
if self.follow_symlink && is_reparse && surrogate {
348403
Err(e)
349404
} else {
@@ -360,14 +415,11 @@ impl OpCode for PathStat {
360415
}
361416
}
362417

363-
/// Now all values of [`BY_HANDLE_FILE_INFORMATION`] is valid. If the last
364-
/// [`bool`] element is `false`, only the fields that are contained in
365-
/// [`WIN32_FIND_DATAW`] are valid.
366418
impl IntoInner for PathStat {
367-
type Inner = (BY_HANDLE_FILE_INFORMATION, u32, bool);
419+
type Inner = FileMetadata;
368420

369421
fn into_inner(self) -> Self::Inner {
370-
(self.stat, self.reparse_tag, self.handle_info)
422+
self.stat
371423
}
372424
}
373425

compio-driver/src/op.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ use std::net::Shutdown;
88
use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut, SetBufInit};
99
use socket2::SockAddr;
1010

11-
#[cfg(windows)]
12-
pub use crate::sys::op::ConnectNamedPipe;
1311
pub use crate::sys::op::{
1412
Accept, FileStat, OpenFile, PathStat, Recv, RecvFrom, RecvFromVectored, RecvVectored, Send,
1513
SendTo, SendToVectored, SendVectored,
1614
};
15+
#[cfg(windows)]
16+
pub use crate::sys::op::{ConnectNamedPipe, FileMetadata};
1717
#[cfg(unix)]
1818
pub use crate::sys::op::{ReadVectoredAt, WriteVectoredAt};
1919
use crate::sys::{sockaddr_storage, socklen_t, RawFd};

0 commit comments

Comments
 (0)