@@ -3,6 +3,7 @@ use std::sync::OnceLock;
3
3
use std:: {
4
4
io,
5
5
net:: Shutdown ,
6
+ os:: windows:: io:: { AsRawHandle , FromRawHandle , OwnedHandle } ,
6
7
pin:: Pin ,
7
8
ptr:: { null, null_mut} ,
8
9
task:: Poll ,
@@ -18,8 +19,9 @@ use windows_sys::{
18
19
core:: GUID ,
19
20
Win32 :: {
20
21
Foundation :: {
21
- CloseHandle , GetLastError , ERROR_HANDLE_EOF , ERROR_IO_INCOMPLETE , ERROR_IO_PENDING ,
22
- ERROR_NOT_FOUND , ERROR_NO_DATA , ERROR_PIPE_CONNECTED ,
22
+ CloseHandle , GetLastError , ERROR_ACCESS_DENIED , ERROR_HANDLE_EOF , ERROR_IO_INCOMPLETE ,
23
+ ERROR_IO_PENDING , ERROR_NOT_FOUND , ERROR_NO_DATA , ERROR_PIPE_CONNECTED ,
24
+ ERROR_SHARING_VIOLATION , FILETIME ,
23
25
} ,
24
26
Networking :: WinSock :: {
25
27
closesocket, setsockopt, shutdown, socklen_t, WSAIoctl , WSARecv , WSARecvFrom , WSASend ,
@@ -30,8 +32,12 @@ use windows_sys::{
30
32
} ,
31
33
Security :: SECURITY_ATTRIBUTES ,
32
34
Storage :: FileSystem :: {
33
- CreateFileW , FlushFileBuffers , ReadFile , WriteFile , FILE_CREATION_DISPOSITION ,
34
- FILE_FLAGS_AND_ATTRIBUTES , FILE_SHARE_MODE ,
35
+ CreateFileW , FileAttributeTagInfo , FindClose , FindFirstFileW , FlushFileBuffers ,
36
+ GetFileInformationByHandle , GetFileInformationByHandleEx , ReadFile , WriteFile ,
37
+ BY_HANDLE_FILE_INFORMATION , FILE_ATTRIBUTE_REPARSE_POINT , FILE_ATTRIBUTE_TAG_INFO ,
38
+ FILE_CREATION_DISPOSITION , FILE_FLAGS_AND_ATTRIBUTES , FILE_FLAG_BACKUP_SEMANTICS ,
39
+ FILE_FLAG_OPEN_REPARSE_POINT , FILE_SHARE_DELETE , FILE_SHARE_MODE , FILE_SHARE_READ ,
40
+ FILE_SHARE_WRITE , OPEN_EXISTING , WIN32_FIND_DATAW ,
35
41
} ,
36
42
System :: {
37
43
Pipes :: ConnectNamedPipe ,
@@ -201,6 +207,222 @@ impl OpCode for CloseFile {
201
207
}
202
208
}
203
209
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
+
277
+ /// Get metadata of an opened file.
278
+ pub struct FileStat {
279
+ pub ( crate ) fd : RawFd ,
280
+ pub ( crate ) stat : FileMetadata ,
281
+ }
282
+
283
+ impl FileStat {
284
+ /// Create [`FileStat`].
285
+ pub fn new ( fd : RawFd ) -> Self {
286
+ Self {
287
+ fd,
288
+ stat : Default :: default ( ) ,
289
+ }
290
+ }
291
+ }
292
+
293
+ impl OpCode for FileStat {
294
+ fn is_overlapped ( & self ) -> bool {
295
+ false
296
+ }
297
+
298
+ unsafe fn operate ( mut self : Pin < & mut Self > , _optr : * mut OVERLAPPED ) -> Poll < io:: Result < usize > > {
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 ( ) {
303
+ let mut tag: FILE_ATTRIBUTE_TAG_INFO = std:: mem:: zeroed ( ) ;
304
+ syscall ! (
305
+ BOOL ,
306
+ GetFileInformationByHandleEx (
307
+ self . fd as _,
308
+ FileAttributeTagInfo ,
309
+ & mut tag as * mut _ as _,
310
+ std:: mem:: size_of:: <FILE_ATTRIBUTE_TAG_INFO >( ) as _
311
+ )
312
+ ) ?;
313
+ debug_assert_eq ! ( self . stat. dwFileAttributes, tag. FileAttributes ) ;
314
+ self . stat . dwReparseTag = tag. ReparseTag ;
315
+ }
316
+ Poll :: Ready ( Ok ( 0 ) )
317
+ }
318
+
319
+ unsafe fn cancel ( self : Pin < & mut Self > , _optr : * mut OVERLAPPED ) -> io:: Result < ( ) > {
320
+ Ok ( ( ) )
321
+ }
322
+ }
323
+
324
+ impl IntoInner for FileStat {
325
+ type Inner = FileMetadata ;
326
+
327
+ fn into_inner ( self ) -> Self :: Inner {
328
+ self . stat
329
+ }
330
+ }
331
+
332
+ /// Get metadata from path.
333
+ pub struct PathStat {
334
+ pub ( crate ) path : U16CString ,
335
+ pub ( crate ) follow_symlink : bool ,
336
+ pub ( crate ) stat : FileMetadata ,
337
+ }
338
+
339
+ impl PathStat {
340
+ /// Create [`PathStat`].
341
+ pub fn new ( path : U16CString , follow_symlink : bool ) -> Self {
342
+ Self {
343
+ path,
344
+ follow_symlink,
345
+ stat : Default :: default ( ) ,
346
+ }
347
+ }
348
+
349
+ unsafe fn open_and_stat ( & self , optr : * mut OVERLAPPED ) -> io:: Result < FileMetadata > {
350
+ let mut flags = FILE_FLAG_BACKUP_SEMANTICS ;
351
+ if !self . follow_symlink {
352
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT ;
353
+ }
354
+ let handle = syscall ! (
355
+ HANDLE ,
356
+ CreateFileW (
357
+ self . path. as_ptr( ) ,
358
+ 0 ,
359
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
360
+ null( ) ,
361
+ OPEN_EXISTING ,
362
+ flags,
363
+ 0
364
+ )
365
+ ) ?;
366
+ let handle = OwnedHandle :: from_raw_handle ( handle as _ ) ;
367
+ let mut op = FileStat :: new ( handle. as_raw_handle ( ) ) ;
368
+ let op_pin = std:: pin:: Pin :: new ( & mut op) ;
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) => {
386
+ self . stat = stat;
387
+ Ok ( 0 )
388
+ }
389
+ Err ( e)
390
+ if [
391
+ Some ( ERROR_SHARING_VIOLATION as _ ) ,
392
+ Some ( ERROR_ACCESS_DENIED as _ ) ,
393
+ ]
394
+ . contains ( & e. raw_os_error ( ) ) =>
395
+ {
396
+ let mut wfd: WIN32_FIND_DATAW = std:: mem:: zeroed ( ) ;
397
+ let handle = syscall ! ( HANDLE , FindFirstFileW ( self . path. as_ptr( ) , & mut wfd) ) ?;
398
+ FindClose ( handle) ;
399
+ self . stat = wfd. into ( ) ;
400
+ let is_reparse = self . stat . is_reparse_point ( ) ;
401
+ let surrogate = self . stat . dwReparseTag & 0x20000000 != 0 ;
402
+ if self . follow_symlink && is_reparse && surrogate {
403
+ Err ( e)
404
+ } else {
405
+ Ok ( 0 )
406
+ }
407
+ }
408
+ Err ( e) => Err ( e) ,
409
+ } ;
410
+ Poll :: Ready ( res)
411
+ }
412
+
413
+ unsafe fn cancel ( self : Pin < & mut Self > , _optr : * mut OVERLAPPED ) -> io:: Result < ( ) > {
414
+ Ok ( ( ) )
415
+ }
416
+ }
417
+
418
+ impl IntoInner for PathStat {
419
+ type Inner = FileMetadata ;
420
+
421
+ fn into_inner ( self ) -> Self :: Inner {
422
+ self . stat
423
+ }
424
+ }
425
+
204
426
impl < T : IoBufMut > OpCode for ReadAt < T > {
205
427
unsafe fn operate ( mut self : Pin < & mut Self > , optr : * mut OVERLAPPED ) -> Poll < io:: Result < usize > > {
206
428
if let Some ( overlapped) = optr. as_mut ( ) {
0 commit comments