Skip to content

Commit a62b609

Browse files
committedMar 19, 2019
Implement _lstatFile on Windows
Windows doesn't have lstat, but it still can be implemented. This should help remove some of the Windows specific codepaths.

File tree

1 file changed

+49
-5
lines changed

1 file changed

+49
-5
lines changed
 

‎Foundation/FileManager.swift

+49-5
Original file line numberDiff line numberDiff line change
@@ -1804,7 +1804,6 @@ open class FileManager : NSObject {
18041804
return path1entries.isEmpty
18051805
}
18061806

1807-
#if !os(Windows)
18081807
private func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer<Int8>? = nil) throws -> stat {
18091808
let _fsRep: UnsafePointer<Int8>
18101809
if fsRep == nil {
@@ -1818,19 +1817,64 @@ open class FileManager : NSObject {
18181817
}
18191818

18201819
var statInfo = stat()
1820+
#if os(Windows)
1821+
let h = path.withCString(encodedAs: UTF16.self) {
1822+
CreateFileW(/*lpFileName=*/$0,
1823+
/*dwDesiredAccess=*/DWORD(0),
1824+
/*dwShareMode=*/DWORD(FILE_SHARE_READ),
1825+
/*lpSecurityAttributes=*/nil,
1826+
/*dwCreationDisposition=*/DWORD(OPEN_EXISTING),
1827+
/*dwFlagsAndAttributes=*/DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
1828+
/*hTemplateFile=*/nil)
1829+
}
1830+
if h == INVALID_HANDLE_VALUE {
1831+
throw _NSErrorWithWindowsError(GetLastError(), reading: false)
1832+
}
1833+
var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
1834+
GetFileInformationByHandle(h, &info)
1835+
// Group id is always 0 on Windows
1836+
statInfo.st_gid = 0
1837+
statInfo.st_atime = info.ftLastAccessTime.time_t
1838+
statInfo.st_ctime = info.ftCreationTime.time_t
1839+
statInfo.st_dev = info.dwVolumeSerialNumber
1840+
// inodes have meaning on FAT/HPFS/NTFS
1841+
statInfo.st_ino = 0
1842+
statInfo.st_rdev = info.dwVolumeSerialNumber
1843+
1844+
let isReparsePoint = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) != 0
1845+
let isDir = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) != 0
1846+
let fileMode = isDir ? _S_IFDIR : _S_IFREG
1847+
// On a symlink to a directory, Windows sets both the REPARSE_POINT and
1848+
// DIRECTORY attributes. Since Windows doesn't provide S_IFLNK and we
1849+
// want unix style "symlinks to directories are not directories
1850+
// themselves, we say symlinks are regular files
1851+
statInfo.st_mode = UInt16(isReparsePoint ? _S_IFREG : fileMode)
1852+
let isReadOnly = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) != 0
1853+
statInfo.st_mode |= UInt16(isReadOnly ? _S_IREAD : (_S_IREAD | _S_IWRITE))
1854+
statInfo.st_mode |= UInt16(_S_IEXEC)
1855+
1856+
statInfo.st_mtime = info.ftLastWriteTime.time_t
1857+
statInfo.st_nlink = Int16(info.nNumberOfLinks)
1858+
if info.nFileSizeHigh != 0 {
1859+
throw _NSErrorWithErrno(EOVERFLOW, reading: true, path: path)
1860+
}
1861+
statInfo.st_size = Int32(info.nFileSizeLow)
1862+
// Uid is always 0 on Windows systems
1863+
statInfo.st_uid = 0
1864+
CloseHandle(h)
1865+
#else
18211866
guard lstat(_fsRep, &statInfo) == 0 else {
18221867
throw _NSErrorWithErrno(errno, reading: true, path: path)
18231868
}
1869+
#endif
18241870
return statInfo
18251871
}
1826-
#endif
18271872

1828-
@available(Windows, deprecated, message: "Not Yet Implemented")
18291873
internal func _permissionsOfItem(atPath path: String) throws -> Int {
1874+
let fileInfo = try _lstatFile(atPath: path)
18301875
#if os(Windows)
1831-
NSUnimplemented()
1876+
return Int(fileInfo.st_mode & ~UInt16(ucrt.S_IFMT))
18321877
#else
1833-
let fileInfo = try _lstatFile(atPath: path)
18341878
return Int(fileInfo.st_mode & ~S_IFMT)
18351879
#endif
18361880
}

0 commit comments

Comments
 (0)
Please sign in to comment.