@@ -496,7 +496,7 @@ extension FileManager {
496
496
497
497
if ffd. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
498
498
let readableAttributes = ffd. dwFileAttributes & DWORD ( bitPattern: ~ FILE_ATTRIBUTE_READONLY)
499
- guard file . withCString ( encodedAs: UTF16 . self, { SetFileAttributesW ( $0, readableAttributes) } ) else {
499
+ guard itemPath . withCString ( encodedAs: UTF16 . self, { SetFileAttributesW ( $0, readableAttributes) } ) else {
500
500
throw _NSErrorWithWindowsError ( GetLastError ( ) , reading: false , paths: [ file] )
501
501
}
502
502
}
@@ -583,10 +583,6 @@ extension FileManager {
583
583
return true
584
584
}
585
585
586
- internal func _compareFiles( withFileSystemRepresentation file1Rep: UnsafePointer < Int8 > , andFileSystemRepresentation file2Rep: UnsafePointer < Int8 > , size: Int64 , bufSize: Int ) -> Bool {
587
- NSUnimplemented ( )
588
- }
589
-
590
586
internal func _lstatFile( atPath path: String , withFileSystemRepresentation fsRep: UnsafePointer < Int8 > ? = nil ) throws -> stat {
591
587
let _fsRep : UnsafePointer < Int8 >
592
588
if fsRep == nil {
@@ -648,7 +644,95 @@ extension FileManager {
648
644
}
649
645
650
646
internal func _contentsEqual( atPath path1: String , andPath path2: String ) -> Bool {
651
- NSUnimplemented ( )
647
+ guard let path1Handle = path1. withCString ( encodedAs: UTF16 . self, {
648
+ CreateFileW ( $0, GENERIC_READ, DWORD ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) , nil ,
649
+ DWORD ( OPEN_EXISTING) , DWORD ( FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS) , nil )
650
+ } ) else {
651
+ return false
652
+ }
653
+ defer { CloseHandle ( path1Handle) }
654
+
655
+ guard let path2Handle = path2. withCString ( encodedAs: UTF16 . self, {
656
+ CreateFileW ( $0, GENERIC_READ, DWORD ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) , nil ,
657
+ DWORD ( OPEN_EXISTING) , DWORD ( FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS) , nil )
658
+ } ) else {
659
+ return false
660
+ }
661
+ defer { CloseHandle ( path2Handle) }
662
+
663
+ guard path1Handle != INVALID_HANDLE_VALUE, path2Handle != INVALID_HANDLE_VALUE else {
664
+ return false
665
+ }
666
+
667
+ let file1Type = GetFileType ( path1Handle)
668
+ guard GetLastError ( ) == NO_ERROR else {
669
+ return false
670
+ }
671
+ let file2Type = GetFileType ( path2Handle)
672
+ guard GetLastError ( ) == NO_ERROR else {
673
+ return false
674
+ }
675
+
676
+ guard file1Type == file2Type, file1Type == FILE_TYPE_DISK else {
677
+ return false
678
+ }
679
+
680
+ var path1FileInfo = BY_HANDLE_FILE_INFORMATION ( )
681
+ var path2FileInfo = BY_HANDLE_FILE_INFORMATION ( )
682
+ guard GetFileInformationByHandle ( path1Handle, & path1FileInfo) ,
683
+ GetFileInformationByHandle ( path2Handle, & path2FileInfo) else {
684
+ return false
685
+ }
686
+
687
+ // If both paths point to the same volume/filenumber or they are both zero length
688
+ // then they are considered equal
689
+ let sameFile = path1FileInfo. nFileIndexHigh == path2FileInfo. nFileIndexHigh
690
+ && path1FileInfo. nFileIndexLow == path2FileInfo. nFileIndexLow
691
+ && path1FileInfo. dwVolumeSerialNumber == path2FileInfo. dwVolumeSerialNumber
692
+
693
+ if sameFile {
694
+ return true
695
+ }
696
+
697
+ let path1Attrs = path1FileInfo. dwFileAttributes
698
+ let path2Attrs = path2FileInfo. dwFileAttributes
699
+ if DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) & path1Attrs == DWORD ( FILE_ATTRIBUTE_REPARSE_POINT)
700
+ || DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) & path2Attrs == FILE_ATTRIBUTE_REPARSE_POINT {
701
+ guard DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) & path1Attrs == DWORD ( FILE_ATTRIBUTE_REPARSE_POINT)
702
+ && DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) & path2Attrs == FILE_ATTRIBUTE_REPARSE_POINT else {
703
+ return false
704
+ }
705
+ guard let pathDest1 = try ? _destinationOfSymbolicLink ( atPath: path1) ,
706
+ let pathDest2 = try ? _destinationOfSymbolicLink ( atPath: path2) else {
707
+ return false
708
+ }
709
+ return pathDest1 == pathDest2
710
+ } else if DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path1Attrs == DWORD ( FILE_ATTRIBUTE_DIRECTORY)
711
+ || DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path2Attrs == DWORD ( FILE_ATTRIBUTE_DIRECTORY) {
712
+ guard DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path1Attrs == DWORD ( FILE_ATTRIBUTE_DIRECTORY)
713
+ && DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path2Attrs == FILE_ATTRIBUTE_DIRECTORY else {
714
+ return false
715
+ }
716
+ return _compareDirectories ( atPath: path1, andPath: path2)
717
+ } else {
718
+ let bothEmpty = path1FileInfo. nFileSizeHigh == 0
719
+ && path1FileInfo. nFileSizeLow == 0
720
+ && path2FileInfo. nFileSizeHigh == 0
721
+ && path2FileInfo. nFileSizeLow == 0
722
+
723
+ if bothEmpty {
724
+ return true
725
+ }
726
+
727
+ let path1Fsr = fileSystemRepresentation ( withPath: path1)
728
+ defer { path1Fsr. deallocate ( ) }
729
+ let path2Fsr = fileSystemRepresentation ( withPath: path2)
730
+ defer { path2Fsr. deallocate ( ) }
731
+ return _compareFiles ( withFileSystemRepresentation: path1Fsr,
732
+ andFileSystemRepresentation: path2Fsr,
733
+ size: ( Int64 ( path1FileInfo. nFileSizeHigh) << 32 ) | Int64 ( path1FileInfo. nFileSizeLow) ,
734
+ bufSize: 0x1000 )
735
+ }
652
736
}
653
737
654
738
internal func _appendSymlinkDestination( _ dest: String , toPath: String ) -> String {
@@ -709,7 +793,7 @@ extension FileManager {
709
793
override func nextObject( ) -> Any ? {
710
794
func firstValidItem( ) -> URL ? {
711
795
while let url = _stack. popLast ( ) {
712
- if !FileManager. default. fileExists ( atPath: url. path, isDirectory : nil ) {
796
+ if !FileManager. default. fileExists ( atPath: url. path) {
713
797
guard let handler = _errorHandler,
714
798
handler ( url, _NSErrorWithWindowsError ( GetLastError ( ) , reading: true , paths: [ url. path] ) )
715
799
else { return nil }
@@ -722,15 +806,16 @@ extension FileManager {
722
806
}
723
807
724
808
// If we most recently returned a directory, decend into it
725
- var isDir : ObjCBool = false
726
- guard FileManager . default. fileExists ( atPath: _lastReturned. path, isDirectory: & isDir) else {
727
- guard let handler = _errorHandler,
809
+ guard let attrs = try ? FileManager . default. windowsFileAttributes ( atPath: _lastReturned. path) else {
810
+ guard let handler = _errorHandler,
728
811
handler ( _lastReturned, _NSErrorWithWindowsError ( GetLastError ( ) , reading: true , paths: [ _lastReturned. path] ) )
729
- else { return nil }
730
- return firstValidItem ( )
812
+ else { return nil }
813
+ return firstValidItem ( )
731
814
}
732
815
733
- if isDir. boolValue && ( level == 0 || !_options. contains ( . skipsSubdirectoryDescendants) ) {
816
+ let isDir = attrs. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_DIRECTORY) == DWORD ( FILE_ATTRIBUTE_DIRECTORY)
817
+ && attrs. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) == 0
818
+ if isDir && ( level == 0 || !_options. contains ( . skipsSubdirectoryDescendants) ) {
734
819
var ffd = WIN32_FIND_DATAW ( )
735
820
let dirPath = joinPath ( prefix: _lastReturned. path, suffix: " * " )
736
821
let handle = dirPath. withCString ( encodedAs: UTF16 . self) {
0 commit comments