CFDataRef createMkext1ForArch(const NXArchInfo * arch, CFArrayRef archiveKexts, boolean_t compress) { CFMutableDataRef result = NULL; CFMutableDictionaryRef kextsByIdentifier = NULL; Mkext1Context context; mkext1_header * mkextHeader = NULL; // do not free const uint8_t * adler_point = 0; CFIndex count, i; result = CFDataCreateMutable(kCFAllocatorDefault, /* capaacity */ 0); if (!result || !createCFMutableDictionary(&kextsByIdentifier)) { OSKextLogMemError(); goto finish; } /* mkext1 can only contain 1 kext for a given bundle identifier, so we * have to pick out the most recent versions. */ count = CFArrayGetCount(archiveKexts); for (i = 0; i < count; i++) { OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(archiveKexts, i); CFStringRef bundleIdentifier = OSKextGetIdentifier(theKext); OSKextRef savedKext = (OSKextRef)CFDictionaryGetValue(kextsByIdentifier, bundleIdentifier); OSKextVersion thisVersion, savedVersion; if (!OSKextSupportsArchitecture(theKext, arch)) { continue; } if (!savedKext) { CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext); continue; } thisVersion = OSKextGetVersion(theKext); savedVersion = OSKextGetVersion(savedKext); if (thisVersion > savedVersion) { CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext); } } /* Add room for the mkext header and kext descriptors. */ CFDataSetLength(result, sizeof(mkext1_header) + CFDictionaryGetCount(kextsByIdentifier) * sizeof(mkext_kext)); context.mkext = result; context.kextIndex = 0; context.compressOffset = (uint32_t)CFDataGetLength(result); context.arch = arch; context.fatal = false; context.compress = compress; CFDictionaryApplyFunction(kextsByIdentifier, addToMkext1, &context); if (context.fatal) { SAFE_RELEASE_NULL(result); goto finish; } mkextHeader = (mkext1_header *)CFDataGetBytePtr(result); mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC); mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN); mkextHeader->version = OSSwapHostToBigInt32(0x01008000); // 'vers' 1.0.0 mkextHeader->numkexts = OSSwapHostToBigInt32((__uint32_t)CFDictionaryGetCount(kextsByIdentifier)); mkextHeader->cputype = OSSwapHostToBigInt32(arch->cputype); mkextHeader->cpusubtype = OSSwapHostToBigInt32(arch->cpusubtype); mkextHeader->length = OSSwapHostToBigInt32((__uint32_t)CFDataGetLength(result)); adler_point = (UInt8 *)&mkextHeader->version; mkextHeader->adler32 = OSSwapHostToBigInt32(local_adler32( (UInt8 *)&mkextHeader->version, (int)(CFDataGetLength(result) - (adler_point - (uint8_t *)mkextHeader)))); OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag, "Created mkext for %s containing %lu kexts.", arch->name, CFDictionaryGetCount(kextsByIdentifier)); finish: SAFE_RELEASE(kextsByIdentifier); return result; }
ExitStatus readArgs(int argc, char * const * argv, KextstatArgs * toolArgs) { ExitStatus result = EX_USAGE; CFStringRef scratchString = NULL; // must release int optChar = 0; bzero(toolArgs, sizeof(*toolArgs)); /***** * Allocate collection objects needed for command line argument processing. */ if (!createCFMutableArray(&toolArgs->bundleIDs, &kCFTypeArrayCallBacks)) { goto finish; } /***** * Process command-line arguments. */ result = EX_USAGE; while ((optChar = getopt_long_only(argc, argv, kOptChars, sOptInfo, NULL)) != -1) { SAFE_RELEASE_NULL(scratchString); switch (optChar) { case kOptHelp: usage(kUsageLevelFull); result = kKextstatExitHelp; goto finish; break; case kOptNoKernelComponents: toolArgs->flagNoKernelComponents = true; break; case kOptListOnly: toolArgs->flagListOnly = true; break; case kOptBundleIdentifier: scratchString = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8); if (!scratchString) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFArrayAppendValue(toolArgs->bundleIDs, scratchString); break; case kOptArchitecture: toolArgs->flagShowArchitecture = true; break; } } argc -= optind; argv += optind; if (argc) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Extra arguments starting at %s....", argv[0]); usage(kUsageLevelBrief); goto finish; } result = EX_OK; finish: SAFE_RELEASE_NULL(scratchString); if (result == EX_USAGE) { usage(kUsageLevelBrief); } return result; }
/********************************************************************* * Internal function for use by KextManagerLoadKextWithIdentifier() * and KextManagerLoadKextWithURL(). *********************************************************************/ OSReturn __KextManagerSendLoadKextRequest( CFMutableDictionaryRef requestDict, CFArrayRef dependencyKextAndFolderURLs) { OSReturn result = kOSReturnError; mach_port_t kextd_port = MACH_PORT_NULL; CFDataRef requestData = NULL; // must release CFMutableArrayRef dependencyPaths = NULL; // must release CFURLRef dependencyAbsURL = NULL; // must release CFStringRef dependencyPath = NULL; // must release CFErrorRef error = NULL; // must release if (!requestDict) { result = kOSKextReturnInvalidArgument; goto finish; } result = get_kextd_port(&kextd_port); if (result != kOSReturnSuccess) { goto finish; } if (dependencyKextAndFolderURLs && CFArrayGetCount(dependencyKextAndFolderURLs)) { CFIndex count, index; dependencyPaths = CFArrayCreateMutable(kCFAllocatorDefault, /* capacity */ 0, &kCFTypeArrayCallBacks); if (!dependencyPaths) { result = kOSKextReturnNoMemory; goto finish; } CFDictionarySetValue(requestDict, kKextLoadDependenciesKey, dependencyPaths); count = CFArrayGetCount(dependencyKextAndFolderURLs); for (index = 0; index < count; index++) { CFURLRef thisURL = (CFURLRef)CFArrayGetValueAtIndex( dependencyKextAndFolderURLs, index); SAFE_RELEASE_NULL(dependencyPath); SAFE_RELEASE_NULL(dependencyAbsURL); dependencyAbsURL = CFURLCopyAbsoluteURL(thisURL); if (!dependencyAbsURL) { result = kOSKextReturnNoMemory; goto finish; } dependencyPath = CFURLCopyFileSystemPath(dependencyAbsURL, kCFURLPOSIXPathStyle); if (!dependencyPath) { result = kOSKextReturnNoMemory; goto finish; } CFArrayAppendValue(dependencyPaths, dependencyPath); } } requestData = CFPropertyListCreateData(kCFAllocatorDefault, requestDict, kCFPropertyListBinaryFormat_v1_0, /* options */ 0, &error); if (!requestData) { // any point in logging error reason here? nothing caller can do.... result = kOSKextReturnSerialization; goto finish; } result = kextmanager_load_kext(kextd_port, (char *)CFDataGetBytePtr(requestData), CFDataGetLength(requestData)); finish: SAFE_RELEASE(requestData); SAFE_RELEASE(dependencyPaths); SAFE_RELEASE(dependencyPath); SAFE_RELEASE(dependencyAbsURL); SAFE_RELEASE(error); return result; }
ExitStatus readArgs( int * argc, char * const ** argv, KcgenArgs * toolArgs) { ExitStatus result = EX_USAGE; ExitStatus scratchResult = EX_USAGE; CFStringRef scratchString = NULL; // must release CFNumberRef scratchNumber = NULL; // must release CFURLRef scratchURL = NULL; // must release size_t len = 0; int32_t i = 0; int optchar = 0; int longindex = -1; bzero(toolArgs, sizeof(*toolArgs)); /***** * Allocate collection objects. */ if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks) || !createCFMutableSet(&toolArgs->optionalKextIDs, &kCFTypeSetCallBacks) || !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks) || !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) || !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks) || !createCFMutableArray(&toolArgs->targetArchs, NULL)) { OSKextLogMemError(); result = EX_OSERR; exit(result); } /***** * Process command line arguments. */ while ((optchar = getopt_long_only(*argc, *argv, kOptChars, sOptInfo, &longindex)) != -1) { SAFE_RELEASE_NULL(scratchString); SAFE_RELEASE_NULL(scratchNumber); SAFE_RELEASE_NULL(scratchURL); /* When processing short (single-char) options, there is no way to * express optional arguments. Instead, we suppress missing option * argument errors by adding a leading ':' to the option string. * When getopt detects a missing argument, it will return a ':' so that * we can screen for options that are not required to have an argument. */ if (optchar == ':') { switch (optopt) { case kOptPrelinkedKernel: optchar = optopt; break; default: OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - option requires an argument -- -%c.", optopt); break; } } switch (optchar) { case kOptArch: if (!addArchForName(toolArgs, optarg)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - unknown architecture %s.", optarg); goto finish; } break; case kOptBundleIdentifier: scratchString = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8); if (!scratchString) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFSetAddValue(toolArgs->kextIDs, scratchString); break; case kOptPrelinkedKernel: scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv, /* isLongopt */ longindex != -1); if (scratchResult != EX_OK) { result = scratchResult; goto finish; } break; case kOptHelp: usage(kUsageLevelFull); result = kKcgenExitHelp; goto finish; case kOptKernel: if (toolArgs->kernelPath) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning - kernel file already specified; using last."); } else { toolArgs->kernelPath = malloc(PATH_MAX); if (!toolArgs->kernelPath) { OSKextLogMemError(); result = EX_OSERR; goto finish; } } len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX); if (len >= PATH_MAX) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - kernel filename length exceeds PATH_MAX"); goto finish; } break; case kOptTests: toolArgs->printTestResults = true; break; case kOptQuiet: beQuiet(); break; case kOptVerbose: scratchResult = setLogFilterForOpt(*argc, *argv, /* forceOnFlags */ kOSKextLogKextOrGlobalMask); if (scratchResult != EX_OK) { result = scratchResult; goto finish; } break; case kOptNoAuthentication: OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Note: -%s is implicitly set for %s.", kOptNameNoAuthentication, progname); break; case 0: switch (longopt) { case kLongOptOptionalBundleIdentifier: scratchString = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8); if (!scratchString) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFSetAddValue(toolArgs->optionalKextIDs, scratchString); break; case kLongOptCompressed: toolArgs->compress = true; break; case kLongOptUncompressed: toolArgs->uncompress = true; break; case kLongOptSymbols: if (toolArgs->symbolDirURL) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning - symbol directory already specified; using last."); SAFE_RELEASE_NULL(toolArgs->symbolDirURL); } scratchURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)optarg, strlen(optarg), true); if (!scratchURL) { OSKextLogStringError(/* kext */ NULL); result = EX_OSERR; goto finish; } toolArgs->symbolDirURL = CFRetain(scratchURL); toolArgs->generatePrelinkedSymbols = true; break; case kLongOptVolumeRoot: if (toolArgs->volumeRootURL) { OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Warning: volume root already specified; using last."); SAFE_RELEASE_NULL(toolArgs->volumeRootURL); } scratchURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)optarg, strlen(optarg), true); if (!scratchURL) { OSKextLogStringError(/* kext */ NULL); result = EX_OSERR; goto finish; } toolArgs->volumeRootURL = CFRetain(scratchURL); break; case kLongOptAllPersonalities: OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Note: -%s is implicitly set for %s.", kOptNameAllPersonalities, progname); break; case kLongOptNoLinkFailures: OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, "Note: -%s is implicitly set for %s.", kOptNameNoLinkFailures, progname); break; case kLongOptStripSymbols: toolArgs->stripSymbols = true; break; case kLongOptMaxSliceSize: toolArgs->maxSliceSize = atol(optarg); break; default: /* Because we use ':', getopt_long doesn't print an error message. */ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - unrecognized option %s", (*argv)[optind-1]); goto finish; break; } break; default: /* Because we use ':', getopt_long doesn't print an error message. */ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Error - unrecognized option %s", (*argv)[optind-1]); goto finish; break; } /* Reset longindex, because getopt_long_only() is stupid and doesn't. */ longindex = -1; } /* Update the argc & argv seen by main(). */ *argc -= optind; *argv += optind; /* * Record the kext & directory names from the command line. */ for (i = 0; i < *argc; i++) { SAFE_RELEASE_NULL(scratchURL); SAFE_RELEASE_NULL(scratchString); scratchURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true); if (!scratchURL) { OSKextLogMemError(); result = EX_OSERR; goto finish; } CFArrayAppendValue(toolArgs->argURLs, scratchURL); scratchString = CFURLCopyPathExtension(scratchURL); if (scratchString && CFEqual(scratchString, CFSTR("kext"))) { CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL); } else { CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL); } } result = EX_OK; finish: SAFE_RELEASE(scratchString); SAFE_RELEASE(scratchNumber); SAFE_RELEASE(scratchURL); if (result == EX_USAGE) { usage(kUsageLevelBrief); } return result; }