2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 // CoreFoundation related utilities
28 #include <security_utilities/cfutilities.h>
29 #include <security_utilities/errors.h>
30 #include <security_utilities/debugging.h>
31 #include <security_utilities/unix++.h>
32 #include <utilities/SecCFRelease.h>
41 ModuleNexus
<CFEmptyArray
> cfEmptyArray
;
43 CFEmptyArray::CFEmptyArray()
45 mArray
= CFArrayCreate(NULL
, NULL
, 0, NULL
);
50 // Turn a C(++) string into a CFURLRef indicating a file: path
52 CFURLRef
makeCFURL(const char *s
, bool isDirectory
, CFURLRef base
)
55 return CFURLCreateWithFileSystemPathRelativeToBase(NULL
,
56 CFTempString(s
), kCFURLPOSIXPathStyle
, isDirectory
, base
);
58 return CFURLCreateWithFileSystemPath(NULL
,
59 CFTempString(s
), kCFURLPOSIXPathStyle
, isDirectory
);
62 CFURLRef
makeCFURL(CFStringRef s
, bool isDirectory
, CFURLRef base
)
65 return CFURLCreateWithFileSystemPathRelativeToBase(NULL
, s
, kCFURLPOSIXPathStyle
, isDirectory
, base
);
67 return CFURLCreateWithFileSystemPath(NULL
, s
, kCFURLPOSIXPathStyle
, isDirectory
);
72 // CFMallocData objects
74 CFMallocData::operator CFDataRef ()
76 CFDataRef result
= makeCFDataMalloc(mData
, mSize
);
79 mData
= NULL
; // release ownership
85 // Make CFDictionaries from stuff
87 CFDictionaryRef
makeCFDictionary(unsigned count
, ...)
89 CFTypeRef keys
[count
], values
[count
];
91 va_start(args
, count
);
92 for (unsigned n
= 0; n
< count
; n
++) {
93 keys
[n
] = va_arg(args
, CFTypeRef
);
94 values
[n
] = va_arg(args
, CFTypeRef
);
97 return CFDictionaryCreate(NULL
, (const void **)keys
, (const void **)values
, count
,
98 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
101 CFMutableDictionaryRef
makeCFMutableDictionary()
103 if (CFMutableDictionaryRef r
= CFDictionaryCreateMutable(NULL
, 0,
104 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
))
109 CFMutableDictionaryRef
makeCFMutableDictionary(unsigned count
, ...)
111 CFMutableDictionaryRef dict
= makeCFMutableDictionary();
114 va_start(args
, count
);
115 for (unsigned n
= 0; n
< count
; n
++) {
116 CFTypeRef key
= va_arg(args
, CFTypeRef
);
117 CFTypeRef value
= va_arg(args
, CFTypeRef
);
118 CFDictionaryAddValue(dict
, key
, value
);
125 CFMutableDictionaryRef
makeCFMutableDictionary(CFDictionaryRef dict
)
127 if (CFMutableDictionaryRef r
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
))
132 CFDictionaryRef
makeCFDictionaryFrom(CFDataRef data
)
135 CFPropertyListRef plist
= CFPropertyListCreateFromXMLData(NULL
, data
,
136 kCFPropertyListImmutable
, NULL
);
137 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
139 return CFDictionaryRef(plist
);
145 CFDictionaryRef
makeCFDictionaryFrom(const void *data
, size_t length
)
147 return makeCFDictionaryFrom(CFTempData(data
, length
).get());
152 static void cfarrayApplyBlock_func(const void *value
, const void *ctx
)
154 CFArrayApplierBlock block
= CFArrayApplierBlock(ctx
);
157 void cfArrayApplyBlock(CFArrayRef array
, CFRange range
, CFArrayApplierBlock block
)
159 CFArrayApplyFunction(array
, range
, (CFArrayApplierFunction
)cfarrayApplyBlock_func
, block
);
161 void cfArrayApplyBlock(CFArrayRef array
, CFArrayApplierBlock block
)
163 CFRange range
= CFRangeMake(0, CFArrayGetCount(array
));
164 cfArrayApplyBlock(array
, range
, block
);
167 static void cfdictionaryApplyBlock_func(const void *key
, const void *value
, void *ctx
)
169 CFDictionaryApplierBlock block
= CFDictionaryApplierBlock(ctx
);
172 void cfDictionaryApplyBlock(CFDictionaryRef dict
, CFDictionaryApplierBlock block
)
174 CFDictionaryApplyFunction(dict
, cfdictionaryApplyBlock_func
, block
);
179 // Turn a CFString into a UTF8-encoded C++ string.
180 // If release==true, the argument will be CFReleased even in case of error.
182 string
cfString(CFStringRef str
)
187 if (const char *s
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
)) {
191 // need to extract into buffer
193 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
);
194 std::vector
<char> buffer
;
195 buffer
.resize(length
+ 1);
196 if (CFStringGetCString(str
, &buffer
[0], length
+ 1, kCFStringEncodingUTF8
))
201 string
cfStringRelease(CFStringRef CF_CONSUMED inStr
)
203 CFRef
<CFStringRef
> str(inStr
);
204 return cfString(str
);
207 string
cfString(CFURLRef inUrl
)
212 UInt8 buffer
[PATH_MAX
+1];
213 if (CFURLGetFileSystemRepresentation(inUrl
, true, buffer
, sizeof(buffer
)))
214 return string(reinterpret_cast<char *>(buffer
));
219 string
cfStringRelease(CFURLRef CF_CONSUMED inUrl
)
221 CFRef
<CFURLRef
> bundle(inUrl
);
222 return cfString(bundle
);
225 string
cfString(CFBundleRef inBundle
)
229 return cfStringRelease(CFBundleCopyBundleURL(inBundle
));
232 string
cfStringRelease(CFBundleRef CF_CONSUMED inBundle
)
234 CFRef
<CFBundleRef
> bundle(inBundle
);
235 return cfString(bundle
);
239 string
cfString(CFTypeRef it
, OSStatus err
)
242 MacOSError::throwMe(err
);
243 CFTypeID id
= CFGetTypeID(it
);
244 if (id
== CFStringGetTypeID())
245 return cfString(CFStringRef(it
));
246 else if (id
== CFURLGetTypeID())
247 return cfString(CFURLRef(it
));
248 else if (id
== CFBundleGetTypeID())
249 return cfString(CFBundleRef(it
));
251 return cfStringRelease(CFCopyDescription(it
));
257 // CFURLAccess wrappers for specific purposes
259 CFDataRef
cfReadFile(CFURLRef url
)
264 if (CFURLCreateDataAndPropertiesFromResource(NULL
, url
,
265 &data
, NULL
, NULL
, &error
)) {
268 secinfo("cfloadfile", "failed to fetch %s error=%d", cfString(url
).c_str(), int(error
));
273 CFDataRef
cfReadFile(int fd
, size_t bytes
)
275 uint8_t *buffer
= (uint8_t *) malloc(bytes
);
280 if (read(fd
, buffer
, bytes
) != bytes
) {
285 CFDataRef result
= CFDataCreateWithBytesNoCopy(kCFAllocatorMalloc
, buffer
, bytes
, kCFAllocatorMalloc
);
287 // If CFDataCreateWithBytesNoCopy fails, the buffer is not free()-ed
288 if (result
== NULL
) {
299 CFArrayRef
makeCFArray(CFIndex count
, ...)
301 CFTypeRef elements
[count
];
303 va_start(args
, count
);
304 for (CFIndex n
= 0; n
< count
; n
++)
305 elements
[n
] = va_arg(args
, CFTypeRef
);
307 return CFArrayCreate(NULL
, elements
, count
, &kCFTypeArrayCallBacks
);
310 CFMutableArrayRef
makeCFMutableArray(CFIndex count
, ...)
312 CFMutableArrayRef array
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
314 va_start(args
, count
);
315 for (CFIndex n
= 0; n
< count
; n
++)
316 CFArrayAppendValue(array
, va_arg(args
, CFTypeRef
));
321 struct mmapAllocatorInfo
{
325 static void *mmapDeallocatorAllocate(CFIndex allocSize
, CFOptionFlags hint
, void *info
) {
326 /* We do nothing here. makeMappedData already did everything, the only thing we want
327 * this allocator for is to deallocate. */
331 static void mmapDeallocatorDeallocate(void *ptr
, void *info
) {
332 struct mmapAllocatorInfo
const *mmapInfo
=
333 reinterpret_cast<struct mmapAllocatorInfo
const *>
334 (CFDataGetBytePtr(reinterpret_cast<CFDataRef
>(info
)));
336 if (munmap(ptr
, mmapInfo
->size
) != 0) {
337 secdebug("mmapdeallocatordeallocate", "could not unmap: errno %d", errno
);
341 static CFIndex
mmapPreferredSize(CFIndex size
, CFOptionFlags hint
, void *info
) {
342 return size
+ sizeof(struct mmapAllocatorInfo
); // No need to be exact here.
345 CFDataRef
cfMapFile(int fd
, size_t bytes
)
347 off_t offset
= lseek(fd
, 0, SEEK_CUR
);
350 secdebug("cfmapfile", "cannot get file offset, errno %d", errno
);
353 uint8_t *buf
= (uint8_t*)mmap(NULL
, bytes
, PROT_READ
, MAP_PRIVATE
, fd
, offset
);
355 if (buf
== MAP_FAILED
) {
356 secdebug("cfmapfile", "cannot mmap file, errno %d", errno
);
360 /* We're finally set up. */
362 struct mmapAllocatorInfo info
= {
366 CFRef
<CFDataRef
> infoData
= makeCFData(&info
, sizeof(info
));
368 CFAllocatorContext context
= {
372 .release
= CFRelease
,
373 .copyDescription
= NULL
,
374 .allocate
= mmapDeallocatorAllocate
,
376 .deallocate
= mmapDeallocatorDeallocate
,
377 .preferredSize
= mmapPreferredSize
380 context
.info
= (void*)infoData
.get();
382 CFRef
<CFAllocatorRef
> deallocator
= CFAllocatorCreate(NULL
, &context
);
384 CFDataRef result
= CFDataCreateWithBytesNoCopy(NULL
, buf
, info
.size
, deallocator
.get());
386 // If CFDataCreateWithBytesNoCopy fails, the buffer is not unallocated
387 if (result
== NULL
) {
395 CFDataRef
cfMapFile(CFURLRef url
) {
398 /* This is contrived,
399 * but we want this as compatible to cfLoadFile as possible, which also means
400 * not throwing the exceptions that cfString might, as cfLoadFile does not call
404 path
= cfString(url
);
406 secdebug("cfmapfile", "Exception while forming path from URL, giving up.");
410 UnixPlusPlus::AutoFileDesc
fd(path
.c_str(), O_RDONLY
, 0666 | UnixPlusPlus::AutoFileDesc::modeMissingOk
);
415 secdebug("cfmapfile", "cannot open file '%s', errno %d", path
.c_str(), errno
);
419 if (fstat(fd
.fd(), &st
) != 0) {
420 secdebug("cfmapfile", "cannot stat '%s', errno %d", path
.c_str(), errno
);
424 if (st
.st_size
< 0) {
425 secdebug("cfmapfile", "size for '%s' is negative", path
.c_str());
429 return cfMapFile(fd
.fd(), fd
.fileSize());
432 CFDataRef
cfLoadFile(CFURLRef url
){
433 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
434 return cfMapFile(url
);
436 return cfReadFile(url
);
440 CFDataRef
cfLoadFile(int fd
, size_t bytes
){
441 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
442 return cfMapFile(fd
, bytes
);
444 return cfReadFile(fd
, bytes
);
449 } // end namespace Security