Skip to content

Commit d8d4511

Browse files
authored
Use fallback home directory on Windows (#854) (#862)
* Use %SystemDrive\Users\Public as fallback home directory on Windows * Update to ALLUSERSPROFILE * Fix test failure * Fix iOS test failure
1 parent 07b7a5d commit d8d4511

File tree

2 files changed

+25
-16
lines changed

2 files changed

+25
-16
lines changed

Sources/FoundationEssentials/String/String+Path.swift

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -367,33 +367,27 @@ extension String {
367367
#if !NO_FILESYSTEM
368368
internal static func homeDirectoryPath(forUser user: String? = nil) -> String {
369369
#if os(Windows)
370-
func GetUserProfile() -> String? {
371-
return "USERPROFILE".withCString(encodedAs: UTF16.self) { pwszVariable in
372-
let dwLength: DWORD = GetEnvironmentVariableW(pwszVariable, nil, 0)
373-
// Ensure that `USERPROFILE` is defined.
374-
if dwLength == 0 { return nil }
375-
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwLength)) {
376-
guard GetEnvironmentVariableW(pwszVariable, $0.baseAddress, dwLength) == dwLength - 1 else {
377-
return nil
378-
}
379-
return String(decodingCString: $0.baseAddress!, as: UTF16.self)
370+
if let user {
371+
func fallbackUserDirectory() -> String {
372+
guard let fallback = ProcessInfo.processInfo.environment["ALLUSERSPROFILE"] else {
373+
fatalError("Unable to find home directory for user \(user) and ALLUSERSPROFILE environment variable is not set")
380374
}
375+
376+
return fallback
381377
}
382-
}
383-
384-
if let user {
378+
385379
return user.withCString(encodedAs: UTF16.self) { pwszUserName in
386380
var cbSID: DWORD = 0
387381
var cchReferencedDomainName: DWORD = 0
388382
var eUse: SID_NAME_USE = SidTypeUnknown
389383
guard LookupAccountNameW(nil, pwszUserName, nil, &cbSID, nil, &cchReferencedDomainName, &eUse) else {
390-
fatalError("unable to lookup SID for user \(user)")
384+
return fallbackUserDirectory()
391385
}
392386

393387
return withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(cbSID)) { pSID in
394388
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(cchReferencedDomainName)) { pwszReferencedDomainName in
395389
guard LookupAccountNameW(nil, pwszUserName, pSID.baseAddress, &cbSID, pwszReferencedDomainName.baseAddress, &cchReferencedDomainName, &eUse) else {
396-
fatalError("unable to lookup SID for user \(user)")
390+
return fallbackUserDirectory()
397391
}
398392

399393
var pwszSID: LPWSTR? = nil
@@ -423,7 +417,7 @@ extension String {
423417

424418
var hToken: HANDLE? = nil
425419
guard OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) else {
426-
guard let UserProfile = GetUserProfile() else {
420+
guard let UserProfile = ProcessInfo.processInfo.environment["UserProfile"] else {
427421
fatalError("unable to evaluate `%UserProfile%`")
428422
}
429423
return UserProfile

Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,4 +897,19 @@ final class FileManagerTests : XCTestCase {
897897
}
898898
}
899899
}
900+
901+
func testHomeDirectoryForNonExistantUser() throws {
902+
#if canImport(Darwin) && !os(macOS)
903+
throw XCTSkip("This test is not applicable on this platform")
904+
#else
905+
#if os(Windows)
906+
let fallbackPath = URL(filePath: try XCTUnwrap(ProcessInfo.processInfo.environment["ALLUSERSPROFILE"]), directoryHint: .isDirectory)
907+
#else
908+
let fallbackPath = URL(filePath: "/var/empty", directoryHint: .isDirectory)
909+
#endif
910+
911+
XCTAssertEqual(FileManager.default.homeDirectory(forUser: ""), fallbackPath)
912+
XCTAssertEqual(FileManager.default.homeDirectory(forUser: UUID().uuidString), fallbackPath)
913+
#endif
914+
}
900915
}

0 commit comments

Comments
 (0)