Skip to content

Use FHS/XDG paths for UserDefaults on Linux & BSD-y OSes #1399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CoreFoundation/Base.subproj/CFFileUtilities.c
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@ CF_PRIVATE void _CFIterateDirectory(CFStringRef directoryPath, Boolean appendSla
}


#if DEPLOYMENT_RUNTIME_SWIFT
#if !DEPLOYMENT_RUNTIME_OBJC

// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
// Version 0.8
Expand Down Expand Up @@ -1356,5 +1356,5 @@ CF_PRIVATE CFArrayRef _CFCreateCFArrayByTokenizingString(const char *values, cha
return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks);
}

#endif
#endif // !DEPLOYMENT_RUNTIME_OBJC

82 changes: 82 additions & 0 deletions CoreFoundation/Base.subproj/CFKnownLocations.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* CFKnownLocations.c
Copyright (c) 1999-2017, Apple Inc. and the Swift project authors

Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
*/

#include "CFKnownLocations.h"

#include <CoreFoundation/CFString.h>
#include "CFPriv.h"
#include "CFInternal.h"

#include <assert.h>

CFURLRef _Nullable _CFKnownLocationCreatePreferencesURLForUser(CFKnownLocationUser user, CFStringRef _Nullable username) {
CFURLRef location = NULL;

#if (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI)

/*
Building for a Darwin OS. (We use these paths on Swift builds as well, so that we can interoperate a little with Darwin's defaults(1) command and the other system facilities; but you want to use the system version of CF if possible on those platforms, which will talk to cfprefsd(8) and has stronger interprocess consistency guarantees.)

User:
- Any: /Library/Preferences
- Current: $HOME/Library/Preferences
*/

switch (user) {
case _kCFKnownLocationUserAny:
location = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/Library/Preferences"), kCFURLPOSIXPathStyle, true);
break;

case _kCFKnownLocationUserCurrent:
username = NULL;
// passthrough to:
case _kCFKnownLocationUserByName: {
CFURLRef home = CFCopyHomeDirectoryURLForUser(username);
location = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, CFSTR("/Library/Preferences"), kCFURLPOSIXPathStyle, true, home);
CFRelease(home);

break;
}

}
#elif !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

/*
Building for an OS that uses the FHS, BSD's hier(7), and/or the XDG specification for paths:

User:
- Any: /usr/local/etc/
- Current: $XDG_CONFIG_PATH (usually: $HOME/.config/).
*/

switch (user) {
case _kCFKnownLocationUserAny:
location = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/usr/local/etc"), kCFURLPOSIXPathStyle, true);
break;

case _kCFKnownLocationUserByName:
assert(username == NULL);
// passthrough to:
case _kCFKnownLocationUserCurrent: {
CFStringRef path = _CFXDGCreateConfigHomePath();
location = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, true);
CFRelease(path);

break;
}
}

#else

#error For this platform, you need to define a preferences path for both 'any user' (i.e. installation-wide preferences) or the current user.

#endif

return location;
}
42 changes: 42 additions & 0 deletions CoreFoundation/Base.subproj/CFKnownLocations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* CFKnownLocations.h
Copyright (c) 1999-2017, Apple Inc. and the Swift project authors

Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
*/

#if !defined(__COREFOUNDATION_CFKNOWNLOCATIONS__)
#define __COREFOUNDATION_CFKNOWNLOCATIONS__ 1

#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFURL.h>

CF_ASSUME_NONNULL_BEGIN

typedef CF_ENUM(CFIndex, CFKnownLocationUser) {
_kCFKnownLocationUserAny,
_kCFKnownLocationUserCurrent,
_kCFKnownLocationUserByName,
};

/* A note on support:

- We document that CFPreferences… can only take AnyUser or CurrentUser as users.
- The code we shipped so far accepted the name of any one user on the current system as an alternative, but:
- For platforms that use the XDG spec to identify a configuration path in a user's home, we cannot determine that path for any user other than the one we're currently running as.

So:
- We're keeping that behavior when building Core Foundation for Darwin/ObjC for compatibility, hence the _EXTENSIBLE above; on those platforms, the …ByName enum will continue working to get locations for arbitrary usernames. But:
- For Swift and any new platform, we are enforcing the documented constraint. Using a user value other than …Any or …Current above will assert (or return NULL if asserts are off).

See CFKnownLocations.c for a summary of what paths are returned.
*/

// The username parameter is ignored for any user constant other than …ByName. …ByName with a NULL username is the same as …Current.
extern CFURLRef _Nullable _CFKnownLocationCreatePreferencesURLForUser(CFKnownLocationUser user, CFStringRef _Nullable username);

CF_ASSUME_NONNULL_END

#endif /* __COREFOUNDATION_CFKNOWNLOCATIONS__ */
29 changes: 29 additions & 0 deletions CoreFoundation/Base.subproj/CFPriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <CoreFoundation/CFRunLoop.h>
#include <CoreFoundation/CFSocket.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <CoreFoundation/CFKnownLocations.h>

CF_EXTERN_C_BEGIN

Expand Down Expand Up @@ -655,6 +656,34 @@ CF_EXPORT void _CFGetPathExtensionRangesFromPathComponent(CFStringRef inName, CF
CF_EXPORT Boolean _CFExtensionUniCharsIsValidToAppend(const UniChar *uchars, CFIndex ucharsLength) API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
CF_EXPORT Boolean _CFExtensionIsValidToAppend(CFStringRef extension) API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));


#if !DEPLOYMENT_RUNTIME_OBJC

// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
// Version 0.8

// note: All paths set in these environment variables must be absolute.

/// a single base directory relative to which user-specific data files should be written. This directory is defined by the environment variable $XDG_DATA_HOME.
CF_EXPORT CFStringRef _CFXDGCreateDataHomePath(void) CF_RETURNS_RETAINED;

/// a single base directory relative to which user-specific configuration files should be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
CF_EXPORT CFStringRef _CFXDGCreateConfigHomePath(void) CF_RETURNS_RETAINED;

/// a set of preference ordered base directories relative to which data files should be searched. This set of directories is defined by the environment variable $XDG_DATA_DIRS.
CF_EXPORT CFArrayRef _CFXDGCreateDataDirectoriesPaths(void) CF_RETURNS_RETAINED;

/// a set of preference ordered base directories relative to which configuration files should be searched. This set of directories is defined by the environment variable $XDG_CONFIG_DIRS.
CF_EXPORT CFArrayRef _CFXDGCreateConfigDirectoriesPaths(void) CF_RETURNS_RETAINED;

/// a single base directory relative to which user-specific non-essential (cached) data should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
CF_EXPORT CFStringRef _CFXDGCreateCacheDirectoryPath(void) CF_RETURNS_RETAINED;

/// a single base directory relative to which user-specific runtime files and other file objects should be placed. This directory is defined by the environment variable $XDG_RUNTIME_DIR.
CF_EXPORT CFStringRef _CFXDGCreateRuntimeDirectoryPath(void) CF_RETURNS_RETAINED;

#endif // !DEPLOYMENT_RUNTIME_OBJC

CF_EXTERN_C_END

#endif /* ! __COREFOUNDATION_CFPRIV__ */
Expand Down
50 changes: 22 additions & 28 deletions CoreFoundation/Preferences.subproj/CFPreferences.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <CoreFoundation/CFUUID.h>
#endif

#include <assert.h>

#if DEBUG_PREFERENCES_MEMORY
#include "../Tests/CFCountingAllocator.c"
#endif
Expand Down Expand Up @@ -182,37 +184,29 @@ static CFURLRef _preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
return url;

#else
CFURLRef home = NULL;
CFURLRef url;
int levels = 0;
// if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
CFURLRef location = NULL;

CFKnownLocationUser user;

if (userName == kCFPreferencesAnyUser) {
if (!home) home = CFURLCreateWithFileSystemPath(alloc, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle, true);
levels = 1;
if (hostName == kCFPreferencesCurrentHost) url = home;
else {
url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Network/"), kCFURLPOSIXPathStyle, true, home);
levels ++;
CFRelease(home);
}
user = _kCFKnownLocationUserAny;
} else if (userName == kCFPreferencesCurrentUser) {
user = _kCFKnownLocationUserCurrent;
} else {
home = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName);
if (home) {
url = (safeLevel > 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle, true, home) :
CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle, true, home);
levels = 2;
CFRelease(home);
if (hostName != kCFPreferencesAnyHost) {
home = url;
url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("ByHost/"), kCFURLPOSIXPathStyle, true, home);
levels ++;
CFRelease(home);
}
} else {
url = NULL;
}
user = _kCFKnownLocationUserByName;
}
return url;

CFURLRef base = _CFKnownLocationCreatePreferencesURLForUser(user, userName);

if (hostName == kCFPreferencesCurrentHost) {
location = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, CFSTR("ByHost"), kCFURLPOSIXPathStyle, true, base);
} else {
assert(hostName == kCFPreferencesAnyHost);
location = CFRetain(base);
}

CFRelease(base);
return location;
#endif
}

Expand Down
2 changes: 2 additions & 0 deletions CoreFoundation/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@
'Locale.subproj/CFDateFormatter_Private.h',
'Locale.subproj/CFLocale_Private.h',
'Parsing.subproj/CFPropertyList_Private.h',
'Base.subproj/CFKnownLocations.h',
],
project = [
])
Expand Down Expand Up @@ -292,6 +293,7 @@
'String.subproj/CFRegularExpression.c',
'String.subproj/CFAttributedString.c',
'String.subproj/CFRunArray.c',
'Base.subproj/CFKnownLocations.h',
]

sources = CompileSources(sources_list)
Expand Down
47 changes: 40 additions & 7 deletions Foundation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
0383A1751D2E558A0052E5D1 /* TestStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0383A1741D2E558A0052E5D1 /* TestStream.swift */; };
03B6F5841F15F339004F25AF /* TestURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B6F5831F15F339004F25AF /* TestURLProtocol.swift */; };
1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1520469A1D8AEABE00D02E36 /* HTTPServer.swift */; };
153E951120111DC500F250BE /* CFKnownLocations.h in Headers */ = {isa = PBXBuildFile; fileRef = 153E950F20111DC500F250BE /* CFKnownLocations.h */; settings = {ATTRIBUTES = (Private, ); }; };
153E951220111DC500F250BE /* CFKnownLocations.c in Sources */ = {isa = PBXBuildFile; fileRef = 153E951020111DC500F250BE /* CFKnownLocations.c */; };
159884921DCC877700E3314C /* TestHTTPCookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159884911DCC877700E3314C /* TestHTTPCookieStorage.swift */; };
231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestDecimal.swift */; };
294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; };
Expand Down Expand Up @@ -498,6 +500,8 @@
0383A1741D2E558A0052E5D1 /* TestStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestStream.swift; sourceTree = "<group>"; };
03B6F5831F15F339004F25AF /* TestURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestURLProtocol.swift; sourceTree = "<group>"; };
1520469A1D8AEABE00D02E36 /* HTTPServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPServer.swift; sourceTree = "<group>"; };
153E950F20111DC500F250BE /* CFKnownLocations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFKnownLocations.h; sourceTree = "<group>"; };
153E951020111DC500F250BE /* CFKnownLocations.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = CFKnownLocations.c; sourceTree = "<group>"; };
159884911DCC877700E3314C /* TestHTTPCookieStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHTTPCookieStorage.swift; sourceTree = "<group>"; };
22B9C1E01C165D7A00DECFF9 /* TestDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDate.swift; sourceTree = "<group>"; };
231503DA1D8AEE5D0061694D /* TestDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDecimal.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1091,6 +1095,8 @@
5B5D895F1BBDABF400234F36 /* CFUtilities.h */,
5B5D89541BBDAA0100234F36 /* CFUUID.c */,
5B5D89531BBDAA0100234F36 /* CFUUID.h */,
153E950F20111DC500F250BE /* CFKnownLocations.h */,
153E951020111DC500F250BE /* CFKnownLocations.c */,
5B5D88B51BBC978900234F36 /* CoreFoundation_Prefix.h */,
5B5D895B1BBDAB7E00234F36 /* CoreFoundation.h */,
5B5D88C61BBC983600234F36 /* ForFoundationOnly.h */,
Expand Down Expand Up @@ -1912,6 +1918,7 @@
5B7C8AD61BEA80FC00C5B690 /* CFPlugIn.h in Headers */,
5B7C8AD81BEA80FC00C5B690 /* CFPreferences.h in Headers */,
5B7C8AF11BEA81AC00C5B690 /* CFStreamAbstract.h in Headers */,
153E951120111DC500F250BE /* CFKnownLocations.h in Headers */,
5B7C8AFE1BEA81AC00C5B690 /* CFURLComponents.h in Headers */,
5B7C8ABC1BEA805C00C5B690 /* CFAvailability.h in Headers */,
5B6E11A91DA45EB5009B48A3 /* CFDateFormatter_Private.h in Headers */,
Expand Down Expand Up @@ -2363,6 +2370,7 @@
5B7C8A961BEA7FF900C5B690 /* CFBundle_Binary.c in Sources */,
5B7C8AAC1BEA800D00C5B690 /* CFString.c in Sources */,
5B7C8A821BEA7FCE00C5B690 /* CFStorage.c in Sources */,
153E951220111DC500F250BE /* CFKnownLocations.c in Sources */,
5B7C8AAF1BEA800D00C5B690 /* CFStringUtilities.c in Sources */,
5B7C8A891BEA7FDB00C5B690 /* CFNumberFormatter.c in Sources */,
5B7C8A8C1BEA7FE200C5B690 /* CFDate.c in Sources */,
Expand Down Expand Up @@ -2660,7 +2668,11 @@
INFOPLIST_FILE = Foundation/Info.plist;
INIT_ROUTINE = "___CFInitialize";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
OTHER_CFLAGS = (
"-DCF_BUILDING_CF",
"-DDEPLOYMENT_TARGET_MACOSX",
Expand Down Expand Up @@ -2732,7 +2744,11 @@
INFOPLIST_FILE = Foundation/Info.plist;
INIT_ROUTINE = "___CFInitialize";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
OTHER_CFLAGS = (
"-DCF_BUILDING_CF",
"-DDEPLOYMENT_TARGET_MACOSX",
Expand Down Expand Up @@ -2880,7 +2896,11 @@
/usr/include/libxml2,
);
INFOPLIST_FILE = TestFoundation/Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = mh_execute;
OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_ENABLE_LIBDISPATCH -swift-version 4";
Expand All @@ -2906,7 +2926,11 @@
/usr/include/libxml2,
);
INFOPLIST_FILE = TestFoundation/Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = mh_execute;
OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_ENABLE_LIBDISPATCH -swift-version 4";
Expand All @@ -2930,7 +2954,12 @@
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = TestFoundation/xdgTestHelper/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../../.. @loader_path/../../.. @executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../../..",
"@loader_path/../../..",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.12;
PRODUCT_BUNDLE_IDENTIFIER = org.swift.xdgTestHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -2952,7 +2981,12 @@
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = TestFoundation/xdgTestHelper/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../../.. @loader_path/../../.. @executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../../..",
"@loader_path/../../..",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.12;
PRODUCT_BUNDLE_IDENTIFIER = org.swift.xdgTestHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -3044,4 +3078,3 @@
};
rootObject = 5B5D88541BBC938800234F36 /* Project object */;
}

Loading