@@ -155,22 +155,7 @@ impl DirEntry {
155155 }
156156
157157 pub fn metadata ( & self ) -> io:: Result < FileAttr > {
158- Ok ( FileAttr {
159- attributes : self . data . dwFileAttributes ,
160- creation_time : self . data . ftCreationTime ,
161- last_access_time : self . data . ftLastAccessTime ,
162- last_write_time : self . data . ftLastWriteTime ,
163- file_size : ( ( self . data . nFileSizeHigh as u64 ) << 32 ) | ( self . data . nFileSizeLow as u64 ) ,
164- reparse_tag : if self . data . dwFileAttributes & c:: FILE_ATTRIBUTE_REPARSE_POINT != 0 {
165- // reserved unless this is a reparse point
166- self . data . dwReserved0
167- } else {
168- 0
169- } ,
170- volume_serial_number : None ,
171- number_of_links : None ,
172- file_index : None ,
173- } )
158+ Ok ( self . data . into ( ) )
174159 }
175160}
176161
@@ -879,6 +864,26 @@ impl FileAttr {
879864 self . file_index
880865 }
881866}
867+ impl From < c:: WIN32_FIND_DATAW > for FileAttr {
868+ fn from ( wfd : c:: WIN32_FIND_DATAW ) -> Self {
869+ FileAttr {
870+ attributes : wfd. dwFileAttributes ,
871+ creation_time : wfd. ftCreationTime ,
872+ last_access_time : wfd. ftLastAccessTime ,
873+ last_write_time : wfd. ftLastWriteTime ,
874+ file_size : ( ( wfd. nFileSizeHigh as u64 ) << 32 ) | ( wfd. nFileSizeLow as u64 ) ,
875+ reparse_tag : if wfd. dwFileAttributes & c:: FILE_ATTRIBUTE_REPARSE_POINT != 0 {
876+ // reserved unless this is a reparse point
877+ wfd. dwReserved0
878+ } else {
879+ 0
880+ } ,
881+ volume_serial_number : None ,
882+ number_of_links : None ,
883+ file_index : None ,
884+ }
885+ }
886+ }
882887
883888fn to_u64 ( ft : & c:: FILETIME ) -> u64 {
884889 ( ft. dwLowDateTime as u64 ) | ( ( ft. dwHighDateTime as u64 ) << 32 )
@@ -1145,22 +1150,73 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
11451150}
11461151
11471152pub fn stat ( path : & Path ) -> io:: Result < FileAttr > {
1148- let mut opts = OpenOptions :: new ( ) ;
1149- // No read or write permissions are necessary
1150- opts. access_mode ( 0 ) ;
1151- // This flag is so we can open directories too
1152- opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
1153- let file = File :: open ( path, & opts) ?;
1154- file. file_attr ( )
1153+ metadata ( path, ReparsePoint :: Follow )
11551154}
11561155
11571156pub fn lstat ( path : & Path ) -> io:: Result < FileAttr > {
1157+ metadata ( path, ReparsePoint :: Open )
1158+ }
1159+
1160+ #[ repr( u32 ) ]
1161+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
1162+ enum ReparsePoint {
1163+ Follow = 0 ,
1164+ Open = c:: FILE_FLAG_OPEN_REPARSE_POINT ,
1165+ }
1166+ impl ReparsePoint {
1167+ fn as_flag ( self ) -> u32 {
1168+ self as u32
1169+ }
1170+ }
1171+
1172+ fn metadata ( path : & Path , reparse : ReparsePoint ) -> io:: Result < FileAttr > {
11581173 let mut opts = OpenOptions :: new ( ) ;
11591174 // No read or write permissions are necessary
11601175 opts. access_mode ( 0 ) ;
1161- opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS | c:: FILE_FLAG_OPEN_REPARSE_POINT ) ;
1162- let file = File :: open ( path, & opts) ?;
1163- file. file_attr ( )
1176+ opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS | reparse. as_flag ( ) ) ;
1177+
1178+ // Attempt to open the file normally.
1179+ // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
1180+ // If the fallback fails for any reason we return the original error.
1181+ match File :: open ( path, & opts) {
1182+ Ok ( file) => file. file_attr ( ) ,
1183+ Err ( e) if e. raw_os_error ( ) == Some ( c:: ERROR_SHARING_VIOLATION as _ ) => {
1184+ // `ERROR_SHARING_VIOLATION` will almost never be returned.
1185+ // Usually if a file is locked you can still read some metadata.
1186+ // However, there are special system files, such as
1187+ // `C:\hiberfil.sys`, that are locked in a way that denies even that.
1188+ unsafe {
1189+ let path = maybe_verbatim ( path) ?;
1190+
1191+ // `FindFirstFileW` accepts wildcard file names.
1192+ // Fortunately wildcards are not valid file names and
1193+ // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
1194+ // therefore it's safe to assume the file name given does not
1195+ // include wildcards.
1196+ let mut wfd = mem:: zeroed ( ) ;
1197+ let handle = c:: FindFirstFileW ( path. as_ptr ( ) , & mut wfd) ;
1198+
1199+ if handle == c:: INVALID_HANDLE_VALUE {
1200+ // This can fail if the user does not have read access to the
1201+ // directory.
1202+ Err ( e)
1203+ } else {
1204+ // We no longer need the find handle.
1205+ c:: FindClose ( handle) ;
1206+
1207+ // `FindFirstFileW` reads the cached file information from the
1208+ // directory. The downside is that this metadata may be outdated.
1209+ let attrs = FileAttr :: from ( wfd) ;
1210+ if reparse == ReparsePoint :: Follow && attrs. file_type ( ) . is_symlink ( ) {
1211+ Err ( e)
1212+ } else {
1213+ Ok ( attrs)
1214+ }
1215+ }
1216+ }
1217+ }
1218+ Err ( e) => Err ( e) ,
1219+ }
11641220}
11651221
11661222pub fn set_perm ( p : & Path , perm : FilePermissions ) -> io:: Result < ( ) > {
0 commit comments