@@ -155,22 +155,7 @@ impl DirEntry {
155
155
}
156
156
157
157
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 ( ) )
174
159
}
175
160
}
176
161
@@ -879,6 +864,26 @@ impl FileAttr {
879
864
self . file_index
880
865
}
881
866
}
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
+ }
882
887
883
888
fn to_u64 ( ft : & c:: FILETIME ) -> u64 {
884
889
( ft. dwLowDateTime as u64 ) | ( ( ft. dwHighDateTime as u64 ) << 32 )
@@ -1145,22 +1150,73 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
1145
1150
}
1146
1151
1147
1152
pub 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 )
1155
1154
}
1156
1155
1157
1156
pub 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 > {
1158
1173
let mut opts = OpenOptions :: new ( ) ;
1159
1174
// No read or write permissions are necessary
1160
1175
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
+ }
1164
1220
}
1165
1221
1166
1222
pub fn set_perm ( p : & Path , perm : FilePermissions ) -> io:: Result < ( ) > {
0 commit comments