Skip to content

Commit f419d5a

Browse files
committed
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.
1 parent 74b714a commit f419d5a

File tree

1 file changed

+55
-5
lines changed

1 file changed

+55
-5
lines changed

Foundation/FileManager.swift

Lines changed: 55 additions & 5 deletions
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,70 @@ 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+
let err = GetLastError()
1832+
if err == ERROR_FILE_NOT_FOUND {
1833+
errno = ENOENT
1834+
}
1835+
throw _NSErrorWithErrno(errno, reading: true, path: path)
1836+
}
1837+
var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
1838+
GetFileInformationByHandle(h, &info)
1839+
// Group id is always 0 on Windows
1840+
statInfo.st_gid = 0
1841+
statInfo.st_atime = info.ftLastAccessTime.time_t
1842+
statInfo.st_ctime = info.ftCreationTime.time_t
1843+
statInfo.st_dev = info.dwVolumeSerialNumber
1844+
// inodes have meaning on FAT/HPFS/NTFS
1845+
statInfo.st_ino = 0
1846+
statInfo.st_rdev = info.dwVolumeSerialNumber
1847+
statInfo.st_mode =
1848+
// On a symlink to a directory, Windows sets both the REPARSE_POINT and
1849+
// DIRECTORY attributes. Since Windows doesn't provide S_IFLNK and we
1850+
// want unix style "symlinks to directories are not directories
1851+
// themselves, we say symlinks are regular files
1852+
UInt16((info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT)) != 0
1853+
? _S_IFREG
1854+
: (info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY)) != 0
1855+
? _S_IFDIR
1856+
: _S_IFREG)
1857+
statInfo.st_mode |= UInt16((info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY)) != 0
1858+
? _S_IREAD
1859+
: _S_IREAD | _S_IWRITE)
1860+
1861+
statInfo.st_mode |= UInt16(_S_IEXEC)
1862+
statInfo.st_mtime = info.ftLastWriteTime.time_t
1863+
statInfo.st_nlink = Int16(info.nNumberOfLinks)
1864+
if info.nFileSizeHigh != 0 {
1865+
throw _NSErrorWithErrno(EOVERFLOW, reading: true, path: path)
1866+
}
1867+
statInfo.st_size = Int32(info.nFileSizeLow)
1868+
// Uid is always 0 on Windows systems
1869+
statInfo.st_uid = 0
1870+
CloseHandle(h)
1871+
#else
18211872
guard lstat(_fsRep, &statInfo) == 0 else {
18221873
throw _NSErrorWithErrno(errno, reading: true, path: path)
18231874
}
1875+
#endif
18241876
return statInfo
18251877
}
1826-
#endif
18271878

1828-
@available(Windows, deprecated, message: "Not Yet Implemented")
18291879
internal func _permissionsOfItem(atPath path: String) throws -> Int {
1880+
let fileInfo = try _lstatFile(atPath: path)
18301881
#if os(Windows)
1831-
NSUnimplemented()
1882+
return Int(fileInfo.st_mode & ~UInt16(ucrt.S_IFMT))
18321883
#else
1833-
let fileInfo = try _lstatFile(atPath: path)
18341884
return Int(fileInfo.st_mode & ~S_IFMT)
18351885
#endif
18361886
}

0 commit comments

Comments
 (0)