@@ -589,7 +589,7 @@ extension FileManager {
589589
590590 if ffd. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
591591 let readableAttributes = ffd. dwFileAttributes & DWORD ( bitPattern: ~ FILE_ATTRIBUTE_READONLY)
592- guard file . withCString ( encodedAs: UTF16 . self, { SetFileAttributesW ( $0, readableAttributes) } ) else {
592+ guard itemPath . withCString ( encodedAs: UTF16 . self, { SetFileAttributesW ( $0, readableAttributes) } ) else {
593593 throw _NSErrorWithWindowsError ( GetLastError ( ) , reading: false , paths: [ file] )
594594 }
595595 }
@@ -684,10 +684,6 @@ extension FileManager {
684684 return true
685685 }
686686
687- internal func _compareFiles( withFileSystemRepresentation file1Rep: UnsafePointer < Int8 > , andFileSystemRepresentation file2Rep: UnsafePointer < Int8 > , size: Int64 , bufSize: Int ) -> Bool {
688- NSUnimplemented ( )
689- }
690-
691687 internal func _lstatFile( atPath path: String , withFileSystemRepresentation fsRep: UnsafePointer < Int8 > ? = nil ) throws -> stat {
692688 let _fsRep : UnsafePointer < Int8 >
693689 if fsRep == nil {
@@ -749,7 +745,86 @@ extension FileManager {
749745 }
750746
751747 internal func _contentsEqual( atPath path1: String , andPath path2: String ) -> Bool {
752- NSUnimplemented ( )
748+ guard let path1Handle = path1. withCString ( encodedAs: UTF16 . self, {
749+ CreateFileW ( $0, GENERIC_READ, DWORD ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) , nil ,
750+ DWORD ( OPEN_EXISTING) , DWORD ( FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS) , nil )
751+ } ) , path1Handle != INVALID_HANDLE_VALUE else {
752+ return false
753+ }
754+
755+ defer { CloseHandle ( path1Handle) }
756+
757+ guard let path2Handle = path2. withCString ( encodedAs: UTF16 . self, {
758+ CreateFileW ( $0, GENERIC_READ, DWORD ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) , nil ,
759+ DWORD ( OPEN_EXISTING) , DWORD ( FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS) , nil )
760+ } ) , path2Handle != INVALID_HANDLE_VALUE else {
761+ return false
762+ }
763+ defer { CloseHandle ( path2Handle) }
764+
765+ let file1Type = GetFileType ( path1Handle)
766+ guard GetLastError ( ) == NO_ERROR else {
767+ return false
768+ }
769+ let file2Type = GetFileType ( path2Handle)
770+ guard GetLastError ( ) == NO_ERROR else {
771+ return false
772+ }
773+
774+ guard file1Type == FILE_TYPE_DISK, file2Type == FILE_TYPE_DISK else {
775+ return false
776+ }
777+
778+ var path1FileInfo = BY_HANDLE_FILE_INFORMATION ( )
779+ var path2FileInfo = BY_HANDLE_FILE_INFORMATION ( )
780+ guard GetFileInformationByHandle ( path1Handle, & path1FileInfo) ,
781+ GetFileInformationByHandle ( path2Handle, & path2FileInfo) else {
782+ return false
783+ }
784+
785+ // If both paths point to the same volume/filenumber or they are both zero length
786+ // then they are considered equal
787+ if path1FileInfo. nFileIndexHigh == path2FileInfo. nFileIndexHigh
788+ && path1FileInfo. nFileIndexLow == path2FileInfo. nFileIndexLow
789+ && path1FileInfo. dwVolumeSerialNumber == path2FileInfo. dwVolumeSerialNumber {
790+ return true
791+ }
792+
793+ let path1Attrs = path1FileInfo. dwFileAttributes
794+ let path2Attrs = path2FileInfo. dwFileAttributes
795+ if path1Attrs & DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT
796+ || path2Attrs & DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT {
797+ guard path1Attrs & DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT
798+ && path2Attrs & DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT else {
799+ return false
800+ }
801+ guard let pathDest1 = try ? _destinationOfSymbolicLink ( atPath: path1) ,
802+ let pathDest2 = try ? _destinationOfSymbolicLink ( atPath: path2) else {
803+ return false
804+ }
805+ return pathDest1 == pathDest2
806+ } else if DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path1Attrs == DWORD ( FILE_ATTRIBUTE_DIRECTORY)
807+ || DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path2Attrs == DWORD ( FILE_ATTRIBUTE_DIRECTORY) {
808+ guard DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path1Attrs == DWORD ( FILE_ATTRIBUTE_DIRECTORY)
809+ && DWORD ( FILE_ATTRIBUTE_DIRECTORY) & path2Attrs == FILE_ATTRIBUTE_DIRECTORY else {
810+ return false
811+ }
812+ return _compareDirectories ( atPath: path1, andPath: path2)
813+ } else {
814+ if path1FileInfo. nFileSizeHigh == 0 && path1FileInfo. nFileSizeLow == 0
815+ && path2FileInfo. nFileSizeHigh == 0 && path2FileInfo. nFileSizeLow == 0 {
816+ return true
817+ }
818+
819+ let path1Fsr = fileSystemRepresentation ( withPath: path1)
820+ defer { path1Fsr. deallocate ( ) }
821+ let path2Fsr = fileSystemRepresentation ( withPath: path2)
822+ defer { path2Fsr. deallocate ( ) }
823+ return _compareFiles ( withFileSystemRepresentation: path1Fsr,
824+ andFileSystemRepresentation: path2Fsr,
825+ size: ( Int64 ( path1FileInfo. nFileSizeHigh) << 32 ) | Int64 ( path1FileInfo. nFileSizeLow) ,
826+ bufSize: 0x1000 )
827+ }
753828 }
754829
755830 internal func _appendSymlinkDestination( _ dest: String , toPath: String ) -> String {
@@ -810,7 +885,7 @@ extension FileManager {
810885 override func nextObject( ) -> Any ? {
811886 func firstValidItem( ) -> URL ? {
812887 while let url = _stack. popLast ( ) {
813- if !FileManager. default. fileExists ( atPath: url. path, isDirectory : nil ) {
888+ if !FileManager. default. fileExists ( atPath: url. path) {
814889 guard let handler = _errorHandler,
815890 handler ( url, _NSErrorWithWindowsError ( GetLastError ( ) , reading: true , paths: [ url. path] ) )
816891 else { return nil }
@@ -823,15 +898,16 @@ extension FileManager {
823898 }
824899
825900 // If we most recently returned a directory, decend into it
826- var isDir : ObjCBool = false
827- guard FileManager . default. fileExists ( atPath: _lastReturned. path, isDirectory: & isDir) else {
828- guard let handler = _errorHandler,
901+ guard let attrs = try ? FileManager . default. windowsFileAttributes ( atPath: _lastReturned. path) else {
902+ guard let handler = _errorHandler,
829903 handler ( _lastReturned, _NSErrorWithWindowsError ( GetLastError ( ) , reading: true , paths: [ _lastReturned. path] ) )
830- else { return nil }
831- return firstValidItem ( )
904+ else { return nil }
905+ return firstValidItem ( )
832906 }
833907
834- if isDir. boolValue && ( level == 0 || !_options. contains ( . skipsSubdirectoryDescendants) ) {
908+ let isDir = attrs. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_DIRECTORY) == DWORD ( FILE_ATTRIBUTE_DIRECTORY)
909+ && attrs. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_REPARSE_POINT) == 0
910+ if isDir && ( level == 0 || !_options. contains ( . skipsSubdirectoryDescendants) ) {
835911 var ffd = WIN32_FIND_DATAW ( )
836912 let dirPath = joinPath ( prefix: _lastReturned. path, suffix: " * " )
837913 let handle = dirPath. withCString ( encodedAs: UTF16 . self) {
0 commit comments