Use fallback home directory on Windows (#854)

* Use %SystemDrive\Users\Public as fallback home directory on Windows

* Update to ALLUSERSPROFILE

* Fix test failure

* Fix iOS test failure
This commit is contained in:
Jeremy Schonfeld 2024-08-15 17:27:56 -07:00 committed by GitHub
parent c15a5e1f5c
commit ebb00b8077
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 17 deletions

View File

@ -369,33 +369,27 @@ extension String {
#if !NO_FILESYSTEM #if !NO_FILESYSTEM
internal static func homeDirectoryPath(forUser user: String? = nil) -> String { internal static func homeDirectoryPath(forUser user: String? = nil) -> String {
#if os(Windows) #if os(Windows)
func GetUserProfile() -> String? {
return "USERPROFILE".withCString(encodedAs: UTF16.self) { pwszVariable in
let dwLength: DWORD = GetEnvironmentVariableW(pwszVariable, nil, 0)
// Ensure that `USERPROFILE` is defined.
if dwLength == 0 { return nil }
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwLength)) {
guard GetEnvironmentVariableW(pwszVariable, $0.baseAddress, dwLength) == dwLength - 1 else {
return nil
}
return String(decodingCString: $0.baseAddress!, as: UTF16.self)
}
}
}
if let user { if let user {
func fallbackUserDirectory() -> String {
guard let fallback = ProcessInfo.processInfo.environment["ALLUSERSPROFILE"] else {
fatalError("Unable to find home directory for user \(user) and ALLUSERSPROFILE environment variable is not set")
}
return fallback
}
return user.withCString(encodedAs: UTF16.self) { pwszUserName in return user.withCString(encodedAs: UTF16.self) { pwszUserName in
var cbSID: DWORD = 0 var cbSID: DWORD = 0
var cchReferencedDomainName: DWORD = 0 var cchReferencedDomainName: DWORD = 0
var eUse: SID_NAME_USE = SidTypeUnknown var eUse: SID_NAME_USE = SidTypeUnknown
guard LookupAccountNameW(nil, pwszUserName, nil, &cbSID, nil, &cchReferencedDomainName, &eUse) else { guard LookupAccountNameW(nil, pwszUserName, nil, &cbSID, nil, &cchReferencedDomainName, &eUse) else {
fatalError("unable to lookup SID for user \(user)") return fallbackUserDirectory()
} }
return withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(cbSID)) { pSID in return withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(cbSID)) { pSID in
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(cchReferencedDomainName)) { pwszReferencedDomainName in return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(cchReferencedDomainName)) { pwszReferencedDomainName in
guard LookupAccountNameW(nil, pwszUserName, pSID.baseAddress, &cbSID, pwszReferencedDomainName.baseAddress, &cchReferencedDomainName, &eUse) else { guard LookupAccountNameW(nil, pwszUserName, pSID.baseAddress, &cbSID, pwszReferencedDomainName.baseAddress, &cchReferencedDomainName, &eUse) else {
fatalError("unable to lookup SID for user \(user)") return fallbackUserDirectory()
} }
var pwszSID: LPWSTR? = nil var pwszSID: LPWSTR? = nil
@ -425,7 +419,7 @@ extension String {
var hToken: HANDLE? = nil var hToken: HANDLE? = nil
guard OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) else { guard OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) else {
guard let UserProfile = GetUserProfile() else { guard let UserProfile = ProcessInfo.processInfo.environment["UserProfile"] else {
fatalError("unable to evaluate `%UserProfile%`") fatalError("unable to evaluate `%UserProfile%`")
} }
return UserProfile return UserProfile

View File

@ -897,4 +897,19 @@ final class FileManagerTests : XCTestCase {
} }
} }
} }
func testHomeDirectoryForNonExistantUser() throws {
#if canImport(Darwin) && !os(macOS)
throw XCTSkip("This test is not applicable on this platform")
#else
#if os(Windows)
let fallbackPath = URL(filePath: try XCTUnwrap(ProcessInfo.processInfo.environment["ALLUSERSPROFILE"]), directoryHint: .isDirectory)
#else
let fallbackPath = URL(filePath: "/var/empty", directoryHint: .isDirectory)
#endif
XCTAssertEqual(FileManager.default.homeDirectory(forUser: ""), fallbackPath)
XCTAssertEqual(FileManager.default.homeDirectory(forUser: UUID().uuidString), fallbackPath)
#endif
}
} }