#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <libc.h>
#include <Kernel/libsa/mkext.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fts.h>
#include <mach-o/arch.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/machine/vm_param.h>
#include <mach/kmod.h>
#include <mach/mach_types.h>
#include <IOKit/kext/KXKextManager.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitServer.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOCFSerialize.h>
#include <libkern/OSByteOrder.h>
#define DEFAULT_CACHE_DIR "/System/Library/Caches/com.apple.kernelcaches"
#define SA_HAS_RUN_PATH "/var/db/.AppleSetupDone"
#define kKXROMExtensionsFolder "/System/Library/Caches/com.apple.romextensions/"
#define TEMP_DIR "/com.apple.iokit.kextcache.mkext.XX"
char * progname = "(unknown)";
__private_extern__ u_int32_t
local_adler32(u_int8_t *buffer, int32_t length);
__private_extern__ void
find_arch(u_int8_t **dataP, off_t *sizeP, cpu_type_t in_cpu,
cpu_subtype_t in_cpu_subtype, u_int8_t *data_ptr, off_t filesize);
__private_extern__ int
get_arch_from_flag(char *name, cpu_type_t *cpuP, cpu_subtype_t *subcpuP);
ssize_t createMkextArchive(
int fd,
CFDictionaryRef kextDict,
const char * mkextFilename,
const char * archName,
cpu_type_t archCPU,
cpu_subtype_t archSubtype,
int verbose_level);
Boolean checkMkextArchiveSize( ssize_t size );
KXKextManagerError
PreLink(KXKextManagerRef theKextManager, CFDictionaryRef kextDict,
const char * kernelCacheFilename,
const struct timeval *cacheFileTimes,
const char * kernel_file_name,
const char * platform_name,
const char * root_path,
CFSetRef kernel_requests,
Boolean all_plists,
const NXArchInfo * arch,
int verbose_level, Boolean debug_mode);
char * CFURLCopyCString(CFURLRef anURL);
static void addKextForMkextCache(
KXKextRef theKext,
CFMutableDictionaryRef checkDictionary,
cpu_type_t archCPU,
cpu_subtype_t archSubtype,
Boolean local,
Boolean network,
Boolean safeboot,
CFSetRef kernel_requests,
int verbose_level,
Boolean do_tests);
static void collectKextsForMkextCache(
CFArrayRef kexts,
CFMutableDictionaryRef checkDictionary,
cpu_type_t archCPU,
cpu_subtype_t archSubtype,
Boolean local,
Boolean network,
Boolean safeboot,
CFSetRef kernel_requests,
int verbose_level,
Boolean do_tests);
__private_extern__ void verbose_log(const char * format, ...);
static void error_log(const char * format, ...);
static int user_approve(int default_answer, const char * format, ...);
static const char * user_input(const char * format, ...);
static Boolean addKextsToManager(
KXKextManagerRef aManager,
CFArrayRef kextNames,
CFMutableArrayRef kextNamesToUse,
Boolean do_tests);
static void get_catalog_demand_lists(CFMutableSetRef * kernel_requests,
CFSetRef * kernel_cache_misses,
int verbose_level);
static void usage(int level);
#define kMaxArchs 64
int main(int argc, const char * argv[])
{
int exit_code = 0;
int optchar;
KXKextManagerRef theKextManager = NULL; KXKextManagerError result;
CFIndex i, count;
Boolean do_tests = false; Boolean forkExit = false; Boolean local_for_repositories = false; Boolean network_for_repositories = false; Boolean safeboot_for_repositories = false; Boolean local_for_all = false; Boolean network_for_all = false; Boolean safeboot_for_all = false; Boolean repositoryCaches = false; const char * mkextFilename = NULL; char kernelCacheBuffer[PATH_MAX];
const char * kernelCacheFilename = NULL; Boolean pretend_authentic = false; Boolean load_in_task = false;
Boolean include_kernel_requests = false;
Boolean cache_looks_uptodate = false;
Boolean debug_mode = false; int verbose_level = 0; const char * default_kernel_file = "/mach_kernel";
const char * kernel_file = default_kernel_file; const char * source_extensions = "/System/Library/Extensions";
int fd = -1;
const char * output_filename = NULL;
const char * temp_dir;
char * temp_file = NULL;
struct stat kernel_stat_buf;
struct stat extensions_stat_buf;
struct stat rom_extensions_stat_buf;
Boolean have_kernel_time, have_extensions_time;
struct timeval _cacheFileTimes[2];
struct timeval *cacheFileTimes = NULL;
struct {
char platform_name[64];
char root_path[256];
} platform_name_root_path;
io_registry_entry_t entry;
Boolean all_plists;
NXArchInfo archAny = {
"any",
CPU_TYPE_ANY,
CPU_SUBTYPE_MULTIPLE
};
CFMutableArrayRef repositoryDirectories = NULL; CFMutableArrayRef repositories = NULL; CFMutableArrayRef kextNames = NULL; CFMutableArrayRef kextNamesToUse = NULL; CFMutableArrayRef namedKexts = NULL; CFMutableArrayRef repositoryKexts = NULL; CFMutableDictionaryRef checkDictionary = NULL; CFMutableSetRef kernel_requests = NULL; CFSetRef kernel_cache_misses = NULL; const NXArchInfo *archs[kMaxArchs] = {NULL};
int nArchs = 0;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
repositoryDirectories = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!repositoryDirectories) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
repositories = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!repositories) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
kextNames = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!kextNames) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
kextNamesToUse = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!kextNamesToUse) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
namedKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!namedKexts) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
repositoryKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!repositoryKexts) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
checkDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!checkDictionary) {
exit_code = 1;
fprintf(stderr, "memory allocation failure\n");
goto finish;
}
bzero(&platform_name_root_path, sizeof(platform_name_root_path));
entry = IORegistryEntryFromPath(kIOMasterPortDefault, kIOServicePlane ":/");
if (entry)
{
if (KERN_SUCCESS != IORegistryEntryGetName(entry, platform_name_root_path.platform_name))
platform_name_root_path.platform_name[0] = 0;
IOObjectRelease(entry);
}
entry = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/chosen");
if (entry)
{
CFTypeRef obj;
obj = IORegistryEntryCreateCFProperty(entry, CFSTR("rootpath"), kCFAllocatorDefault, kNilOptions);
if (obj && (CFGetTypeID(obj) == CFDataGetTypeID()))
{
CFIndex len = CFDataGetLength((CFDataRef) obj);
strncpy(platform_name_root_path.root_path, CFDataGetBytePtr((CFDataRef) obj), len);
platform_name_root_path.root_path[len] = 0;
}
IOObjectRelease(entry);
}
if (!platform_name_root_path.platform_name[0] || !platform_name_root_path.root_path[0])
platform_name_root_path.platform_name[0] = platform_name_root_path.root_path[0] = 0;
have_kernel_time = (0 == stat(kernel_file, &kernel_stat_buf));
have_extensions_time = (0 == stat(source_extensions, &extensions_stat_buf));
if (have_kernel_time || have_extensions_time)
{
cacheFileTimes = _cacheFileTimes;
if (!have_kernel_time
|| (have_extensions_time && (extensions_stat_buf.st_mtime > kernel_stat_buf.st_mtime)))
{
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &extensions_stat_buf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &extensions_stat_buf.st_mtimespec);
}
else
{
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &kernel_stat_buf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &kernel_stat_buf.st_mtimespec);
}
cacheFileTimes[1].tv_sec++;
}
while ((optchar = getopt(argc, (char * const *)argv,
"a:cdeFhkK:lLm:nNrsStvz")) != -1) {
switch (optchar) {
case 'a':
if (nArchs >= kMaxArchs) {
fprintf(stderr, "maximum of %d architectures supported\n", kMaxArchs);
exit_code = 1;
goto finish;
}
archs[nArchs] = NXGetArchInfoFromName(optarg);
if (archs[nArchs] == NULL) {
fprintf(stderr, "unknown CPU arch %s\n\n", optarg);
usage(0);
exit_code = 1;
goto finish;
}
++nArchs;
break;
case 'r':
include_kernel_requests = true;
break;
case 'd':
debug_mode = true;
break;
case 'e':
if (mkextFilename) {
fprintf(stderr, "an output filename has already been set\n\n");
usage(0);
exit_code = 1;
goto finish;
}
mkextFilename = "/System/Library/Extensions.mkext";
CFArrayAppendValue(repositoryDirectories, kKXSystemExtensionsFolder);
break;
case 'F':
forkExit = true;
break;
case 'K':
kernel_file = optarg;
break;
case 'k':
repositoryCaches = true;
break;
case 'l':
local_for_repositories = true;
break;
case 'L':
local_for_all = true;
break;
case 'c':
if (kernelCacheFilename) {
fprintf(stderr, "an output filename has already been set\n\n");
usage(0);
exit_code = 1;
goto finish;
}
if (optind >= argc) {
kernelCacheFilename = DEFAULT_CACHE_DIR "/kernelcache";
struct stat cache_stat_buf;
if ((-1 == stat(DEFAULT_CACHE_DIR, &cache_stat_buf))
|| !(S_IFDIR & cache_stat_buf.st_mode)) {
mkdir(DEFAULT_CACHE_DIR, 0755);
}
CFArrayAppendValue(repositoryDirectories,
kKXSystemExtensionsFolder);
if (0 == stat(kKXROMExtensionsFolder, &rom_extensions_stat_buf))
CFArrayAppendValue(repositoryDirectories, CFSTR(kKXROMExtensionsFolder));
if (!platform_name_root_path.platform_name[0]) {
local_for_repositories = true;
}
if (!include_kernel_requests) {
platform_name_root_path.root_path[0] = platform_name_root_path.platform_name[0] = 0;
}
if (platform_name_root_path.platform_name[0] || platform_name_root_path.root_path[0])
{
sprintf(kernelCacheBuffer, "%s.%08X",
kernelCacheFilename,
NXSwapHostIntToBig(local_adler32((u_int8_t *) &platform_name_root_path,
sizeof(platform_name_root_path))));
kernelCacheFilename = kernelCacheBuffer;
}
if ((0 == stat(kernelCacheFilename, &cache_stat_buf))
&& have_kernel_time && have_extensions_time)
{
if ((cache_stat_buf.st_mtime > kernel_stat_buf.st_mtime)
&& (cache_stat_buf.st_mtime > extensions_stat_buf.st_mtime)
&& (cache_stat_buf.st_mtime == cacheFileTimes[1].tv_sec))
cache_looks_uptodate = true;
}
if (-1 == stat(SA_HAS_RUN_PATH, &cache_stat_buf)) {
if (verbose_level >= 1) {
verbose_log("SetupAssistant not yet run");
}
exit(0);
}
} else {
kernelCacheFilename = argv[optind++];
}
break;
case 'm':
if (mkextFilename) {
fprintf(stderr, "an mkext filename has already been set\n\n");
usage(0);
exit_code = 1;
goto finish;
}
mkextFilename = optarg;
break;
case 'n':
network_for_repositories = true;
break;
case 'N':
network_for_all = true;
break;
case 's':
safeboot_for_repositories = true;
break;
case 'S':
safeboot_for_all = true;
break;
case 't':
do_tests = true;
break;
case 'v':
{
const char * next;
if (verbose_level > 0) {
fprintf(stderr, "duplicate use of -v option\n\n");
usage(0);
exit_code = 1;
goto finish;
}
if (optind >= argc) {
verbose_level = 1;
} else {
next = argv[optind];
if ((next[0] == '1' || next[0] == '2' ||
next[0] == '3' || next[0] == '4' ||
next[0] == '5' || next[0] == '6') &&
next[1] == '\0') {
verbose_level = atoi(next);
optind++;
} else {
verbose_level = 1;
}
}
}
break;
case 'z':
pretend_authentic = true;
break;
case 'h':
usage(2);
exit_code = 0;
goto finish;
default:
fprintf(stderr, "unknown option -%c\n", optchar);
usage(0);
exit_code = 1;
goto finish;
}
}
argc -= optind;
argv += optind;
if (!mkextFilename && !kernelCacheFilename && !repositoryCaches) {
fprintf(stderr, "no work to do; one of -m, -c or -k must be specified\n");
usage(0);
exit_code = 1;
goto finish;
}
for (i = 0; i < argc; i++) {
CFStringRef argString = CFStringCreateWithCString(kCFAllocatorDefault,
argv[i], kCFStringEncodingMacRoman);
if (!argString) {
fprintf(stderr, "memory allocation failure\n");
exit_code = 1;
goto finish;
}
if (CFStringHasSuffix(argString, CFSTR(".kext")) ||
CFStringHasSuffix(argString, CFSTR(".kext/")) ) {
CFArrayAppendValue(kextNames, argString);
}
else
{
if (!CFArrayGetCount(repositoryDirectories))
{
source_extensions = argv[i];
have_extensions_time = (0 == stat(source_extensions, &extensions_stat_buf));
if (have_kernel_time || have_extensions_time)
{
cacheFileTimes = _cacheFileTimes;
if (!have_kernel_time
|| (have_extensions_time && (extensions_stat_buf.st_mtime > kernel_stat_buf.st_mtime)))
{
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &extensions_stat_buf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &extensions_stat_buf.st_mtimespec);
}
else
{
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &kernel_stat_buf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &kernel_stat_buf.st_mtimespec);
}
cacheFileTimes[1].tv_sec++;
}
}
CFArrayAppendValue(repositoryDirectories, argString);
}
CFRelease(argString);
}
if (CFArrayGetCount(kextNames) == 0 &&
CFArrayGetCount(repositoryDirectories) == 0) {
fprintf(stderr, "no kernel extensions or directories specified\n\n");
usage(0);
exit_code = 1;
goto finish;
}
if (forkExit) {
if (verbose_level >= 1) {
verbose_log("forking a child process to do the work\n");
}
switch (fork()) {
case -1:
fprintf(stderr, "cannot fork\n");
exit_code = 1;
goto finish;
break;
case 0: setpriority(PRIO_PROCESS, getpid(), 20); if (include_kernel_requests) {
kern_return_t kern_result;
mach_timespec_t waitTime = { 40, 0 };
kern_result = IOKitWaitQuiet(kIOMasterPortDefault, &waitTime);
if (kern_result == kIOReturnTimeout) {
fprintf(stderr, "IOKitWaitQuiet() timed out");
} else if (kern_result != kIOReturnSuccess) {
fprintf(stderr, "IOKitWaitQuiet() failed with result code %x",
kern_result);
}
sleep(40);
}
break;
default: exit_code = 0;
goto finish;
break;
}
}
if (include_kernel_requests)
get_catalog_demand_lists(&kernel_requests, &kernel_cache_misses, verbose_level);
if (cache_looks_uptodate)
{
CFShow(kernel_cache_misses);
CFShow(kernel_requests);
if ((!kernel_cache_misses || !kernel_requests)
|| (!CFSetGetCount(kernel_cache_misses))
|| (CFSetGetCount(kernel_cache_misses) == CFSetGetCount(kernel_requests)))
{
if (verbose_level >= 1) {
verbose_log("cache %s up to date", kernelCacheFilename);
}
exit(0);
}
}
theKextManager = KXKextManagerCreate(kCFAllocatorDefault);
if (!theKextManager) {
fprintf(stderr, "can't allocate kernel extension manager\n");
exit_code = 1;
goto finish;
}
result = KXKextManagerInit(theKextManager, load_in_task,
false );
if (result != kKXKextManagerErrorNone) {
fprintf(stderr, "can't initialize kernel extension manager (%s)\n",
KXKextManagerErrorStaticCStringForError(result));
exit_code = 1;
goto finish;
}
KXKextManagerSetPerformsFullTests(theKextManager, do_tests);
KXKextManagerSetPerformsStrictAuthentication(theKextManager, true);
KXKextManagerSetLogLevel(theKextManager, verbose_level);
KXKextManagerSetLogFunction(theKextManager, &verbose_log);
KXKextManagerSetErrorLogFunction(theKextManager, &error_log);
KXKextManagerSetUserVetoFunction(theKextManager, &user_approve);
KXKextManagerSetUserApproveFunction(theKextManager, &user_approve);
KXKextManagerSetUserInputFunction(theKextManager, &user_input);
KXKextManagerDisableClearRelationships(theKextManager);
count = CFArrayGetCount(repositoryDirectories);
for (i = 0; i < count; i++) {
CFURLRef directoryURL = NULL; KXKextRepositoryRef repository = NULL; CFArrayRef candidateKexts = NULL; CFArrayRef badKexts = NULL;
CFStringRef directory = (CFStringRef)CFArrayGetValueAtIndex(
repositoryDirectories, i);
directoryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
directory, kCFURLPOSIXPathStyle, true);
if (!directoryURL) {
char repositoryPath[MAXPATHLEN];
if (!CFStringGetCString(directory, repositoryPath,
sizeof(repositoryPath) / sizeof(char), kCFStringEncodingMacRoman)) {
fprintf(stderr, "string conversion error\n");
} else {
fprintf(stderr, "'%s': no such directory\n", repositoryPath);
}
exit_code = 1;
goto finish;
}
result = KXKextManagerAddRepositoryDirectory(theKextManager,
directoryURL, true ,
false , &repository);
if (result != kKXKextManagerErrorNone) {
fprintf(stderr, "can't add repository (%s).\n",
KXKextManagerErrorStaticCStringForError(result));
continue;
}
CFArrayAppendValue(repositories, repository);
candidateKexts = KXKextRepositoryCopyCandidateKexts(repository);
if (candidateKexts) {
CFArrayAppendArray(repositoryKexts, candidateKexts,
CFRangeMake(0, CFArrayGetCount(candidateKexts)));
CFRelease(candidateKexts);
candidateKexts = NULL;
}
badKexts = KXKextRepositoryCopyBadKexts(repository);
if (badKexts) {
CFArrayAppendArray(repositoryKexts, badKexts,
CFRangeMake(0, CFArrayGetCount(badKexts)));
CFRelease(badKexts);
badKexts = NULL;
}
CFRelease(directoryURL);
directoryURL = NULL;
}
if (!addKextsToManager(theKextManager, kextNames, kextNamesToUse, do_tests)) {
exit_code = 1;
goto finish;
}
if (pretend_authentic) {
KXKextManagerMarkKextsAuthentic(theKextManager);
} else if (do_tests) {
KXKextManagerAuthenticateKexts(theKextManager);
}
KXKextManagerEnableClearRelationships(theKextManager);
KXKextManagerCalculateVersionRelationships(theKextManager);
KXKextManagerResolveAllKextDependencies(theKextManager);
if (repositoryCaches) {
count = CFArrayGetCount(repositories);
for (i = 0; i < count; i++) {
KXKextRepositoryRef repository = (KXKextRepositoryRef)
CFArrayGetValueAtIndex(repositories, i);
KXKextRepositoryWriteCache(
repository, NULL);
}
}
if (!mkextFilename && !kernelCacheFilename) {
goto finish;
}
count = CFArrayGetCount(kextNamesToUse);
for (i = 0; i < count; i++) {
CFStringRef kextName = CFArrayGetValueAtIndex(kextNamesToUse, i);
CFURLRef kextURL = NULL; KXKextRef thisKext = NULL;
kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
kextName, kCFURLPOSIXPathStyle, true);
if (!kextURL) {
fprintf(stderr, "memory allocation failure\n");
exit_code = 1;
goto finish;
}
thisKext = KXKextManagerGetKextWithURL(theKextManager,
kextURL);
CFRelease(kextURL);
if (!thisKext) {
fprintf(stderr, "internal error; kext not found\n");
exit_code = 1;
goto finish;
}
CFArrayAppendValue(namedKexts, thisKext);
}
if (nArchs == 0) {
archs[nArchs++] = &archAny;
}
if (kernelCacheFilename)
{
KXKextManagerError err;
const NXArchInfo * hostArch;
hostArch = NXGetLocalArchInfo();
if (!hostArch) {
fprintf(stderr, "can't determine the host architecture\n");
exit_code = 1;
goto domkext;
}
if((nArchs > 1) ||
((archs[0]->cputype != CPU_TYPE_ANY) && (archs[0]->cputype != hostArch->cputype))) {
fprintf(stderr, "can't create fat cache\n");
exit_code = 1;
goto domkext;
}
CFDictionaryRemoveAllValues(checkDictionary);
collectKextsForMkextCache(namedKexts, checkDictionary,
hostArch->cputype, hostArch->cpusubtype,
local_for_all, network_for_all, safeboot_for_all,
kernel_requests,
verbose_level, do_tests);
collectKextsForMkextCache(repositoryKexts, checkDictionary,
hostArch->cputype, hostArch->cpusubtype,
local_for_repositories || local_for_all,
network_for_repositories || network_for_all,
safeboot_for_repositories || safeboot_for_all,
kernel_requests,
verbose_level, do_tests);
all_plists = false;
if (!include_kernel_requests) {
platform_name_root_path.root_path[0] = platform_name_root_path.platform_name[0] = 0;
all_plists = !( local_for_repositories || local_for_all
|| network_for_repositories || network_for_all
|| safeboot_for_repositories || safeboot_for_all);
}
err = PreLink(theKextManager, checkDictionary, kernelCacheFilename, cacheFileTimes, kernel_file,
platform_name_root_path.platform_name, platform_name_root_path.root_path,
kernel_requests, all_plists,
hostArch, verbose_level, debug_mode);
if (kKXKextManagerErrorNone != err)
{
exit_code = 1;
goto domkext;
}
}
domkext:
if (!mkextFilename) {
goto finish;
}
temp_dir = getenv("TMPDIR");
if (!temp_dir)
temp_dir = "/tmp";
temp_file = malloc(strlen(temp_dir) + strlen(TEMP_DIR) + 1);
strcpy(temp_file, temp_dir);
strcat(temp_file, TEMP_DIR);
mktemp(temp_file);
fd = open(temp_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (-1 == fd) {
fprintf(stderr, "can't create %s - %s\n", temp_file,
strerror(errno));
result = false;
goto finish; }
output_filename = temp_file;
struct fat_header fatHeader;
struct fat_arch fatArchs[kMaxArchs];
unsigned long fat_offset = 0;
ssize_t bytes_written = 0;
if (nArchs > 1) {
fat_offset = sizeof(struct fat_header) + (sizeof(struct fat_arch) * nArchs);
lseek(fd, fat_offset, SEEK_SET);
}
for (i=0; i<nArchs; i++) {
fatArchs[i].cputype = NXSwapHostLongToBig(archs[i]->cputype);
fatArchs[i].cpusubtype = NXSwapHostLongToBig(archs[i]->cpusubtype);
fatArchs[i].offset = NXSwapHostLongToBig(fat_offset);
if (verbose_level >= 1) {
verbose_log("processing architecture %s", archs[i]->name);
}
CFDictionaryRemoveAllValues(checkDictionary);
collectKextsForMkextCache(namedKexts, checkDictionary,
archs[i]->cputype, archs[i]->cpusubtype,
local_for_all, network_for_all, safeboot_for_all,
NULL ,
verbose_level, do_tests);
collectKextsForMkextCache(repositoryKexts, checkDictionary,
archs[i]->cputype, archs[i]->cpusubtype,
local_for_repositories || local_for_all,
network_for_repositories || network_for_all,
safeboot_for_repositories || safeboot_for_all,
NULL ,
verbose_level, do_tests);
bytes_written = createMkextArchive(fd, checkDictionary, output_filename,
archs[i]->name, archs[i]->cputype, archs[i]->cpusubtype, verbose_level);
if (bytes_written < 0) {
exit_code = 1;
goto finish;
}
fat_offset += bytes_written;
fatArchs[i].size = NXSwapHostLongToBig(bytes_written);
fatArchs[i].align = NXSwapHostLongToBig(0);
}
if (checkMkextArchiveSize(fat_offset) == false) {
fprintf(stderr, "archive would be too large; aborting\n");
exit_code = 1;
goto finish;
}
if (nArchs > 1) {
lseek(fd, 0, SEEK_SET);
fatHeader.magic = NXSwapHostLongToBig(FAT_MAGIC);
fatHeader.nfat_arch = NXSwapHostLongToBig(nArchs);
bytes_written = write(fd, &fatHeader, sizeof(fatHeader));
if (bytes_written != sizeof(fatHeader)) {
perror("write");
exit_code = 1;
goto finish;
}
for (i=0; i<nArchs; i++) {
bytes_written = write(fd, &fatArchs[i], sizeof(fatArchs[i]));
if (bytes_written != sizeof(fatArchs[i])) {
perror("write");
exit_code = 1;
goto finish;
}
}
}
close(fd);
fd = -1;
if (have_extensions_time)
{
struct timespec mod_time = extensions_stat_buf.st_mtimespec;
if ((0 == stat(source_extensions, &extensions_stat_buf))
&& ((mod_time.tv_sec != extensions_stat_buf.st_mtimespec.tv_sec)
|| (mod_time.tv_nsec != extensions_stat_buf.st_mtimespec.tv_nsec)))
{
fprintf(stderr, "cache stale - not creating %s\n", mkextFilename);
exit_code = 1;
goto finish;
}
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &extensions_stat_buf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &extensions_stat_buf.st_mtimespec);
cacheFileTimes[1].tv_sec++;
}
if (-1 == rename(output_filename, mkextFilename)) {
fprintf(stderr, "can't create file %s: %s\n", mkextFilename,
strerror(errno));
exit_code = 1;
goto finish;
}
output_filename = NULL;
if (cacheFileTimes && (-1 == utimes(mkextFilename, cacheFileTimes))) {
fprintf(stderr, "can't set file times %s: %s\n", mkextFilename,
strerror(errno));
exit_code = 1;
goto finish;
}
finish:
if (-1 != fd) close(fd);
if (output_filename)
{
if (-1 == unlink(output_filename)) {
fprintf(stderr, "can't remove file %s - %s\n", output_filename,
strerror(errno));
}
}
if (temp_file) free(temp_file);
if (repositoryDirectories) CFRelease(repositoryDirectories);
if (kextNames) CFRelease(kextNames);
if (kextNamesToUse) CFRelease(kextNamesToUse);
if (namedKexts) CFRelease(namedKexts);
if (repositoryKexts) CFRelease(repositoryKexts);
if (checkDictionary) CFRelease(checkDictionary);
if (theKextManager) CFRelease(theKextManager);
exit(exit_code);
return exit_code;
}
static void get_catalog_demand_lists(CFMutableSetRef * kernel_requests,
CFSetRef * kernel_cache_misses,
int verbose_level)
{
kern_return_t kr;
char * propertiesBuffer;
int loaded_bytecount;
kr = IOCatalogueGetData(MACH_PORT_NULL, kIOCatalogGetModuleDemandList,
&propertiesBuffer, &loaded_bytecount);
if (kIOReturnSuccess == kr)
{
CFSetRef set;
set = (CFSetRef)
IOCFUnserialize(propertiesBuffer, kCFAllocatorDefault, 0, 0);
vm_deallocate(mach_task_self(), (vm_address_t) propertiesBuffer, loaded_bytecount);
*kernel_requests = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, set);
CFRelease(set);
}
kr = IOCatalogueGetData(MACH_PORT_NULL, kIOCatalogGetCacheMissList,
&propertiesBuffer, &loaded_bytecount);
if (kIOReturnSuccess == kr)
{
if (verbose_level > 0)
verbose_log("cache misses:\n%s", propertiesBuffer);
*kernel_cache_misses = (CFSetRef)
IOCFUnserialize(propertiesBuffer, kCFAllocatorDefault, 0, 0);
vm_deallocate(mach_task_self(), (vm_address_t) propertiesBuffer, loaded_bytecount);
}
}
char * CFURLCopyCString(CFURLRef anURL)
{
char * string = NULL; CFIndex bufferLength;
CFURLRef absURL = NULL; CFStringRef urlString = NULL; Boolean error = false;
absURL = CFURLCopyAbsoluteURL(anURL);
if (!absURL) {
goto finish;
}
urlString = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
if (!urlString) {
goto finish;
}
bufferLength = 1 + CFStringGetLength(urlString);
string = (char *)malloc(bufferLength * sizeof(char));
if (!string) {
goto finish;
}
if (!CFStringGetCString(urlString, string, bufferLength,
kCFStringEncodingMacRoman)) {
error = true;
goto finish;
}
finish:
if (error) {
free(string);
string = NULL;
}
if (absURL) CFRelease(absURL);
if (urlString) CFRelease(urlString);
return string;
}
static void addKextForMkextCache(
KXKextRef theKext,
CFMutableDictionaryRef checkDictionary,
cpu_type_t archCPU,
cpu_subtype_t archSubtype,
Boolean local,
Boolean network,
Boolean safeboot,
CFSetRef kernel_requests,
int verbose_level,
Boolean do_tests)
{
Boolean include_it = true; Boolean do_children = true;
CFStringRef kextName = NULL; char kext_name[MAXPATHLEN] = "";
CFBundleRef kextBundle = NULL; CFStringRef requiredString = NULL;
CFURLRef executableURL = NULL; char * executable_path = NULL;
int fd = -1; caddr_t machO = (caddr_t)-1; off_t machOSize = 0;
kextName = KXKextCopyAbsolutePath(theKext);
if (!kextName) {
fprintf(stderr, "memory allocation failure\n");
exit(1);
}
if (!CFStringGetCString(kextName, kext_name,
sizeof(kext_name) / sizeof(char), kCFStringEncodingMacRoman)) {
fprintf(stderr, "string conversion failure\n");
include_it = false;
do_children = false;
goto finish;
}
if (CFDictionaryGetValue(checkDictionary, kextName)) {
include_it = false;
do_children = true;
goto finish;
}
if (!KXKextIsValid(theKext)) {
fprintf(stderr, "kernel extension %s is not valid; "
"skipping it and any plugins\n", kext_name);
if (do_tests) {
fprintf(stderr, "kext diagnostics:\n");
KXKextPrintDiagnostics(theKext, stderr);
}
include_it = false;
do_children = false;
goto finish;
}
kextBundle = KXKextGetBundle(theKext);
include_it = false;
do_children = true;
if (kernel_requests) {
CFStringRef bundleIdentifier = CFBundleGetIdentifier(kextBundle);
if (!bundleIdentifier) {
fprintf(stderr, "can't get identifier for kext\n");
do_children = false;
goto finish;
}
include_it = CFSetContainsValue(kernel_requests, bundleIdentifier);
} else if (!local && !network && !safeboot)
include_it = true;
if (!include_it && (local || network || safeboot)) {
requiredString = CFBundleGetValueForInfoDictionaryKey(kextBundle,
CFSTR("OSBundleRequired"));
if (!requiredString) {
if (verbose_level >= 3) {
verbose_log("skipping bundle %s; no OSBundleRequired key "
"(still checking plugins)\n",
kext_name);
}
include_it = false;
do_children = true;
goto finish;
} else if (CFStringCompare(requiredString,
CFSTR("Root"), 0) == kCFCompareEqualTo ||
CFStringCompare(requiredString,
CFSTR("Console"), 0) == kCFCompareEqualTo) {
include_it = true;
do_children = true;
} else if (local &&
CFStringCompare(requiredString, CFSTR("Local-Root"), 0) ==
kCFCompareEqualTo) {
include_it = true;
do_children = true;
} else if (network &&
CFStringCompare(requiredString, CFSTR("Network-Root"), 0) ==
kCFCompareEqualTo) {
include_it = true;
do_children = true;
} else if (safeboot &&
CFStringCompare(requiredString, CFSTR("Safe Boot"), 0) ==
kCFCompareEqualTo) {
include_it = true;
do_children = true;
} else {
char required_string[120];
if (!CFStringGetCString(requiredString, required_string,
sizeof(required_string) / sizeof(char),
kCFStringEncodingASCII)) {
strcpy(required_string, "(unknown)");
}
if (verbose_level >= 3) {
verbose_log(
"skipping bundle %s; OSBundleRequired key is \"%s\" "
"(still checking plugins)\n",
kext_name, required_string);
}
include_it = false;
do_children = true;
goto finish;
}
}
if (!KXKextHasBeenAuthenticated(theKext)) {
KXKextAuthenticate(theKext);
}
if (!KXKextIsAuthentic(theKext)) {
fprintf(stderr, "kernel extension %s is not authentic (check ownership and permissions); "
"skipping it and any plugins\n", kext_name);
if (do_tests) {
fprintf(stderr, "kext diagnostics:\n");
KXKextPrintDiagnostics(theKext, stderr);
}
include_it = false;
do_children = false;
goto finish;
}
KXKextResolveDependencies(theKext);
if (!KXKextGetHasAllDependencies(theKext)) {
fprintf(stderr, "warning: kernel extension %s is missing dependencies "
"(including in cache anyway; "
"dependencies may be available from elsewhere)\n",
kext_name);
if (do_tests) {
fprintf(stderr, "kext diagnostics:\n");
KXKextPrintDiagnostics(theKext, stderr);
}
include_it = true;
do_children = true;
}
if (KXKextGetDeclaresExecutable(theKext)) {
executableURL = CFBundleCopyExecutableURL(kextBundle);
if (!executableURL) {
fprintf(stderr, "skipping bundle %s; "
" declares an exectuable but has none "
"(still checking plugins)\n",
kext_name);
include_it = false;
do_children = true;
goto finish;
} else {
executable_path = CFURLCopyCString(executableURL);
if (!executable_path) {
fprintf(stderr, "memory allocation failure\n");
exit(1);
} else {
struct stat stat_buf;
off_t archSize;
fd = open(executable_path, O_RDONLY, 0);
if (-1 == fd) { fprintf(stderr, "can't open file %s; "
"skipping the kext that contains it\n",
executable_path);
include_it = false;
do_children = true;
goto finish;
}
if (fstat(fd, &stat_buf) < 0) {
fprintf(stderr, "can't get size of file %s; "
"skipping the kext that contains it\n",
executable_path);
include_it = false;
do_children = true;
goto finish;
}
machOSize = stat_buf.st_size;
machO = mmap(NULL, machOSize,
PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE,
fd, 0 );
if (machO == (caddr_t)-1) {
fprintf(stderr, "can't map file %s; "
"skipping the kext that contains it\n",
executable_path);
include_it = false;
do_children = true;
goto finish;
}
find_arch(NULL, &archSize, archCPU, archSubtype,
machO, machOSize);
if (!archSize) { fprintf(stderr, "%s doesn't contain code for the "
"architecture specified; "
"skipping the kext that contains it\n",
executable_path);
include_it = false;
do_children = true;
goto finish;
}
}
}
}
finish:
if (machO != (caddr_t)-1) {
munmap(machO, machOSize);
}
if (fd != -1) {
close(fd);
}
if (include_it && !CFDictionaryGetValue(checkDictionary, kextName)) {
CFDictionarySetValue(checkDictionary, kextName, theKext);
}
if (do_children) {
collectKextsForMkextCache(KXKextGetPlugins(theKext),
checkDictionary, archCPU, archSubtype, local, network, safeboot, kernel_requests,
verbose_level, do_tests);
}
if (kextName) CFRelease(kextName);
if (executableURL) CFRelease(executableURL);
if (executable_path) free(executable_path);
return;
}
static void collectKextsForMkextCache(
CFArrayRef kexts,
CFMutableDictionaryRef checkDictionary,
cpu_type_t archCPU,
cpu_subtype_t archSubtype,
Boolean local,
Boolean network,
Boolean safeboot,
CFSetRef kernel_requests,
int verbose_level,
Boolean do_tests)
{
CFIndex count, i;
KXKextRef theKext = NULL; if (!kexts) {
goto finish;
}
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
theKext = (KXKextRef)CFArrayGetValueAtIndex(kexts, i);
addKextForMkextCache(theKext, checkDictionary, archCPU, archSubtype,
local, network, safeboot, kernel_requests, verbose_level, do_tests);
}
finish:
return;
}
__private_extern__ void verbose_log(const char * format, ...)
{
va_list ap;
char fake_buffer[2];
int output_length;
char * output_string;
va_start(ap, format);
output_length = vsnprintf(fake_buffer, 1, format, ap);
va_end(ap);
output_string = (char *)malloc(output_length + 1);
if (!output_string) {
fprintf(stderr, "memory allocation failure\n");
return;
}
va_start(ap, format);
vsprintf(output_string, format, ap);
va_end(ap);
va_start(ap, format);
fprintf(stdout, "%s: %s\n", progname, output_string);
va_end(ap);
free(output_string);
return;
}
static void error_log(const char * format, ...)
{
va_list ap;
char fake_buffer[2];
int output_length;
char * output_string;
va_start(ap, format);
output_length = vsnprintf(fake_buffer, 1, format, ap);
va_end(ap);
output_string = (char *)malloc(output_length + 1);
if (!output_string) {
fprintf(stderr, "memory allocation failure\n");
return;
}
va_start(ap, format);
vsprintf(output_string, format, ap);
va_end(ap);
va_start(ap, format);
fprintf(stderr, "%s: %s\n", progname, output_string);
va_end(ap);
free(output_string);
return;
}
static int user_approve(int default_answer, const char * format, ...)
{
int result = 1;
va_list ap;
char fake_buffer[2];
int output_length;
char * output_string;
char * prompt_string = NULL;
int c, x;
va_start(ap, format);
output_length = vsnprintf(fake_buffer, 1, format, ap);
va_end(ap);
output_string = (char *)malloc(output_length + 1);
if (!output_string) {
fprintf(stderr, "memory allocation failure\n");
result = -1;
goto finish;
}
va_start(ap, format);
vsprintf(output_string, format, ap);
va_end(ap);
prompt_string = default_answer ? " [Y/n]" : " [y/N]";
while ( 1 ) {
fprintf(stdout, "%s%s%s", output_string, prompt_string, "? ");
fflush(stdout);
c = fgetc(stdin);
if (c == EOF) {
result = -1;
goto finish;
}
if ( c != '\n' ) {
do {
x = fgetc(stdin);
} while (x != '\n' && x != EOF);
if (x == EOF) {
result = -1;
goto finish;
}
}
if (c == '\n') {
result = default_answer ? 1 : 0;
goto finish;
} else if (tolower(c) == 'y') {
result = 1;
goto finish;
} else if (tolower(c) == 'n') {
result = 0;
goto finish;
}
}
finish:
if (output_string) free(output_string);
return result;
}
static const char * user_input(const char * format, ...)
{
char * result = NULL; va_list ap;
char fake_buffer[2];
int output_length;
char * output_string = NULL;
unsigned index;
size_t size = 80;
int c;
result = (char *)malloc(size);
if (!result) {
goto finish;
}
index = 0;
va_start(ap, format);
output_length = vsnprintf(fake_buffer, 1, format, ap);
va_end(ap);
output_string = (char *)malloc(output_length + 1);
if (!output_string) {
fprintf(stderr, "memory allocation failure\n");
result = NULL;
goto finish;
}
va_start(ap, format);
vsprintf(output_string, format, ap);
va_end(ap);
fprintf(stdout, "%s ", output_string);
fflush(stdout);
c = fgetc(stdin);
while (c != '\n' && c != EOF) {
if (index >= size) {
size += 80;
result = realloc(result, size);
if (!result) {
goto finish;
}
}
result[index++] = (char)c;
c = fgetc(stdin);
}
if (c == EOF) {
if (result) free(result);
result = NULL;
goto finish;
}
finish:
if (output_string) free(output_string);
return result;
}
static Boolean addKextsToManager(
KXKextManagerRef aManager,
CFArrayRef kextNames,
CFMutableArrayRef kextNamesToUse,
Boolean do_tests)
{
Boolean result = true; KXKextManagerError kxresult = kKXKextManagerErrorNone;
CFIndex i, count;
KXKextRef theKext = NULL; CFURLRef kextURL = NULL;
count = CFArrayGetCount(kextNames);
for (i = 0; i < count; i++) {
char name_buffer[MAXPATHLEN];
CFStringRef kextName = (CFStringRef)CFArrayGetValueAtIndex(
kextNames, i);
if (kextURL) {
CFRelease(kextURL);
kextURL = NULL;
}
kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
kextName, kCFURLPOSIXPathStyle, true);
if (!kextURL) {
fprintf(stderr, "memory allocation failure\n");
result = false;
goto finish;
}
if (!CFStringGetCString(kextName,
name_buffer, sizeof(name_buffer) - 1, kCFStringEncodingMacRoman)) {
fprintf(stderr, "memory allocation or string conversion error\n");
result = false;
goto finish;
}
kxresult = KXKextManagerAddKextWithURL(aManager, kextURL, true, &theKext);
if (kxresult != kKXKextManagerErrorNone) {
fprintf(stderr, "can't add kernel extension %s (%s)",
name_buffer, KXKextManagerErrorStaticCStringForError(kxresult));
#if 0
if (do_tests && theKext) {
fprintf(stderr, "kernel extension problems:\n");
KXKextPrintDiagnostics(theKext, stderr);
}
continue;
#endif 0
}
if (kextNamesToUse && theKext &&
(kxresult == kKXKextManagerErrorNone || do_tests)) {
CFArrayAppendValue(kextNamesToUse, kextName);
}
}
finish:
if (kextURL) CFRelease(kextURL);
return result;
}
static void usage(int level)
{
fprintf(stderr,
"usage: %s [-a arch] [-c kernel_cache_filename] [-e] [-F] [-h] [-k]\n"
" [-K kernel_filename] [-l | -L] [-r] [-m mkext_filename] [-n | -N]\n"
" [-r] [-s | -S] [-t] [-v [1-6]] [-z] [kext_or_directory] ...\n\n",
progname);
if (level < 1) {
return;
}
if (level == 1) {
fprintf(stderr, "use %s -h for an explanation of each option\n",
progname);
return;
}
fprintf(stderr,
" kext_or_directory: Add the kext or all kexts in directory to cache\n"
" (required unless using -e)\n");
fprintf(stderr,
" -a arch: Add only kexts that contain code for arch\n");
fprintf(stderr,
" -c kernel_cache_filename: Create an kernel prelink cache\n");
fprintf(stderr,
" -e: Create or update the mkext cache for /System/Library/Extensions\n");
fprintf(stderr,
" -F: Fork and exit immediately (to work in background)\n");
fprintf(stderr,
" -h: This usage statement\n");
fprintf(stderr,
" -k: Create or update any repository .kextcache files\n");
fprintf(stderr,
" -K kernel_filename: Name of kernel file for kernel cache\n");
fprintf(stderr,
" -l: Add local-root kexts in directories to an mkext cache\n");
fprintf(stderr,
" -L: Add local-root kexts for all args to an mkext cache\n");
fprintf(stderr,
" -m mkext_filename: Create an mkext archive\n");
fprintf(stderr,
" -n: Add network-root kexts in directories to an mkext cache\n");
fprintf(stderr,
" -N: Add network-root kexts for all args to an mkext cache\n");
fprintf(stderr,
" -r: Add kexts previously loaded by this machine\n");
fprintf(stderr,
" -s: Add safe boot kexts in directories to an mkext cache\n");
fprintf(stderr,
" -S: Add safe boot kexts for all args to an mkext cache\n");
fprintf(stderr,
" -t: Perform diagnostic tests on kexts and print results\n");
fprintf(stderr,
" -v: verbose mode; print info about caching process\n");
fprintf(stderr,
" -z: don't authenticate kexts (for use during development)\n");
fprintf(stderr, "\n");
fprintf(stderr, "At least one of -k, -c or -m must be specified.\n");
fprintf(stderr,
"-l/-L and -n/-N may both be specified to make a cache of local-\n"
"and network-root kexts\n");
return;
}