@@ -1150,22 +1150,73 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
1150
1150
}
1151
1151
1152
1152
pub fn stat ( path : & Path ) -> io:: Result < FileAttr > {
1153
- let mut opts = OpenOptions :: new ( ) ;
1154
- // No read or write permissions are necessary
1155
- opts. access_mode ( 0 ) ;
1156
- // This flag is so we can open directories too
1157
- opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
1158
- let file = File :: open ( path, & opts) ?;
1159
- file. file_attr ( )
1153
+ metadata ( path, ReparsePoint :: Follow )
1160
1154
}
1161
1155
1162
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 > {
1163
1173
let mut opts = OpenOptions :: new ( ) ;
1164
1174
// No read or write permissions are necessary
1165
1175
opts. access_mode ( 0 ) ;
1166
- opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS | c:: FILE_FLAG_OPEN_REPARSE_POINT ) ;
1167
- let file = File :: open ( path, & opts) ?;
1168
- 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
+ }
1169
1220
}
1170
1221
1171
1222
pub fn set_perm ( p : & Path , perm : FilePermissions ) -> io:: Result < ( ) > {
0 commit comments