@@ -1150,22 +1150,73 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
11501150}
11511151
11521152pub 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 )
11601154}
11611155
11621156pub 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 > {
11631173 let mut opts = OpenOptions :: new ( ) ;
11641174 // No read or write permissions are necessary
11651175 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+ }
11691220}
11701221
11711222pub fn set_perm ( p : & Path , perm : FilePermissions ) -> io:: Result < ( ) > {
0 commit comments