1/* 2 * Copyright (c) 2006, 2012 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <CoreFoundation/CoreFoundation.h> 24#include <CoreFoundation/CFBundlePriv.h> 25#include <errno.h> 26#include <libc.h> 27#include <libgen.h> // dirname() 28#include <sys/types.h> 29#include <sys/mman.h> 30#include <fts.h> 31#include <paths.h> 32#include <mach-o/arch.h> 33#include <mach-o/fat.h> 34#include <mach-o/loader.h> 35#include <mach-o/nlist.h> 36#include <mach-o/swap.h> 37#include <mach/mach.h> 38#include <mach/mach_error.h> 39#include <mach/mach_types.h> 40#include <mach/machine/vm_param.h> 41#include <mach/kmod.h> 42#include <notify.h> 43#include <stdlib.h> 44#include <unistd.h> // sleep(3) 45#include <sys/types.h> 46#include <sys/stat.h> 47#include <Security/SecKeychainPriv.h> 48 49#include <DiskArbitration/DiskArbitrationPrivate.h> 50#include <IOKit/IOTypes.h> 51#include <IOKit/IOKitLib.h> 52#include <IOKit/IOKitServer.h> 53#include <IOKit/IOCFUnserialize.h> 54#include <IOKit/IOCFSerialize.h> 55#include <IOKit/pwr_mgt/IOPMLib.h> 56#include <libkern/OSByteOrder.h> 57 58#include <IOKit/kext/OSKext.h> 59#include <IOKit/kext/OSKextPrivate.h> 60#include <IOKit/kext/macho_util.h> 61#include <bootfiles.h> 62 63#include <IOKit/pwr_mgt/IOPMLib.h> 64 65#include "kextcache_main.h" 66#if !NO_BOOT_ROOT 67#include "bootcaches.h" 68#include "bootroot_internal.h" 69#endif /* !NO_BOOT_ROOT */ 70#include "mkext1_file.h" 71#include "compression.h" 72#include "security.h" 73 74// constants 75#define MKEXT_PERMS (0644) 76 77/* The timeout we use when waiting for the system to get to a low load state. 78 * We're shooting for about 10 minutes, but we don't want to collide with 79 * everyone else who wants to do work 10 minutes after boot, so we just pick 80 * a number in that ballpark. 81 */ 82#define kOSKextSystemLoadTimeout (8 * 60) 83#define kOSKextSystemLoadPauseTime (30) 84 85/******************************************************************************* 86* Program Globals 87*******************************************************************************/ 88const char * progname = "(unknown)"; 89 90CFMutableDictionaryRef sNoLoadKextAlertDict = NULL; 91CFMutableDictionaryRef sInvalidSignedKextAlertDict = NULL; 92//CFMutableDictionaryRef sUnsignedKextAlertDict = NULL; 93CFMutableDictionaryRef sExcludedKextAlertDict = NULL; 94CFMutableDictionaryRef sRevokedKextAlertDict = NULL; 95 96/******************************************************************************* 97* Utility and callback functions. 98*******************************************************************************/ 99// put/take helpers 100static void waitForIOKitQuiescence(void); 101static void waitForGreatSystemLoad(void); 102 103#define kMaxArchs 64 104#define kRootPathLen 256 105 106static u_int usecs_from_timeval(struct timeval *t); 107static void timeval_from_usecs(struct timeval *t, u_int usecs); 108static void timeval_difference(struct timeval *dst, 109 struct timeval *a, struct timeval *b); 110static Boolean isValidKextSigningTargetVolume(CFURLRef theURL); 111static Boolean wantsFastLibCompressionForTargetVolume(CFURLRef theURL); 112static void _appendIfNewest(CFMutableArrayRef theArray, OSKextRef theKext); 113 114#if 1 // 17821398 115#include "safecalls.h" 116 117static Boolean needsPrelinkedKernelCopy(KextcacheArgs * toolArgs); 118static Boolean wantsPrelinkedKernelCopy(CFURLRef theVolRootURL); 119 120#define k_kernelcacheFilePath \ 121"/System/Library/Caches/com.apple.kext.caches/Startup/kernelcache" 122 123#define kPrelinkedKernelsPath "/System/Library/PrelinkedKernels" 124#define k_prelinkedkernelFilePath kPrelinkedKernelsPath "/prelinkedkernel" 125 126#endif 127 128/******************************************************************************* 129*******************************************************************************/ 130int main(int argc, char * const * argv) 131{ 132 KextcacheArgs toolArgs; 133 ExitStatus result = EX_SOFTWARE; 134 Boolean fatal = false; 135 136 /***** 137 * Find out what the program was invoked as. 138 */ 139 progname = rindex(argv[0], '/'); 140 if (progname) { 141 progname++; // go past the '/' 142 } else { 143 progname = (char *)argv[0]; 144 } 145 146 /* Set the OSKext log callback right away. 147 */ 148 OSKextSetLogOutputFunction(&tool_log); 149 150 /***** 151 * Check if we were spawned by kextd, set up straightaway 152 * for service log filtering, and hook up to ASL. 153 */ 154 if (getenv("KEXTD_SPAWNED")) { 155 OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask, 156 /* kernel? */ false); 157 OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask, 158 /* kernel? */ true); 159 tool_openlog("com.apple.kextcache"); 160 } 161 162 if (isDebugSetInBootargs()) { 163#if 0 // default to more logging when running with debug boot-args 164 OSKextLogSpec logFilter = kOSKextLogDetailLevel | 165 kOSKextLogVerboseFlagsMask | 166 kOSKextLogKextOrGlobalMask; 167 OSKextSetLogFilter(logFilter, /* kernel? */ false); 168 OSKextSetLogFilter(logFilter, /* kernel? */ true); 169#endif 170 171 /* show command and all arguments... 172 */ 173 int i; 174 int myBufSize = 0; 175 char * mybuf; 176 177 for (i = 0; i < argc; i++) { 178 myBufSize += strlen(argv[i]) + 1; 179 } 180 mybuf = malloc(myBufSize); 181 if (mybuf) { 182 mybuf[0] = 0x00; 183 for (i = 0; i < argc; i++) { 184 if (strlcat(mybuf, argv[i], myBufSize) >= myBufSize) { 185 break; 186 } 187 if (strlcat(mybuf, " ", myBufSize) >= myBufSize) { 188 break; 189 } 190 } 191 OSKextLog(NULL, 192 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 193 "%s", 194 mybuf); 195 free(mybuf); 196 } 197 } 198 199 /***** 200 * Process args & check for permission to load. 201 */ 202 result = readArgs(&argc, &argv, &toolArgs); 203 if (result != EX_OK) { 204 if (result == kKextcacheExitHelp) { 205 result = EX_OK; 206 } 207 goto finish; 208 } 209 210 /***** 211 * Now that we have a custom verbose level set by options, 212 * check the filter kextd passed in and combine them. 213 */ 214 checkKextdSpawnedFilter(/* kernel? */ false); 215 checkKextdSpawnedFilter(/* kernel? */ true); 216 217 result = checkArgs(&toolArgs); 218 if (result != EX_OK) { 219 goto finish; 220 } 221 222 /* From here on out the default exit status is ok. 223 */ 224 result = EX_OK; 225 226 /* Reduce our priority and throttle I/O, then wait for a good time to run. 227 */ 228 if (toolArgs.lowPriorityFlag) { 229 OSKextLog(/* kext */ NULL, 230 kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 231 "Running in low-priority background mode."); 232 233 setpriority(PRIO_PROCESS, getpid(), 20); // run at really low priority 234 setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE); 235 236 /* When building the prelinked kernel, we try to wait for a good time 237 * to do work. We can't do this for an mkext yet because we don't 238 * have a way to know if we're blocking reboot. 239 */ 240 if (toolArgs.prelinkedKernelPath) { 241 waitForGreatSystemLoad(); 242 } 243 } 244 245 /* The whole point of this program is to update caches, so let's not 246 * try to read any (we'll briefly turn this back on when checking them). 247 */ 248 OSKextSetUsesCaches(false); 249 250#if !NO_BOOT_ROOT 251 /* If it's a Boot!=root update or -invalidate invocation, call 252 * checkUpdateCachesAndBoots() with the previously-programmed flags 253 * and then jump to exit. These operations don't combine with 254 * more manual cache-building operations. 255 */ 256 if (toolArgs.updateVolumeURL) { 257 char volPath[PATH_MAX]; 258 259 // go ahead and do the update 260 result = doUpdateVolume(&toolArgs); 261 262 // then check for '-Boot -U /' during Safe Boot 263 if ((toolArgs.updateOpts & kBRUEarlyBoot) && 264 (toolArgs.updateOpts & kBRUExpectUpToDate) && 265 OSKextGetActualSafeBoot() && 266 CFURLGetFileSystemRepresentation(toolArgs.updateVolumeURL, 267 true, (UInt8*)volPath, PATH_MAX) 268 && 0 == strcmp(volPath, "/")) { 269 270 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 271 "Safe boot mode detected; rebuilding caches."); 272 273 // ensure kextd's caches get rebuilt later (16803220) 274 (void)utimes(kSystemExtensionsDir, NULL); 275 (void)utimes(kLibraryExtensionsDir, NULL); 276 } 277 goto finish; 278 } 279#endif /* !NO_BOOT_ROOT */ 280 281 /* If we're uncompressing the prelinked kernel, take care of that here 282 * and exit. 283 */ 284 if (toolArgs.prelinkedKernelPath && !CFArrayGetCount(toolArgs.argURLs) && 285 (toolArgs.compress || toolArgs.uncompress)) 286 { 287 result = compressPrelinkedKernel(toolArgs.volumeRootURL, 288 toolArgs.prelinkedKernelPath, 289 /* compress */ toolArgs.compress); 290 goto finish; 291 } 292 293 /***** 294 * Read the kexts we'll be working with; first the set of all kexts, then 295 * the repository and named kexts for use with mkext-creation flags. 296 */ 297 if (toolArgs.printTestResults) { 298 OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll); 299 } 300 toolArgs.allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, toolArgs.argURLs); 301 if (!toolArgs.allKexts || !CFArrayGetCount(toolArgs.allKexts)) { 302 OSKextLog(/* kext */ NULL, 303 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 304 "No kernel extensions found."); 305 result = EX_SOFTWARE; 306 goto finish; 307 } 308 309 toolArgs.repositoryKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 310 toolArgs.repositoryURLs); 311 toolArgs.namedKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 312 toolArgs.namedKextURLs); 313 if (!toolArgs.repositoryKexts || !toolArgs.namedKexts) { 314 OSKextLog(/* kext */ NULL, 315 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 316 "Error reading extensions."); 317 result = EX_SOFTWARE; 318 goto finish; 319 } 320 321 if (result != EX_OK) { 322 goto finish; 323 } 324 325 if (toolArgs.needLoadedKextInfo) { 326 result = getLoadedKextInfo(&toolArgs); 327 if (result != EX_OK) { 328 goto finish; 329 } 330 } 331 332 // xxx - we are potentially overwriting error results here 333 if (toolArgs.updateSystemCaches) { 334 result = updateSystemPlistCaches(&toolArgs); 335 // don't goto finish on error here, we might be able to create 336 // the other caches 337 } 338 339 if (toolArgs.mkextPath) { 340 result = createMkext(&toolArgs, &fatal); 341 if (fatal) { 342 goto finish; 343 } 344 } 345 346 if (toolArgs.prelinkedKernelPath) { 347 /* If we're updating the system prelinked kernel, make sure we aren't 348 * Safe Boot, or dire consequences shall result. 349 */ 350 if (toolArgs.needDefaultPrelinkedKernelInfo && 351 OSKextGetActualSafeBoot()) { 352 353 OSKextLog(/* kext */ NULL, 354 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 355 "Can't update the system prelinked kernel during safe boot."); 356 result = EX_OSERR; 357 goto finish; 358 } 359 360 /* Create/update the prelinked kernel as explicitly requested, or 361 * for the running kernel. 362 */ 363 result = createPrelinkedKernel(&toolArgs); 364 if (result != EX_OK) { 365 goto finish; 366 } 367 368 } 369 370finish: 371 372 /* We're actually not going to free anything else because we're exiting! 373 */ 374 exit(result); 375 376 SAFE_RELEASE(toolArgs.kextIDs); 377 SAFE_RELEASE(toolArgs.argURLs); 378 SAFE_RELEASE(toolArgs.repositoryURLs); 379 SAFE_RELEASE(toolArgs.namedKextURLs); 380 SAFE_RELEASE(toolArgs.allKexts); 381 SAFE_RELEASE(toolArgs.repositoryKexts); 382 SAFE_RELEASE(toolArgs.namedKexts); 383 SAFE_RELEASE(toolArgs.loadedKexts); 384 SAFE_RELEASE(toolArgs.kernelFile); 385 SAFE_RELEASE(toolArgs.symbolDirURL); 386 SAFE_FREE(toolArgs.mkextPath); 387 SAFE_FREE(toolArgs.prelinkedKernelPath); 388 SAFE_FREE(toolArgs.kernelPath); 389 390 return result; 391} 392 393/******************************************************************************* 394*******************************************************************************/ 395ExitStatus readArgs( 396 int * argc, 397 char * const ** argv, 398 KextcacheArgs * toolArgs) 399{ 400 ExitStatus result = EX_USAGE; 401 ExitStatus scratchResult = EX_USAGE; 402 CFStringRef scratchString = NULL; // must release 403 CFNumberRef scratchNumber = NULL; // must release 404 CFURLRef scratchURL = NULL; // must release 405 size_t len = 0; 406 uint32_t i = 0; 407 int optchar = 0; 408 int longindex = -1; 409 struct stat sb; 410 411 bzero(toolArgs, sizeof(*toolArgs)); 412 413 /***** 414 * Allocate collection objects. 415 */ 416 if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks) || 417 !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks) || 418 !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) || 419 !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks) || 420 !createCFMutableArray(&toolArgs->targetArchs, NULL)) { 421 422 OSKextLogMemError(); 423 result = EX_OSERR; 424 exit(result); 425 } 426 427 /***** 428 * Process command line arguments. 429 */ 430 while ((optchar = getopt_long_only(*argc, *argv, 431 kOptChars, sOptInfo, &longindex)) != -1) { 432 433 SAFE_RELEASE_NULL(scratchString); 434 SAFE_RELEASE_NULL(scratchNumber); 435 SAFE_RELEASE_NULL(scratchURL); 436 437 /* When processing short (single-char) options, there is no way to 438 * express optional arguments. Instead, we suppress missing option 439 * argument errors by adding a leading ':' to the option string. 440 * When getopt detects a missing argument, it will return a ':' so that 441 * we can screen for options that are not required to have an argument. 442 */ 443 if (optchar == ':') { 444 switch (optopt) { 445 case kOptPrelinkedKernel: 446 optchar = optopt; 447 break; 448 default: 449 OSKextLog(/* kext */ NULL, 450 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 451 "%s: option requires an argument -- -%c.", 452 progname, optopt); 453 break; 454 } 455 } 456 457 /* Catch a -m before the switch and redirect it to the latest supported 458 * mkext version, so we don't have to duplicate the code block. 459 */ 460 if (optchar == kOptMkext) { 461 optchar = 0; 462 longopt = kLongOptMkext; 463 } 464 465 /* Catch a -e/-system-mkext and redirect to -system-prelinked-kernel. 466 */ 467 if (optchar == kOptSystemMkext) { 468 optchar = 0; 469 longopt = kLongOptSystemPrelinkedKernel; 470 } 471 472 switch (optchar) { 473 474 case kOptArch: 475 if (!addArchForName(toolArgs, optarg)) { 476 OSKextLog(/* kext */ NULL, 477 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 478 "Unknown architecture %s.", optarg); 479 goto finish; 480 } 481 toolArgs->explicitArch = true; 482 break; 483 484 case kOptBundleIdentifier: 485 scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 486 optarg, kCFStringEncodingUTF8); 487 if (!scratchString) { 488 OSKextLogMemError(); 489 result = EX_OSERR; 490 goto finish; 491 } 492 CFSetAddValue(toolArgs->kextIDs, scratchString); 493 break; 494 495 case kOptPrelinkedKernel: 496 scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv, 497 /* isLongopt */ longindex != -1); 498 if (scratchResult != EX_OK) { 499 result = scratchResult; 500 goto finish; 501 } 502 break; 503 504 505#if !NO_BOOT_ROOT 506 case kOptForce: 507 toolArgs->updateOpts |= kBRUForceUpdateHelpers; 508 break; 509#endif /* !NO_BOOT_ROOT */ 510 511 case kOptLowPriorityFork: 512 toolArgs->lowPriorityFlag = true; 513 break; 514 515 case kOptHelp: 516 usage(kUsageLevelFull); 517 result = kKextcacheExitHelp; 518 goto finish; 519 520 case kOptRepositoryCaches: 521 OSKextLog(/* kext */ NULL, 522 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 523 "-%c is no longer used; ignoring.", 524 kOptRepositoryCaches); 525 break; 526 527 case kOptKernel: 528 if (toolArgs->kernelPath) { 529 OSKextLog(/* kext */ NULL, 530 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 531 "Warning: kernel file already specified; using last."); 532 } else { 533 toolArgs->kernelPath = malloc(PATH_MAX); 534 if (!toolArgs->kernelPath) { 535 OSKextLogMemError(); 536 result = EX_OSERR; 537 goto finish; 538 } 539 } 540 541 len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX); 542 if (len >= PATH_MAX) { 543 OSKextLog(/* kext */ NULL, 544 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 545 "Error: kernel filename length exceeds PATH_MAX"); 546 goto finish; 547 } 548 break; 549 550 case kOptLocalRoot: 551 toolArgs->requiredFlagsRepositoriesOnly |= 552 kOSKextOSBundleRequiredLocalRootFlag; 553 break; 554 555 case kOptLocalRootAll: 556 toolArgs->requiredFlagsAll |= 557 kOSKextOSBundleRequiredLocalRootFlag; 558 break; 559 560 case kOptNetworkRoot: 561 toolArgs->requiredFlagsRepositoriesOnly |= 562 kOSKextOSBundleRequiredNetworkRootFlag; 563 break; 564 565 case kOptNetworkRootAll: 566 toolArgs->requiredFlagsAll |= 567 kOSKextOSBundleRequiredNetworkRootFlag; 568 break; 569 570 case kOptAllLoaded: 571 toolArgs->needLoadedKextInfo = true; 572 break; 573 574 case kOptSafeBoot: 575 toolArgs->requiredFlagsRepositoriesOnly |= 576 kOSKextOSBundleRequiredSafeBootFlag; 577 break; 578 579 case kOptSafeBootAll: 580 toolArgs->requiredFlagsAll |= 581 kOSKextOSBundleRequiredSafeBootFlag; 582 break; 583 584 case kOptTests: 585 toolArgs->printTestResults = true; 586 break; 587 588#if !NO_BOOT_ROOT 589 case kOptInvalidate: 590 if (toolArgs->updateVolumeURL) { 591 OSKextLog(/* kext */ NULL, 592 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 593 "Warning: invalidate volume already specified; using last."); 594 SAFE_RELEASE_NULL(toolArgs->updateVolumeURL); 595 } 596 // sanity check that the volume exists 597 if (stat(optarg, &sb)) { 598 OSKextLog(NULL,kOSKextLogWarningLevel|kOSKextLogFileAccessFlag, 599 "%s - %s.", optarg, strerror(errno)); 600 result = EX_NOINPUT; 601 goto finish; 602 } 603 604 scratchURL = CFURLCreateFromFileSystemRepresentation( 605 kCFAllocatorDefault, 606 (const UInt8 *)optarg, 607 strlen(optarg), 608 true); 609 if (!scratchURL) { 610 OSKextLogStringError(/* kext */ NULL); 611 result = EX_OSERR; 612 goto finish; 613 } 614 toolArgs->updateVolumeURL = CFRetain(scratchURL); 615 toolArgs->updateOpts |= kBRUInvalidateKextcache; 616 break; 617 618 case kOptUpdate: 619 case kOptCheckUpdate: 620 if (toolArgs->updateVolumeURL) { 621 OSKextLog(/* kext */ NULL, 622 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 623 "Warning: update volume already specified; using last."); 624 SAFE_RELEASE_NULL(toolArgs->updateVolumeURL); 625 } 626 // sanity check that the volume exists 627 if (stat(optarg, &sb)) { 628 OSKextLog(NULL,kOSKextLogWarningLevel|kOSKextLogFileAccessFlag, 629 "%s - %s.", optarg, strerror(errno)); 630 result = EX_NOINPUT; 631 goto finish; 632 } 633 634 scratchURL = CFURLCreateFromFileSystemRepresentation( 635 kCFAllocatorDefault, 636 (const UInt8 *)optarg, strlen(optarg), true); 637 if (!scratchURL) { 638 OSKextLogStringError(/* kext */ NULL); 639 result = EX_OSERR; 640 goto finish; 641 } 642 toolArgs->updateVolumeURL = CFRetain(scratchURL); 643 if (optchar == kOptCheckUpdate) { 644 toolArgs->updateOpts |= kBRUExpectUpToDate; 645 toolArgs->updateOpts |= kBRUCachesAnyRoot; 646 } 647 break; 648#endif /* !NO_BOOT_ROOT */ 649 650 case kOptQuiet: 651 beQuiet(); 652 break; 653 654 case kOptVerbose: 655 scratchResult = setLogFilterForOpt(*argc, *argv, 656 /* forceOnFlags */ kOSKextLogKextOrGlobalMask); 657 if (scratchResult != EX_OK) { 658 result = scratchResult; 659 goto finish; 660 } 661 break; 662 663 case kOptNoAuthentication: 664 toolArgs->skipAuthentication = true; 665 break; 666 667 case 0: 668 switch (longopt) { 669 case kLongOptMkext1: 670 case kLongOptMkext2: 671 // note kLongOptMkext == latest supported version 672 if (toolArgs->mkextPath) { 673 OSKextLog(/* kext */ NULL, 674 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 675 "Warning: output mkext file already specified; using last."); 676 } else { 677 toolArgs->mkextPath = malloc(PATH_MAX); 678 if (!toolArgs->mkextPath) { 679 OSKextLogMemError(); 680 result = EX_OSERR; 681 goto finish; 682 } 683 } 684 685 len = strlcpy(toolArgs->mkextPath, optarg, PATH_MAX); 686 if (len >= PATH_MAX) { 687 OSKextLog(/* kext */ NULL, 688 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 689 "Error: mkext filename length exceeds PATH_MAX"); 690 goto finish; 691 } 692 693 if (longopt == kLongOptMkext1) { 694 toolArgs->mkextVersion = 1; 695 } else if (longopt == kLongOptMkext2) { 696 toolArgs->mkextVersion = 2; 697 } else { 698 OSKextLog(/* kext */ NULL, 699 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 700 "Intenral error."); 701 } 702 break; 703 704 case kLongOptVolumeRoot: 705 if (toolArgs->volumeRootURL) { 706 OSKextLog(/* kext */ NULL, 707 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 708 "Warning: volume root already specified; using last."); 709 SAFE_RELEASE_NULL(toolArgs->volumeRootURL); 710 } 711 712 scratchURL = CFURLCreateFromFileSystemRepresentation( 713 kCFAllocatorDefault, 714 (const UInt8 *)optarg, strlen(optarg), true); 715 if (!scratchURL) { 716 OSKextLogStringError(/* kext */ NULL); 717 result = EX_OSERR; 718 goto finish; 719 } 720 721 toolArgs->volumeRootURL = CFRetain(scratchURL); 722 break; 723 724 725 case kLongOptSystemCaches: 726 toolArgs->updateSystemCaches = true; 727 setSystemExtensionsFolders(toolArgs); 728 break; 729 730 case kLongOptCompressed: 731 toolArgs->compress = true; 732 break; 733 734 case kLongOptUncompressed: 735 toolArgs->uncompress = true; 736 break; 737 738 case kLongOptSymbols: 739 if (toolArgs->symbolDirURL) { 740 OSKextLog(/* kext */ NULL, 741 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 742 "Warning: symbol directory already specified; using last."); 743 SAFE_RELEASE_NULL(toolArgs->symbolDirURL); 744 } 745 746 scratchURL = CFURLCreateFromFileSystemRepresentation( 747 kCFAllocatorDefault, 748 (const UInt8 *)optarg, strlen(optarg), true); 749 if (!scratchURL) { 750 OSKextLogStringError(/* kext */ NULL); 751 result = EX_OSERR; 752 goto finish; 753 } 754 755 toolArgs->symbolDirURL = CFRetain(scratchURL); 756 toolArgs->generatePrelinkedSymbols = true; 757 break; 758 759 case kLongOptSystemPrelinkedKernel: 760 scratchResult = setPrelinkedKernelArgs(toolArgs, 761 /* filename */ NULL); 762 if (scratchResult != EX_OK) { 763 result = scratchResult; 764 goto finish; 765 } 766 toolArgs->needLoadedKextInfo = true; 767 toolArgs->requiredFlagsRepositoriesOnly |= 768 kOSKextOSBundleRequiredLocalRootFlag; 769 break; 770 771 case kLongOptAllPersonalities: 772 toolArgs->includeAllPersonalities = true; 773 break; 774 775 case kLongOptNoLinkFailures: 776 toolArgs->noLinkFailures = true; 777 break; 778 779 case kLongOptStripSymbols: 780 toolArgs->stripSymbols = true; 781 break; 782 783#if !NO_BOOT_ROOT 784 case kLongOptInstaller: 785 toolArgs->updateOpts |= kBRUHelpersOptional; 786 toolArgs->updateOpts |= kBRUForceUpdateHelpers; 787 break; 788 case kLongOptCachesOnly: 789 toolArgs->updateOpts |= kBRUCachesOnly; 790 break; 791 case kLongOptEarlyBoot: 792 toolArgs->updateOpts |= kBRUEarlyBoot; 793 break; 794#endif /* !NO_BOOT_ROOT */ 795 796 default: 797 /* Because we use ':', getopt_long doesn't print an error message. 798 */ 799 OSKextLog(/* kext */ NULL, 800 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 801 "unrecognized option %s", (*argv)[optind-1]); 802 goto finish; 803 break; 804 } 805 break; 806 807 default: 808 /* Because we use ':', getopt_long doesn't print an error message. 809 */ 810 OSKextLog(/* kext */ NULL, 811 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 812 "unrecognized option %s", (*argv)[optind-1]); 813 goto finish; 814 break; 815 816 } 817 818 /* Reset longindex, because getopt_long_only() is stupid and doesn't. 819 */ 820 longindex = -1; 821 } 822 823 /* Update the argc & argv seen by main() so that boot<>root calls 824 * handle remaining args. 825 */ 826 *argc -= optind; 827 *argv += optind; 828 829 /***** 830 * If we aren't doing a boot<>root update, record the kext & directory names 831 * from the command line. (If we are doing a boot<>root update, remaining 832 * command line args are processed later.) 833 */ 834 if (!toolArgs->updateVolumeURL) { 835 for (i = 0; i < *argc; i++) { 836 SAFE_RELEASE_NULL(scratchURL); 837 SAFE_RELEASE_NULL(scratchString); 838 839 scratchURL = CFURLCreateFromFileSystemRepresentation( 840 kCFAllocatorDefault, 841 (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true); 842 if (!scratchURL) { 843 OSKextLogMemError(); 844 result = EX_OSERR; 845 goto finish; 846 } 847 CFArrayAppendValue(toolArgs->argURLs, scratchURL); 848 849 scratchString = CFURLCopyPathExtension(scratchURL); 850 if (scratchString && CFEqual(scratchString, CFSTR("kext"))) { 851 CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL); 852 } else { 853 CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL); 854 } 855 } 856 } 857 858 result = EX_OK; 859 860finish: 861 SAFE_RELEASE(scratchString); 862 SAFE_RELEASE(scratchNumber); 863 SAFE_RELEASE(scratchURL); 864 865 if (result == EX_USAGE) { 866 usage(kUsageLevelBrief); 867 } 868 return result; 869} 870 871/******************************************************************************* 872*******************************************************************************/ 873ExitStatus readPrelinkedKernelArgs( 874 KextcacheArgs * toolArgs, 875 int argc, 876 char * const * argv, 877 Boolean isLongopt) 878{ 879 char * filename = NULL; // do not free 880 881 if (optarg) { 882 filename = optarg; 883 } else if (isLongopt && optind < argc) { 884 filename = argv[optind]; 885 optind++; 886 } 887 888 if (filename && !filename[0]) { 889 filename = NULL; 890 } 891 892 return setPrelinkedKernelArgs(toolArgs, filename); 893} 894 895/******************************************************************************* 896*******************************************************************************/ 897ExitStatus setPrelinkedKernelArgs( 898 KextcacheArgs * toolArgs, 899 char * filename) 900{ 901 ExitStatus result = EX_USAGE; 902 903 if (toolArgs->prelinkedKernelPath) { 904 OSKextLog(/* kext */ NULL, 905 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 906 "Warning: prelinked kernel already specified; using last."); 907 } else { 908 toolArgs->prelinkedKernelPath = malloc(PATH_MAX); 909 if (!toolArgs->prelinkedKernelPath) { 910 OSKextLogMemError(); 911 result = EX_OSERR; 912 goto finish; 913 } 914 } 915 916 /* If we don't have a filename we construct a default one, automatically 917 * add the system extensions folders, and note that we're using default 918 * info. 919 */ 920 if (!filename) { 921#if NO_BOOT_ROOT 922 OSKextLog(/* kext */ NULL, 923 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 924 "Error: prelinked kernel filename required"); 925 goto finish; 926#else 927 if (!setDefaultPrelinkedKernel(toolArgs)) { 928 goto finish; 929 } 930 toolArgs->needDefaultPrelinkedKernelInfo = true; 931 setSystemExtensionsFolders(toolArgs); 932#endif /* NO_BOOT_ROOT */ 933 } else { 934 size_t len = strlcpy(toolArgs->prelinkedKernelPath, filename, PATH_MAX); 935 if (len >= PATH_MAX) { 936 OSKextLog(/* kext */ NULL, 937 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 938 "Error: prelinked kernel filename length exceeds PATH_MAX"); 939 goto finish; 940 } 941 } 942 result = EX_OK; 943finish: 944 return result; 945} 946 947 948#if !NO_BOOT_ROOT 949#ifndef kIOPMAssertNoIdleSystemSleep 950#define kIOPMAssertNoIdleSystemSleep \ 951 kIOPMAssertionTypePreventUserIdleSystemSleep 952#endif 953ExitStatus doUpdateVolume(KextcacheArgs *toolArgs) 954{ 955 ExitStatus rval; // no goto's in this function 956 int result; // errno-type value 957 IOReturn pmres = kIOReturnError; // init against future re-flow 958 IOPMAssertionID awakeForUpdate; // valid if pmres == 0 959 960 // unless -F is passed, keep machine awake for for duration 961 // (including waiting for any volume locks with kextd) 962 if (toolArgs->lowPriorityFlag == false) { 963 pmres = IOPMAssertionCreateWithName(kIOPMAssertNoIdleSystemSleep, 964 kIOPMAssertionLevelOn, 965 CFSTR("com.apple.kextmanager.update"), 966 &awakeForUpdate); 967 if (pmres) { 968 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 969 "Warning: couldn't block sleep during cache update"); 970 } 971 972 } 973 974 result = checkUpdateCachesAndBoots(toolArgs->updateVolumeURL, 975 toolArgs->updateOpts); 976 // translate known errno -> sysexits(3) value 977 switch (result) { 978 case ENOENT: 979 case EFTYPE: rval = EX_OSFILE; break; 980 default: rval = result; 981 } 982 983 if (toolArgs->lowPriorityFlag == false && pmres == 0) { 984 // drop assertion 985 if (IOPMAssertionRelease(awakeForUpdate)) 986 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 987 "Warning: error re-enabling sleep after cache update"); 988 } 989 990 return rval; 991} 992 993/******************************************************************************* 994*******************************************************************************/ 995Boolean setDefaultKernel(KextcacheArgs * toolArgs) 996{ 997#if DEV_KERNEL_SUPPORT 998 Boolean addSuffix = FALSE; 999#endif 1000 size_t length = 0; 1001 struct stat statBuf; 1002 1003 if (!toolArgs->kernelPath) { 1004 toolArgs->kernelPath = malloc(PATH_MAX); 1005 if (!toolArgs->kernelPath) { 1006 OSKextLogMemError(); 1007 return FALSE; 1008 } 1009 } 1010 1011 while( true ) { 1012 1013 // use KernelPath from /usr/standalone/bootcaches.plist 1014 if (getKernelPathForURL(toolArgs->volumeRootURL, 1015 toolArgs->kernelPath, 1016 PATH_MAX) == FALSE) { 1017 // no bootcaches.plist? Forced to hardwire... 1018 strlcpy(toolArgs->kernelPath, "/System/Library/Kernels/kernel", 1019 PATH_MAX); 1020 } 1021 1022#if DEV_KERNEL_SUPPORT 1023 // for Apple Internal builds try to default to dev kernel 1024 // /System/Library/Kernels/kernel.development 1025 addSuffix = useDevelopmentKernel(toolArgs->kernelPath); 1026 if (addSuffix) { 1027 if (strlen(toolArgs->kernelPath) + strlen(kDefaultKernelSuffix) + 1 < PATH_MAX) { 1028 strlcat(toolArgs->kernelPath, 1029 kDefaultKernelSuffix, 1030 PATH_MAX); 1031 } 1032 else { 1033 addSuffix = FALSE; 1034 } 1035 } 1036#endif 1037 1038 if (statPath(toolArgs->kernelPath, &statBuf) == EX_OK) { 1039 break; 1040 } 1041 1042 OSKextLog(/* kext */ NULL, 1043 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1044 "Error: invalid kernel path '%s'", 1045 toolArgs->kernelPath); 1046 return FALSE; 1047 } // while... 1048 1049 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[0], &statBuf.st_atimespec); 1050 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[1], &statBuf.st_mtimespec); 1051 1052#if DEV_KERNEL_SUPPORT 1053 if (toolArgs->prelinkedKernelPath && 1054 toolArgs->needDefaultPrelinkedKernelInfo && 1055 addSuffix) { 1056 // we are using default kernelcache name so add .development suffix 1057 length = strlcat(toolArgs->prelinkedKernelPath, 1058 kDefaultKernelSuffix, 1059 PATH_MAX); 1060 if (length >= PATH_MAX) { 1061 OSKextLog(/* kext */ NULL, 1062 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1063 "Error: kernelcache filename length exceeds PATH_MAX"); 1064 return FALSE; 1065 } 1066 } 1067#endif 1068 1069 return TRUE; 1070} 1071 1072 1073 1074/******************************************************************************* 1075*******************************************************************************/ 1076Boolean setDefaultPrelinkedKernel(KextcacheArgs * toolArgs) 1077{ 1078 Boolean result = FALSE; 1079 const char * prelinkedKernelFile = NULL; 1080 size_t length = 0; 1081 1082 prelinkedKernelFile = 1083 _kOSKextCachesRootFolder "/" _kOSKextStartupCachesSubfolder "/" 1084 _kOSKextPrelinkedKernelBasename; 1085 1086 length = strlcpy(toolArgs->prelinkedKernelPath, 1087 prelinkedKernelFile, PATH_MAX); 1088 if (length >= PATH_MAX) { 1089 OSKextLog(/* kext */ NULL, 1090 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1091 "Error: prelinked kernel filename length exceeds PATH_MAX"); 1092 goto finish; 1093 } 1094 1095 result = TRUE; 1096 1097finish: 1098 return result; 1099} 1100#endif /* !NO_BOOT_ROOT */ 1101 1102/******************************************************************************* 1103*******************************************************************************/ 1104void setSystemExtensionsFolders(KextcacheArgs * toolArgs) 1105{ 1106 CFArrayRef sysExtensionsFolders = OSKextGetSystemExtensionsFolderURLs(); 1107 1108 CFArrayAppendArray(toolArgs->argURLs, 1109 sysExtensionsFolders, RANGE_ALL(sysExtensionsFolders)); 1110 CFArrayAppendArray(toolArgs->repositoryURLs, 1111 sysExtensionsFolders, RANGE_ALL(sysExtensionsFolders)); 1112 1113 return; 1114} 1115 1116/******************************************************************************* 1117*******************************************************************************/ 1118#include <servers/bootstrap.h> // bootstrap mach ports 1119 1120static void 1121waitForIOKitQuiescence(void) 1122{ 1123 kern_return_t kern_result = 0; 1124 mach_timespec_t waitTime = { 40, 0 }; 1125 1126 // if kextd is not running yet (early boot) then IOKitWaitQuiet will 1127 // always time out. So go ahead and bail out if there is no kextd. 1128 if ( isKextdRunning() == FALSE ) { 1129 return; 1130 } 1131 1132 OSKextLog(/* kext */ NULL, 1133 kOSKextLogProgressLevel | kOSKextLogIPCFlag, 1134 "Waiting for I/O Kit to quiesce."); 1135 1136 kern_result = IOKitWaitQuiet(kIOMasterPortDefault, &waitTime); 1137 if (kern_result == kIOReturnTimeout) { 1138 OSKextLog(/* kext */ NULL, 1139 kOSKextLogErrorLevel | kOSKextLogIPCFlag, 1140 "IOKitWaitQuiet() timed out."); 1141 } else if (kern_result != kOSReturnSuccess) { 1142 OSKextLog(/* kext */ NULL, 1143 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1144 "IOKitWaitQuiet() failed - %s.", 1145 safe_mach_error_string(kern_result)); 1146 } 1147} 1148 1149/******************************************************************************* 1150* Wait for the system to report that it's a good time to do work. We define a 1151* good time to be when the IOSystemLoadAdvisory API returns a combined level of 1152* kIOSystemLoadAdvisoryLevelGreat, and we'll wait up to kOSKextSystemLoadTimeout 1153* seconds for the system to enter that state before we begin our work. If there 1154* is an error in this function, we just return and get started with the work. 1155*******************************************************************************/ 1156static void 1157waitForGreatSystemLoad(void) 1158{ 1159 struct timeval currenttime; 1160 struct timeval endtime; 1161 struct timeval timeout; 1162 fd_set readfds; 1163 fd_set tmpfds; 1164 uint64_t systemLoadAdvisoryState = 0; 1165 uint32_t notifyStatus = 0; 1166 uint32_t usecs = 0; 1167 int systemLoadAdvisoryFileDescriptor = 0; // closed by notify_cancel() 1168 int systemLoadAdvisoryToken = 0; // must notify_cancel() 1169 int currentToken = 0; // do not notify_cancel() 1170 int myResult; 1171 1172 bzero(¤ttime, sizeof(currenttime)); 1173 bzero(&endtime, sizeof(endtime)); 1174 bzero(&timeout, sizeof(timeout)); 1175 1176 OSKextLog(/* kext */ NULL, 1177 kOSKextLogProgressLevel | kOSKextLogGeneralFlag, 1178 "Waiting for low system load."); 1179 1180 /* Register for SystemLoadAdvisory notifications */ 1181 1182 notifyStatus = notify_register_file_descriptor(kIOSystemLoadAdvisoryNotifyName, 1183 &systemLoadAdvisoryFileDescriptor, 1184 /* flags */ 0, &systemLoadAdvisoryToken); 1185 if (notifyStatus != NOTIFY_STATUS_OK) { 1186 goto finish; 1187 } 1188 1189 OSKextLog(/* kext */ NULL, 1190 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1191 "Received initial system load status %llu", systemLoadAdvisoryState); 1192 1193 /* If it's a good time, we'll just return */ 1194 1195 notifyStatus = notify_get_state(systemLoadAdvisoryToken, &systemLoadAdvisoryState); 1196 if (notifyStatus != NOTIFY_STATUS_OK) { 1197 goto finish; 1198 } 1199 1200 if (systemLoadAdvisoryState == kIOSystemLoadAdvisoryLevelGreat) { 1201 goto finish; 1202 } 1203 1204 /* Set up the select timers */ 1205 1206 myResult = gettimeofday(¤ttime, NULL); 1207 if (myResult < 0) { 1208 goto finish; 1209 } 1210 1211 endtime = currenttime; 1212 endtime.tv_sec += kOSKextSystemLoadTimeout; 1213 1214 timeval_difference(&timeout, &endtime, ¤ttime); 1215 usecs = usecs_from_timeval(&timeout); 1216 1217 FD_ZERO(&readfds); 1218 FD_SET(systemLoadAdvisoryFileDescriptor, &readfds); 1219 1220 /* Check SystemLoadAdvisory notifications until it's a great time to 1221 * do work or we hit the timeout. 1222 */ 1223 1224 while (usecs) { 1225 /* Wait for notifications or the timeout */ 1226 1227 FD_COPY(&readfds, &tmpfds); 1228 myResult = select(systemLoadAdvisoryFileDescriptor + 1, 1229 &tmpfds, NULL, NULL, &timeout); 1230 if (myResult < 0) { 1231 goto finish; 1232 } 1233 1234 /* Set up the next timeout */ 1235 1236 myResult = gettimeofday(¤ttime, NULL); 1237 if (myResult < 0) { 1238 goto finish; 1239 } 1240 1241 timeval_difference(&timeout, &endtime, ¤ttime); 1242 usecs = usecs_from_timeval(&timeout); 1243 1244 /* Check the system load state */ 1245 1246 if (!FD_ISSET(systemLoadAdvisoryFileDescriptor, &tmpfds)) { 1247 continue; 1248 } 1249 1250 myResult = (int)read(systemLoadAdvisoryFileDescriptor, 1251 ¤tToken, sizeof(currentToken)); 1252 if (myResult < 0) { 1253 goto finish; 1254 } 1255 1256 /* The token is written in network byte order. */ 1257 currentToken = ntohl(currentToken); 1258 1259 if (currentToken != systemLoadAdvisoryToken) { 1260 continue; 1261 } 1262 1263 notifyStatus = notify_get_state(systemLoadAdvisoryToken, 1264 &systemLoadAdvisoryState); 1265 if (notifyStatus != NOTIFY_STATUS_OK) { 1266 goto finish; 1267 } 1268 1269 OSKextLog(/* kext */ NULL, 1270 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1271 "Received updated system load status %llu", systemLoadAdvisoryState); 1272 1273 if (systemLoadAdvisoryState == kIOSystemLoadAdvisoryLevelGreat) { 1274 break; 1275 } 1276 } 1277 1278 OSKextLog(/* kext */ NULL, 1279 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1280 "Pausing for another %d seconds to avoid work contention", 1281 kOSKextSystemLoadPauseTime); 1282 1283 /* We'll wait a random amount longer to avoid colliding with 1284 * other work that is waiting for a great time. 1285 */ 1286 sleep(kOSKextSystemLoadPauseTime); 1287 1288 OSKextLog(/* kext */ NULL, 1289 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1290 "System load is low. Proceeding.\n"); 1291finish: 1292 if (systemLoadAdvisoryToken) { 1293 notify_cancel(systemLoadAdvisoryToken); 1294 } 1295 return; 1296} 1297 1298/******************************************************************************* 1299*******************************************************************************/ 1300static u_int 1301usecs_from_timeval(struct timeval *t) 1302{ 1303 u_int usecs = 0; 1304 1305 if (t) { 1306 usecs = (unsigned int)((t->tv_sec * 1000) + t->tv_usec); 1307 } 1308 1309 return usecs; 1310} 1311 1312/******************************************************************************* 1313*******************************************************************************/ 1314static void 1315timeval_from_usecs(struct timeval *t, u_int usecs) 1316{ 1317 if (t) { 1318 if (usecs > 0) { 1319 t->tv_sec = usecs / 1000; 1320 t->tv_usec = usecs % 1000; 1321 } else { 1322 bzero(t, sizeof(*t)); 1323 } 1324 } 1325} 1326 1327/******************************************************************************* 1328* dst = a - b 1329*******************************************************************************/ 1330static void 1331timeval_difference(struct timeval *dst, struct timeval *a, struct timeval *b) 1332{ 1333 u_int ausec = 0, busec = 0, dstusec = 0; 1334 1335 if (dst) { 1336 ausec = usecs_from_timeval(a); 1337 busec = usecs_from_timeval(b); 1338 1339 if (ausec > busec) { 1340 dstusec = ausec - busec; 1341 } 1342 1343 timeval_from_usecs(dst, dstusec); 1344 } 1345} 1346 1347#if !NO_BOOT_ROOT 1348/******************************************************************************* 1349*******************************************************************************/ 1350void setDefaultArchesIfNeeded(KextcacheArgs * toolArgs) 1351{ 1352 /* If no arches were explicitly specified, use the architecture of the 1353 * running kernel. 1354 */ 1355 if (toolArgs->explicitArch) { 1356 return; 1357 } 1358 1359 CFArrayRemoveAllValues(toolArgs->targetArchs); 1360 addArch(toolArgs, OSKextGetRunningKernelArchitecture()); 1361 1362 return; 1363} 1364#endif /* !NO_BOOT_ROOT */ 1365 1366/******************************************************************************* 1367********************************************************************************/ 1368void addArch( 1369 KextcacheArgs * toolArgs, 1370 const NXArchInfo * arch) 1371{ 1372 if (CFArrayContainsValue(toolArgs->targetArchs, 1373 RANGE_ALL(toolArgs->targetArchs), arch)) 1374 { 1375 return; 1376 } 1377 1378 CFArrayAppendValue(toolArgs->targetArchs, arch); 1379} 1380 1381/******************************************************************************* 1382*******************************************************************************/ 1383const NXArchInfo * addArchForName( 1384 KextcacheArgs * toolArgs, 1385 const char * archname) 1386{ 1387 const NXArchInfo * result = NULL; 1388 1389 result = NXGetArchInfoFromName(archname); 1390 if (!result) { 1391 goto finish; 1392 } 1393 1394 addArch(toolArgs, result); 1395 1396finish: 1397 return result; 1398} 1399 1400/******************************************************************************* 1401*******************************************************************************/ 1402void checkKextdSpawnedFilter(Boolean kernelFlag) 1403{ 1404 const char * environmentVariable = NULL; // do not free 1405 char * environmentLogFilterString = NULL; // do not free 1406 1407 if (kernelFlag) { 1408 environmentVariable = "KEXT_LOG_FILTER_KERNEL"; 1409 } else { 1410 environmentVariable = "KEXT_LOG_FILTER_USER"; 1411 } 1412 1413 environmentLogFilterString = getenv(environmentVariable); 1414 1415 /***** 1416 * If we have environment variables for a log spec, take the greater 1417 * of the log levels and OR together the flags from the environment's & 1418 * this process's command-line log specs. This way the most verbose setting 1419 * always applies. 1420 * 1421 * Otherwise, set the environment variable in case we spawn children. 1422 */ 1423 if (environmentLogFilterString) { 1424 OSKextLogSpec toolLogSpec = OSKextGetLogFilter(kernelFlag); 1425 OSKextLogSpec kextdLogSpec = (unsigned int)strtoul(environmentLogFilterString, NULL, 16); 1426 1427 OSKextLogSpec toolLogLevel = toolLogSpec & kOSKextLogLevelMask; 1428 OSKextLogSpec kextdLogLevel = kextdLogSpec & kOSKextLogLevelMask; 1429 OSKextLogSpec comboLogLevel = MAX(toolLogLevel, kextdLogLevel); 1430 1431 OSKextLogSpec toolLogFlags = toolLogSpec & kOSKextLogFlagsMask; 1432 OSKextLogSpec kextdLogFlags = kextdLogSpec & kOSKextLogFlagsMask; 1433 OSKextLogSpec comboLogFlags = toolLogFlags | kextdLogFlags | 1434 kOSKextLogKextOrGlobalMask; 1435 1436 OSKextSetLogFilter(comboLogLevel | comboLogFlags, kernelFlag); 1437 } else { 1438 char logSpecBuffer[16]; // enough for a 64-bit hex value 1439 1440 snprintf(logSpecBuffer, sizeof(logSpecBuffer), "0x%x", 1441 OSKextGetLogFilter(kernelFlag)); 1442 setenv(environmentVariable, logSpecBuffer, /* overwrite */ 1); 1443 } 1444 1445 return; 1446} 1447 1448/******************************************************************************* 1449*******************************************************************************/ 1450ExitStatus checkArgs(KextcacheArgs * toolArgs) 1451{ 1452 ExitStatus result = EX_USAGE; 1453 Boolean expectUpToDate = toolArgs->updateOpts & kBRUExpectUpToDate; 1454 1455 if (!toolArgs->mkextPath && !toolArgs->prelinkedKernelPath && 1456 !toolArgs->updateVolumeURL && !toolArgs->updateSystemCaches) 1457 { 1458 OSKextLog(/* kext */ NULL, 1459 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1460 "No work to do; check options and try again."); 1461 goto finish; 1462 } 1463 1464 if (toolArgs->volumeRootURL && !toolArgs->mkextPath && 1465 !toolArgs->prelinkedKernelPath) 1466 { 1467 OSKextLog(/* kext */ NULL, 1468 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1469 "Use -%s only when creating an mkext archive or prelinked kernel.", 1470 kOptNameVolumeRoot); 1471 goto finish; 1472 } 1473 1474 if (!toolArgs->updateVolumeURL && !CFArrayGetCount(toolArgs->argURLs) && 1475 !toolArgs->compress && !toolArgs->uncompress) 1476 { 1477 OSKextLog(/* kext */ NULL, 1478 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1479 "No kexts or directories specified."); 1480 goto finish; 1481 } 1482 1483 if (!toolArgs->compress && !toolArgs->uncompress) { 1484 toolArgs->compress = true; 1485 } else if (toolArgs->compress && toolArgs->uncompress) { 1486 OSKextLog(/* kext */ NULL, 1487 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1488 "Both -%s and -%s specified; using -%s.", 1489 kOptNameCompressed, kOptNameUncompressed, kOptNameCompressed); 1490 toolArgs->compress = true; 1491 toolArgs->uncompress = false; 1492 } 1493 1494#if !NO_BOOT_ROOT 1495 if ((toolArgs->updateOpts & kBRUForceUpdateHelpers) 1496 && (toolArgs->updateOpts & kBRUCachesOnly)) { 1497 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1498 "-%s (%-c) and %-s are mutually exclusive", 1499 kOptNameForce, kOptForce, kOptNameCachesOnly); 1500 goto finish; 1501 } 1502 if (toolArgs->updateOpts & kBRUForceUpdateHelpers) { 1503 if (expectUpToDate || !toolArgs->updateVolumeURL) { 1504 OSKextLog(/* kext */ NULL, 1505 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1506 "-%s (-%c) is allowed only with -%s (-%c).", 1507 kOptNameForce, kOptForce, kOptNameUpdate, kOptUpdate); 1508 goto finish; 1509 } 1510 } 1511 if (toolArgs->updateOpts & kBRUEarlyBoot) { 1512 if (!toolArgs->updateVolumeURL) { 1513 OSKextLog(/* kext */ NULL, 1514 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1515 "-%s requires -%c.", 1516 kOptNameEarlyBoot, kOptCheckUpdate); 1517 goto finish; 1518 } 1519 } 1520 if (toolArgs->updateOpts & kBRUCachesOnly) { 1521 if (expectUpToDate || !toolArgs->updateVolumeURL) { 1522 OSKextLog(/* kext */ NULL, 1523 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1524 "-%s is allowed only with -%s (-%c).", 1525 kOptNameCachesOnly, kOptNameUpdate, kOptUpdate); 1526 goto finish; 1527 } 1528 } 1529#endif /* !NO_BOOT_ROOT */ 1530 1531 if (toolArgs->updateVolumeURL) { 1532 if (toolArgs->mkextPath || toolArgs->prelinkedKernelPath) { 1533 OSKextLog(/* kext */ NULL, 1534 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1535 "Can't create mkext or prelinked kernel when updating volumes."); 1536 } 1537 } 1538 1539#if !NO_BOOT_ROOT 1540 setDefaultArchesIfNeeded(toolArgs); 1541#endif /* !NO_BOOT_ROOT */ 1542 1543 /* 11860417 - we now support multiple extensions directories, get access and 1544 * mod times from extensions directory with the most current mode date. 1545 */ 1546 if (toolArgs->extensionsDirTimes[1].tv_sec == 0 && 1547 CFArrayGetCount(toolArgs->repositoryURLs)) { 1548 result = getLatestTimesFromCFURLArray(toolArgs->repositoryURLs, 1549 toolArgs->extensionsDirTimes); 1550 if (result != EX_OK) { 1551 OSKextLog(NULL, 1552 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1553 "%s: Can't get mod times", __FUNCTION__); 1554 goto finish; 1555 } 1556 } 1557 1558#if !NO_BOOT_ROOT 1559 if (toolArgs->needDefaultPrelinkedKernelInfo && !toolArgs->kernelPath) { 1560 if (!setDefaultKernel(toolArgs)) { 1561 result = EX_USAGE; 1562 goto finish; 1563 } 1564 } 1565#endif /* !NO_BOOT_ROOT */ 1566 1567 if (toolArgs->prelinkedKernelPath && CFArrayGetCount(toolArgs->argURLs)) { 1568 struct stat myStatBuf; 1569 1570 if (!toolArgs->kernelPath) { 1571 if (!setDefaultKernel(toolArgs)) { 1572 OSKextLog(/* kext */ NULL, 1573 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1574 "No kernel specified for prelinked kernel generation."); 1575 result = EX_USAGE; 1576 goto finish; 1577 } 1578 } 1579 result = statPath(toolArgs->kernelPath, &myStatBuf); 1580 if (result != EX_OK) { 1581 goto finish; 1582 } 1583 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[0], &myStatBuf.st_atimespec); 1584 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[1], &myStatBuf.st_mtimespec); 1585 } 1586 1587 /* Updating system caches requires no additional kexts or repositories, 1588 * and must run as root. 1589 */ 1590 if (toolArgs->needDefaultPrelinkedKernelInfo || 1591 toolArgs->updateSystemCaches) { 1592 1593 if (CFArrayGetCount(toolArgs->namedKextURLs) || CFSetGetCount(toolArgs->kextIDs) || 1594 !CFEqual(toolArgs->repositoryURLs, OSKextGetSystemExtensionsFolderURLs())) { 1595 1596 OSKextLog(/* kext */ NULL, 1597 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1598 "Custom kexts and repository directories are not allowed " 1599 "when updating system kext caches."); 1600 result = EX_USAGE; 1601 goto finish; 1602 1603 } 1604 1605 if (geteuid() != 0) { 1606 OSKextLog(/* kext */ NULL, 1607 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1608 "You must be running as root to update system kext caches."); 1609 result = EX_NOPERM; 1610 goto finish; 1611 } 1612 } 1613 1614 result = EX_OK; 1615 1616finish: 1617 if (result == EX_USAGE) { 1618 usage(kUsageLevelBrief); 1619 } 1620 return result; 1621} 1622 1623/******************************************************************************* 1624*******************************************************************************/ 1625ExitStatus 1626getLoadedKextInfo( 1627 KextcacheArgs *toolArgs) 1628{ 1629 ExitStatus result = EX_SOFTWARE; 1630 CFArrayRef requestedIdentifiers = NULL; // must release 1631 1632 /* Let I/O Kit settle down before we poke at it. 1633 */ 1634 1635 (void) waitForIOKitQuiescence(); 1636 1637 /* Get the list of requested bundle IDs from the kernel and find all of 1638 * the associated kexts. 1639 */ 1640 1641 requestedIdentifiers = OSKextCopyAllRequestedIdentifiers(); 1642 if (!requestedIdentifiers) { 1643 goto finish; 1644 } 1645 1646 toolArgs->loadedKexts = OSKextCopyKextsWithIdentifiers(requestedIdentifiers); 1647 if (!toolArgs->loadedKexts) { 1648 goto finish; 1649 } 1650 1651 result = EX_OK; 1652 1653finish: 1654 SAFE_RELEASE(requestedIdentifiers); 1655 1656 return result; 1657} 1658 1659#pragma mark System Plist Caches 1660 1661/******************************************************************************* 1662*******************************************************************************/ 1663ExitStatus updateSystemPlistCaches(KextcacheArgs * toolArgs) 1664{ 1665 ExitStatus result = EX_OSERR; 1666 ExitStatus directoryResult = EX_OK; // flipped to error as needed 1667 CFArrayRef systemExtensionsURLs = NULL; // do not release 1668 CFArrayRef kexts = NULL; // must release 1669 CFURLRef folderURL = NULL; // do not release 1670 char folderPath[PATH_MAX] = ""; 1671 const NXArchInfo * startArch = OSKextGetArchitecture(); 1672 CFArrayRef directoryValues = NULL; // must release 1673 CFArrayRef personalities = NULL; // must release 1674 CFIndex count, i; 1675 1676 /* We only care about updating info for the system extensions folders. 1677 */ 1678 systemExtensionsURLs = OSKextGetSystemExtensionsFolderURLs(); 1679 if (!systemExtensionsURLs) { 1680 OSKextLogMemError(); 1681 result = EX_OSERR; 1682 goto finish; 1683 } 1684 1685 kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, systemExtensionsURLs); 1686 if (!kexts) { 1687 goto finish; 1688 } 1689 1690 /* Update the global personalities & property-value caches, each per arch. 1691 */ 1692 for (i = 0; i < CFArrayGetCount(toolArgs->targetArchs); i++) { 1693 const NXArchInfo * targetArch = 1694 CFArrayGetValueAtIndex(toolArgs->targetArchs, i); 1695 1696 SAFE_RELEASE_NULL(personalities); 1697 1698 /* Set the active architecture for scooping out personalities and such. 1699 */ 1700 if (!OSKextSetArchitecture(targetArch)) { 1701 goto finish; 1702 } 1703 1704 personalities = OSKextCopyPersonalitiesOfKexts(kexts); 1705 if (!personalities) { 1706 goto finish; 1707 } 1708 1709 if (!_OSKextWriteCache(systemExtensionsURLs, CFSTR(kIOKitPersonalitiesKey), 1710 targetArch, _kOSKextCacheFormatIOXML, personalities)) { 1711 1712 goto finish; 1713 } 1714 1715 /* Loginwindow asks us for this property so let's spare lots of I/O 1716 * by caching it. This read function call updates the caches for us; 1717 * we don't use the output. 1718 */ 1719 if (!readSystemKextPropertyValues(CFSTR(kOSBundleHelperKey), targetArch, 1720 /* forceUpdate? */ true, /* values */ NULL)) { 1721 1722 goto finish; 1723 } 1724 } 1725 1726 /* Update per-directory caches. This is just KextIdentifiers any more. 1727 */ 1728 count = CFArrayGetCount(systemExtensionsURLs); 1729 for (i = 0; i < count; i++) { 1730 1731 folderURL = CFArrayGetValueAtIndex(systemExtensionsURLs, i); 1732 1733 if (!CFURLGetFileSystemRepresentation(folderURL, /* resolveToBase */ true, 1734 (UInt8 *)folderPath, sizeof(folderPath))) { 1735 1736 OSKextLogStringError(/* kext */ NULL); 1737 goto finish; 1738 } 1739 if (EX_OK != updateDirectoryCaches(toolArgs, folderURL)) { 1740 directoryResult = EX_OSERR; 1741 } else { 1742 OSKextLog(/* kext */ NULL, 1743 kOSKextLogBasicLevel | kOSKextLogGeneralFlag, 1744 "Directory caches updated for %s.", folderPath); 1745 } 1746 } 1747 1748 if (directoryResult == EX_OK) { 1749 result = EX_OK; 1750 } 1751 1752finish: 1753 SAFE_RELEASE(kexts); 1754 SAFE_RELEASE(directoryValues); 1755 SAFE_RELEASE(personalities); 1756 1757 OSKextSetArchitecture(startArch); 1758 1759 return result; 1760} 1761 1762/******************************************************************************* 1763*******************************************************************************/ 1764ExitStatus updateDirectoryCaches( 1765 KextcacheArgs * toolArgs, 1766 CFURLRef folderURL) 1767{ 1768 ExitStatus result = EX_OK; // optimistic! 1769 CFArrayRef kexts = NULL; // must release 1770 1771 kexts = OSKextCreateKextsFromURL(kCFAllocatorDefault, folderURL); 1772 if (!kexts) { 1773 result = EX_OSERR; 1774 goto finish; 1775 } 1776 1777 if (!_OSKextWriteIdentifierCacheForKextsInDirectory( 1778 kexts, folderURL, /* force? */ true)) { 1779 result = EX_OSERR; 1780 goto finish; 1781 } 1782 1783 result = EX_OK; 1784 1785finish: 1786 SAFE_RELEASE(kexts); 1787 return result; 1788} 1789 1790#pragma mark Misc Stuff 1791 1792/******************************************************************************* 1793*******************************************************************************/ 1794/******************************************************************************* 1795*******************************************************************************/ 1796/* Open Firmware (PPC only) has an upper limit of 16MB on file transfers, 1797 * so we'll limit ourselves just beneath that. 1798 */ 1799#define kOpenFirmwareMaxFileSize (16 * 1024 * 1024) 1800 1801ExitStatus createMkext( 1802 KextcacheArgs * toolArgs, 1803 Boolean * fatalOut) 1804{ 1805 struct timeval extDirsTimes[2]; 1806 ExitStatus result = EX_SOFTWARE; 1807 CFMutableArrayRef archiveKexts = NULL; // must release 1808 CFMutableArrayRef mkexts = NULL; // must release 1809 CFDataRef mkext = NULL; // must release 1810 const NXArchInfo *targetArch = NULL; // do not free 1811 int i; 1812 1813#if !NO_BOOT_ROOT 1814 /* Try a lock on the volume for the mkext being updated. 1815 * The lock prevents kextd from starting up a competing kextcache. 1816 */ 1817 if (!getenv("_com_apple_kextd_skiplocks")) { 1818 // xxx - updateBoots + related should return only sysexit-type values, not errno 1819 result = takeVolumeForPath(toolArgs->mkextPath); 1820 if (result != EX_OK) { 1821 goto finish; 1822 } 1823 } 1824#endif /* !NO_BOOT_ROOT */ 1825 1826 if (!createCFMutableArray(&mkexts, &kCFTypeArrayCallBacks)) { 1827 OSKextLogMemError(); 1828 result = EX_OSERR; 1829 *fatalOut = true; 1830 goto finish; 1831 } 1832 1833 if (!createCFMutableArray(&archiveKexts, &kCFTypeArrayCallBacks)) { 1834 OSKextLogMemError(); 1835 result = EX_OSERR; 1836 goto finish; 1837 } 1838 1839 for (i = 0; i < CFArrayGetCount(toolArgs->targetArchs); i++) { 1840 targetArch = CFArrayGetValueAtIndex(toolArgs->targetArchs, i); 1841 1842 SAFE_RELEASE_NULL(mkext); 1843 if (!OSKextSetArchitecture(targetArch)) { 1844 OSKextLog(/* kext */ NULL, 1845 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1846 "Can't set architecture %s to create mkext.", 1847 targetArch->name); 1848 result = EX_OSERR; 1849 goto finish; 1850 } 1851 1852 /***** 1853 * Figure out which kexts we're actually archiving. 1854 */ 1855 result = filterKextsForCache(toolArgs, archiveKexts, 1856 targetArch, fatalOut); 1857 if (result != EX_OK || *fatalOut) { 1858 goto finish; 1859 } 1860 1861 if (!CFArrayGetCount(archiveKexts)) { 1862 OSKextLog(/* kext */ NULL, 1863 kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 1864 "No kexts found for architecture %s; skipping architecture.", 1865 targetArch->name); 1866 continue; 1867 } 1868 1869 if (toolArgs->mkextVersion == 2) { 1870 mkext = OSKextCreateMkext(kCFAllocatorDefault, archiveKexts, 1871 toolArgs->volumeRootURL, 1872 kOSKextOSBundleRequiredNone, toolArgs->compress); 1873 } else if (toolArgs->mkextVersion == 1) { 1874 mkext = createMkext1ForArch(targetArch, archiveKexts, 1875 toolArgs->compress); 1876 } 1877 if (!mkext) { 1878 // OSKextCreateMkext() logs an error 1879 result = EX_OSERR; 1880 goto finish; 1881 } 1882 if (targetArch == NXGetArchInfoFromName("ppc")) { 1883 if (CFDataGetLength(mkext) > kOpenFirmwareMaxFileSize) { 1884 OSKextLog(/* kext */ NULL, 1885 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1886 "PPC archive is too large for Open Firmware; aborting."); 1887 result = EX_SOFTWARE; 1888 *fatalOut = true; 1889 goto finish; 1890 } 1891 } 1892 CFArrayAppendValue(mkexts, mkext); 1893 } 1894 1895 if (!CFArrayGetCount(mkexts)) { 1896 OSKextLog(/* kext */ NULL, 1897 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1898 "No mkext archives created."); 1899 goto finish; 1900 } 1901 1902 /* Get access and mod times of the extensions directory with most 1903 * recent mod time. We now support multiple extensions directories. 1904 */ 1905 if (toolArgs->extensionsDirTimes[1].tv_sec != 0) { 1906 result = getLatestTimesFromCFURLArray(toolArgs->repositoryURLs, 1907 extDirsTimes); 1908 if (result != EX_OK) { 1909 goto finish; 1910 } 1911 1912 /* see if an extensions dir has been changed since we started */ 1913 if (timercmp(&toolArgs->extensionsDirTimes[1], &extDirsTimes[1], !=)) { 1914 OSKextLog(/* kext */ NULL, 1915 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogFileAccessFlag, 1916 "An extensions dir has changed since starting; " 1917 "not saving cache file"); 1918 result = kKextcacheExitStale; 1919 goto finish; 1920 } 1921 /* bump kexts modtime by 1 second */ 1922 extDirsTimes[1].tv_sec++; 1923 } 1924 1925 result = writeFatFile(toolArgs->mkextPath, mkexts, toolArgs->targetArchs, 1926 MKEXT_PERMS, 1927 (toolArgs->extensionsDirTimes[1].tv_sec != 0) ? extDirsTimes : NULL); 1928 if (result != EX_OK) { 1929 goto finish; 1930 } 1931 1932 result = EX_OK; 1933 OSKextLog(/* kext */ NULL, 1934 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, 1935 "Created mkext archive %s.", toolArgs->mkextPath); 1936 1937finish: 1938 SAFE_RELEASE(archiveKexts); 1939 SAFE_RELEASE(mkexts); 1940 SAFE_RELEASE(mkext); 1941 1942#if !NO_BOOT_ROOT 1943 putVolumeForPath(toolArgs->mkextPath, result); 1944#endif /* !NO_BOOT_ROOT */ 1945 1946 return result; 1947} 1948 1949/******************************************************************************* 1950*******************************************************************************/ 1951ExitStatus 1952getFileURLModTimePlusOne( 1953 CFURLRef fileURL, 1954 struct timeval *origModTime, 1955 struct timeval cacheFileTimes[2]) 1956{ 1957 ExitStatus result = EX_SOFTWARE; 1958 char path[PATH_MAX]; 1959 1960 if (!CFURLGetFileSystemRepresentation(fileURL, /* resolveToBase */ true, 1961 (UInt8 *)path, sizeof(path))) 1962 { 1963 OSKextLogStringError(/* kext */ NULL); 1964 goto finish; 1965 } 1966 result = getFilePathModTimePlusOne(path, origModTime, cacheFileTimes); 1967 1968finish: 1969 return result; 1970} 1971 1972/******************************************************************************* 1973*******************************************************************************/ 1974ExitStatus 1975getFilePathModTimePlusOne( 1976 const char * filePath, 1977 struct timeval * origModTime, 1978 struct timeval cacheFileTimes[2]) 1979{ 1980 ExitStatus result = EX_SOFTWARE; 1981 1982 result = getFilePathTimes(filePath, cacheFileTimes); 1983 if (result != EX_OK) { 1984 goto finish; 1985 } 1986 1987 /* If asked, check to see if mod time has changed */ 1988 if (origModTime != NULL) { 1989 if (timercmp(origModTime, &cacheFileTimes[1], !=)) { 1990 OSKextLog(/* kext */ NULL, 1991 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogFileAccessFlag, 1992 "Source item %s has changed since starting; " 1993 "not saving cache file", filePath); 1994 result = kKextcacheExitStale; 1995 goto finish; 1996 } 1997 } 1998 1999 /* bump modtime by 1 second */ 2000 cacheFileTimes[1].tv_sec++; 2001 result = EX_OK; 2002finish: 2003 return result; 2004} 2005 2006/******************************************************************************* 2007*******************************************************************************/ 2008typedef struct { 2009 KextcacheArgs * toolArgs; 2010 CFMutableArrayRef kextArray; 2011 Boolean error; 2012} FilterIDContext; 2013 2014void filterKextID(const void * vValue, void * vContext) 2015{ 2016 CFStringRef kextID = (CFStringRef)vValue; 2017 FilterIDContext * context = (FilterIDContext *)vContext; 2018 OSKextRef theKext = OSKextGetKextWithIdentifier(kextID); 2019 2020 /* This should really be a fatal error but embedded counts on 2021 * having optional kexts specified by identifier. 2022 */ 2023 if (!theKext) { 2024 char kextIDCString[KMOD_MAX_NAME]; 2025 2026 CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString), 2027 kCFStringEncodingUTF8); 2028 OSKextLog(/* kext */ NULL, 2029 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2030 "Can't find kext with optional identifier %s; skipping.", kextIDCString); 2031#if 0 2032 OSKextLog(/* kext */ NULL, 2033 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2034 "Error - can't find kext with identifier %s.", kextIDCString); 2035 context->error = TRUE; 2036#endif /* 0 */ 2037 goto finish; 2038 } 2039 2040 if (kextMatchesFilter(context->toolArgs, theKext, 2041 context->toolArgs->requiredFlagsAll) && 2042 !CFArrayContainsValue(context->kextArray, 2043 RANGE_ALL(context->kextArray), theKext)) 2044 { 2045 CFArrayAppendValue(context->kextArray, theKext); 2046 } 2047 2048finish: 2049 return; 2050} 2051 2052 2053/******************************************************************************* 2054*******************************************************************************/ 2055ExitStatus filterKextsForCache( 2056 KextcacheArgs * toolArgs, 2057 CFMutableArrayRef kextArray, 2058 const NXArchInfo * arch, 2059 Boolean * fatalOut) 2060{ 2061 ExitStatus result = EX_SOFTWARE; 2062 CFMutableArrayRef firstPassArray = NULL; 2063 OSKextRequiredFlags requiredFlags; 2064 CFIndex count, i; 2065 Boolean kextSigningOnVol = false; 2066 2067 if (!createCFMutableArray(&firstPassArray, &kCFTypeArrayCallBacks)) { 2068 OSKextLogMemError(); 2069 goto finish; 2070 } 2071 2072 kextSigningOnVol = isValidKextSigningTargetVolume(toolArgs->volumeRootURL); 2073 2074 /***** 2075 * Apply filters to select the kexts. 2076 * 2077 * If kexts have been specified by identifier, those are the only kexts we are going to use. 2078 * Otherwise run through the repository and named kexts and see which ones match the filter. 2079 */ 2080 if (CFSetGetCount(toolArgs->kextIDs)) { 2081 FilterIDContext context; 2082 2083 context.toolArgs = toolArgs; 2084 context.kextArray = firstPassArray; 2085 context.error = FALSE; 2086 CFSetApplyFunction(toolArgs->kextIDs, filterKextID, &context); 2087 2088 if (context.error) { 2089 goto finish; 2090 } 2091 2092 } else { 2093 2094 /* Set up the required flags for repository kexts. If any are set from 2095 * the command line, toss in "Root" and "Console" too. 2096 */ 2097 requiredFlags = toolArgs->requiredFlagsRepositoriesOnly | 2098 toolArgs->requiredFlagsAll; 2099 if (requiredFlags) { 2100 requiredFlags |= kOSKextOSBundleRequiredRootFlag | 2101 kOSKextOSBundleRequiredConsoleFlag; 2102 } 2103 2104 count = CFArrayGetCount(toolArgs->repositoryKexts); 2105 for (i = 0; i < count; i++) { 2106 2107 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 2108 toolArgs->repositoryKexts, i); 2109 2110 if (!kextMatchesFilter(toolArgs, theKext, requiredFlags)) { 2111 2112 char kextPath[PATH_MAX]; 2113 2114 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 2115 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 2116 { 2117 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 2118 } 2119 2120 if (toolArgs->mkextPath) { 2121 OSKextLog(/* kext */ NULL, 2122 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2123 "%s does not match OSBundleRequired conditions; omitting.", 2124 kextPath); 2125 } else if (toolArgs->prelinkedKernelPath) { 2126 OSKextLog(/* kext */ NULL, 2127 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2128 "%s is not demanded by OSBundleRequired conditions.", 2129 kextPath); 2130 } 2131 continue; 2132 } 2133 2134 if (!CFArrayContainsValue(firstPassArray, RANGE_ALL(firstPassArray), theKext)) { 2135 _appendIfNewest(firstPassArray, theKext); 2136 } 2137 } 2138 2139 /* Set up the required flags for named kexts. If any are set from 2140 * the command line, toss in "Root" and "Console" too. 2141 */ 2142 requiredFlags = toolArgs->requiredFlagsAll; 2143 if (requiredFlags) { 2144 requiredFlags |= kOSKextOSBundleRequiredRootFlag | 2145 kOSKextOSBundleRequiredConsoleFlag; 2146 } 2147 2148 count = CFArrayGetCount(toolArgs->namedKexts); 2149 for (i = 0; i < count; i++) { 2150 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 2151 toolArgs->namedKexts, i); 2152 2153 if (!kextMatchesFilter(toolArgs, theKext, requiredFlags)) { 2154 2155 char kextPath[PATH_MAX]; 2156 2157 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 2158 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 2159 { 2160 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 2161 } 2162 2163 if (toolArgs->mkextPath) { 2164 OSKextLog(/* kext */ NULL, 2165 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2166 "%s does not match OSBundleRequired conditions; omitting.", 2167 kextPath); 2168 } else if (toolArgs->prelinkedKernelPath) { 2169 OSKextLog(/* kext */ NULL, 2170 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2171 "%s is not demanded by OSBundleRequired conditions.", 2172 kextPath); 2173 } 2174 continue; 2175 } 2176 2177 if (!CFArrayContainsValue(firstPassArray, RANGE_ALL(firstPassArray), theKext)) { 2178 _appendIfNewest(firstPassArray, theKext); 2179 } 2180 } 2181 } 2182 2183 /***** 2184 * Take all the kexts that matched the filters above and check them for problems. 2185 */ 2186 CFArrayRemoveAllValues(kextArray); 2187 2188 count = CFArrayGetCount(firstPassArray); 2189 if (count) { 2190 Boolean earlyBoot = false; 2191 2192 if (callSecKeychainMDSInstall() != 0) { 2193 // this should never fail, so bail if it does. 2194 goto finish; 2195 } 2196 // not perfect, but we check to see if kextd is running to determine 2197 // if we are in early boot. 2198 earlyBoot = (isKextdRunning() == false); 2199 OSKextIsInExcludeList(NULL, false); // prime the exclude list cache 2200 isInExceptionList(NULL, NULL, false); // prime the exception list cache 2201 for (i = count - 1; i >= 0; i--) { 2202 OSStatus sigResult; 2203 char kextPath[PATH_MAX]; 2204 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 2205 firstPassArray, i); 2206 2207 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 2208 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 2209 { 2210 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 2211 } 2212 2213 /* Skip kexts we have no interest in for the current arch. 2214 */ 2215 if (!OSKextSupportsArchitecture(theKext, arch)) { 2216 OSKextLog(/* kext */ NULL, 2217 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2218 "%s doesn't support architecture '%s'; skipping.", kextPath, 2219 arch->name); 2220 continue; 2221 } 2222 2223 if (!OSKextIsValid(theKext)) { 2224 // xxx - should also use kOSKextLogArchiveFlag? 2225 OSKextLog(/* kext */ NULL, 2226 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2227 kOSKextLogValidationFlag | kOSKextLogGeneralFlag, 2228 "%s is not valid; omitting.", kextPath); 2229 if (toolArgs->printTestResults) { 2230 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2231 } 2232 continue; 2233 } 2234 2235 if (!toolArgs->skipAuthentication && !OSKextIsAuthentic(theKext)) { 2236 OSKextLog(/* kext */ NULL, 2237 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2238 kOSKextLogAuthenticationFlag | kOSKextLogGeneralFlag, 2239 "%s has incorrect permissions; omitting.", kextPath); 2240 if (toolArgs->printTestResults) { 2241 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2242 } 2243 continue; 2244 } 2245 2246 if (OSKextIsInExcludeList(theKext, true)) { 2247 /* send alert about kext and message trace it 2248 */ 2249 addKextToAlertDict(&sExcludedKextAlertDict, theKext); 2250 messageTraceExcludedKext(theKext); 2251 OSKextLog(/* kext */ NULL, 2252 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2253 kOSKextLogValidationFlag | kOSKextLogGeneralFlag, 2254 "%s is in exclude list; omitting.", kextPath); 2255 if (toolArgs->printTestResults) { 2256 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2257 } 2258 continue; 2259 } 2260 2261 if (!OSKextResolveDependencies(theKext)) { 2262 OSKextLog(/* kext */ NULL, 2263 kOSKextLogWarningLevel | kOSKextLogArchiveFlag | 2264 kOSKextLogDependenciesFlag | kOSKextLogGeneralFlag, 2265 "%s is missing dependencies (including anyway; " 2266 "dependencies may be available from elsewhere)", kextPath); 2267 if (toolArgs->printTestResults) { 2268 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2269 } 2270 } 2271 2272 if (kextSigningOnVol 2273 && (sigResult = checkKextSignature(theKext, true, earlyBoot)) != 0 ) { 2274 2275 if (isInvalidSignatureAllowed()) { 2276 OSKextLogCFString(NULL, 2277 kOSKextLogErrorLevel | kOSKextLogLoadFlag, 2278 CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext %s"), 2279 (long)sigResult, (long)sigResult, kextPath); 2280 } 2281 else { 2282 OSKextLog(/* kext */ NULL, 2283 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2284 kOSKextLogAuthenticationFlag | kOSKextLogGeneralFlag, 2285 "%s has invalid signature; omitting.", 2286 kextPath); 2287 if (toolArgs->printTestResults) { 2288 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2289 } 2290 continue; 2291 } 2292 } 2293 2294 if (!CFArrayContainsValue(kextArray, RANGE_ALL(kextArray), theKext)) { 2295 CFArrayAppendValue(kextArray, theKext); 2296 } 2297 } // for loop... 2298 } // count > 0 2299 2300 if (CFArrayGetCount(kextArray)) { 2301 recordKextLoadListForMT(kextArray); 2302 } 2303 2304 result = EX_OK; 2305 2306finish: 2307 return result; 2308} 2309 2310 2311/* Append the kext if it is the newest bundle / version. Remove older if found. 2312 */ 2313static void _appendIfNewest(CFMutableArrayRef theArray, OSKextRef theKext) 2314{ 2315 CFStringRef theBundleID; // do not release 2316 CFStringRef theBundleVersion; // do not release 2317 OSKextVersion theKextVersion = -1; 2318 CFIndex myCount, i; 2319 2320 theBundleID = OSKextGetIdentifier(theKext); 2321 theBundleVersion = OSKextGetValueForInfoDictionaryKey( 2322 theKext, 2323 kCFBundleVersionKey ); 2324 if (theBundleVersion == NULL) { 2325 return; 2326 } 2327 theKextVersion = OSKextParseVersionCFString(theBundleVersion); 2328 if (theKextVersion == -1) { 2329 return; 2330 } 2331 2332 myCount = CFArrayGetCount(theArray); 2333 for (i = 0; i < myCount; i++) { 2334 OSKextRef myKext; // do not release 2335 CFStringRef myBundleID; // do not release 2336 CFStringRef myBundleVersion; // do not release 2337 OSKextVersion myKextVersion = -1; 2338 2339 myKext = (OSKextRef) CFArrayGetValueAtIndex(theArray, i); 2340 myBundleID = OSKextGetIdentifier(myKext); 2341 2342 if ( CFStringCompare(myBundleID, theBundleID, 0) == kCFCompareEqualTo ) { 2343 myBundleVersion = OSKextGetValueForInfoDictionaryKey( 2344 myKext, 2345 kCFBundleVersionKey ); 2346 if (myBundleVersion == NULL) continue; 2347 myKextVersion = OSKextParseVersionCFString(myBundleVersion); 2348 if (myKextVersion > 0 && myKextVersion > theKextVersion ) { 2349 // already have newer version of this kext, do not add it 2350 OSKextLogCFString(NULL, 2351 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2352 CFSTR("%s: found newer, skipping %@"), 2353 __func__, theKext); 2354 return; 2355 } 2356 if (myKextVersion > 0 && myKextVersion == theKextVersion ) { 2357 // already have same version of this kext, do not add it 2358 OSKextLogCFString(NULL, 2359 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2360 CFSTR("%s: found dup, skipping %@"), 2361 __func__, theKext); 2362 return; 2363 } 2364 if (myKextVersion > 0 && myKextVersion < theKextVersion ) { 2365 // found older version of this kext, remove it and add this one 2366 OSKextLogCFString(NULL, 2367 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2368 CFSTR("%s: found older, removing %@"), 2369 __func__, myKext); 2370 CFArrayRemoveValueAtIndex(theArray, i); 2371 break; 2372 } 2373 } 2374 } 2375 2376 CFArrayAppendValue(theArray, theKext); 2377 return; 2378} 2379 2380/* We only want to check code signatures for volumes running 10.9 or 2381 * later version of OS (which means a Kernelcache v1.3 or later) 2382 */ 2383static Boolean isValidKextSigningTargetVolume(CFURLRef theVolRootURL) 2384{ 2385 Boolean myResult = false; 2386 CFDictionaryRef myDict = NULL; // must release 2387 CFDictionaryRef postBootPathsDict = NULL; // do not release 2388 2389 myDict = copyBootCachesDictForURL(theVolRootURL); 2390 if (myDict) { 2391 postBootPathsDict = (CFDictionaryRef) 2392 CFDictionaryGetValue(myDict, kBCPostBootKey); 2393 2394 if (postBootPathsDict && 2395 CFGetTypeID(postBootPathsDict) == CFDictionaryGetTypeID()) { 2396 2397 if (CFDictionaryContainsKey(postBootPathsDict, kBCKernelcacheV3Key)) { 2398 myResult = true; 2399 } 2400 } 2401 } 2402 SAFE_RELEASE(myDict); 2403 2404 return(myResult); 2405} 2406 2407/* Make sure target volume can support fast (lzvn) compression, as well as current runtime library environment */ 2408 2409static Boolean wantsFastLibCompressionForTargetVolume(CFURLRef theVolRootURL) 2410{ 2411 Boolean myResult = false; 2412 CFDictionaryRef myDict = NULL; // must release 2413 CFDictionaryRef postBootPathsDict = NULL; // do not release 2414 CFDictionaryRef kernelCacheDict = NULL; // do not release 2415 2416 myDict = copyBootCachesDictForURL(theVolRootURL); 2417 if (myDict) { 2418 postBootPathsDict = (CFDictionaryRef) 2419 CFDictionaryGetValue(myDict, kBCPostBootKey); 2420 2421 if (postBootPathsDict && 2422 CFGetTypeID(postBootPathsDict) == CFDictionaryGetTypeID()) { 2423 2424 kernelCacheDict = (CFDictionaryRef) 2425 CFDictionaryGetValue(postBootPathsDict, kBCKernelcacheV3Key); 2426 2427 if (kernelCacheDict && 2428 CFGetTypeID(kernelCacheDict) == CFDictionaryGetTypeID()) { 2429 CFStringRef myTempStr; // do not release 2430 2431 myTempStr = (CFStringRef) 2432 CFDictionaryGetValue(kernelCacheDict, 2433 kBCPreferredCompressionKey); 2434 2435 if (myTempStr && CFGetTypeID(myTempStr) == CFStringGetTypeID()) { 2436 if (CFStringCompare(myTempStr, CFSTR("lzvn"), 0) == kCFCompareEqualTo) { 2437 myResult = true; 2438 } 2439 } 2440 } // kernelCacheDict 2441 } // postBootPathsDict 2442 } // myDict 2443 2444 SAFE_RELEASE(myDict); 2445 2446 /* We may not be able to generate FastLib-compressed files */ 2447 if (myResult && !supportsFastLibCompression()) { 2448 myResult = false; 2449 } 2450 2451 return(myResult); 2452} 2453 2454/******************************************************************************* 2455*******************************************************************************/ 2456Boolean 2457kextMatchesFilter( 2458 KextcacheArgs * toolArgs, 2459 OSKextRef theKext, 2460 OSKextRequiredFlags requiredFlags) 2461{ 2462 Boolean result = false; 2463 Boolean needLoadedKextInfo = toolArgs->needLoadedKextInfo && 2464 (OSKextGetArchitecture() == OSKextGetRunningKernelArchitecture()); 2465 2466 if (needLoadedKextInfo) { 2467 result = (requiredFlags && OSKextMatchesRequiredFlags(theKext, requiredFlags)) || 2468 CFArrayContainsValue(toolArgs->loadedKexts, RANGE_ALL(toolArgs->loadedKexts), theKext); 2469 } else { 2470 result = OSKextMatchesRequiredFlags(theKext, requiredFlags); 2471 } 2472 2473 return result; 2474} 2475 2476/******************************************************************************* 2477 * Creates a list of architectures to generate prelinked kernel slices for by 2478 * selecting the requested architectures for which the kernel has a slice. 2479 * Warns when a requested architecture does not have a corresponding kernel 2480 * slice. 2481 *******************************************************************************/ 2482ExitStatus 2483createPrelinkedKernelArchs( 2484 KextcacheArgs * toolArgs, 2485 CFMutableArrayRef * prelinkArchsOut) 2486{ 2487 ExitStatus result = EX_OSERR; 2488 CFMutableArrayRef kernelArchs = NULL; // must release 2489 CFMutableArrayRef prelinkArchs = NULL; // must release 2490 const NXArchInfo * targetArch = NULL; // do not free 2491 u_int i = 0; 2492 2493 result = readFatFileArchsWithPath(toolArgs->kernelPath, &kernelArchs); 2494 if (result != EX_OK) { 2495 goto finish; 2496 } 2497 2498 prelinkArchs = CFArrayCreateMutableCopy(kCFAllocatorDefault, 2499 /* capacity */ 0, toolArgs->targetArchs); 2500 if (!prelinkArchs) { 2501 OSKextLogMemError(); 2502 result = EX_OSERR; 2503 goto finish; 2504 } 2505 2506 for (i = 0; i < CFArrayGetCount(prelinkArchs); ++i) { 2507 targetArch = CFArrayGetValueAtIndex(prelinkArchs, i); 2508 if (!CFArrayContainsValue(kernelArchs, 2509 RANGE_ALL(kernelArchs), targetArch)) 2510 { 2511 OSKextLog(/* kext */ NULL, 2512 kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 2513 "Kernel file %s does not contain requested arch: %s", 2514 toolArgs->kernelPath, targetArch->name); 2515 CFArrayRemoveValueAtIndex(prelinkArchs, i); 2516 i--; 2517 continue; 2518 } 2519 } 2520 2521 *prelinkArchsOut = (CFMutableArrayRef) CFRetain(prelinkArchs); 2522 result = EX_OK; 2523 2524finish: 2525 SAFE_RELEASE(kernelArchs); 2526 SAFE_RELEASE(prelinkArchs); 2527 2528 return result; 2529} 2530 2531/******************************************************************************* 2532 * If the existing prelinked kernel has a valid timestamp, this reads the slices 2533 * out of that prelinked kernel so we don't have to regenerate them. 2534 *******************************************************************************/ 2535ExitStatus 2536createExistingPrelinkedSlices( 2537 KextcacheArgs * toolArgs, 2538 CFMutableArrayRef * existingSlicesOut, 2539 CFMutableArrayRef * existingArchsOut) 2540{ 2541 struct timeval existingFileTimes[2]; 2542 struct timeval prelinkFileTimes[2]; 2543 ExitStatus result = EX_SOFTWARE; 2544 2545 /* If we aren't updating the system prelinked kernel, then we don't want 2546 * to reuse any existing slices. 2547 */ 2548 if (!toolArgs->needDefaultPrelinkedKernelInfo) { 2549 result = EX_OK; 2550 goto finish; 2551 } 2552 2553 bzero(&existingFileTimes, sizeof(existingFileTimes)); 2554 bzero(&prelinkFileTimes, sizeof(prelinkFileTimes)); 2555 2556 result = getFilePathTimes(toolArgs->prelinkedKernelPath, 2557 existingFileTimes); 2558 if (result != EX_OK) { 2559 goto finish; 2560 } 2561 2562 result = getExpectedPrelinkedKernelModTime(toolArgs, 2563 prelinkFileTimes, NULL); 2564 if (result != EX_OK) { 2565 goto finish; 2566 } 2567 2568 /* We are testing that the existing prelinked kernel still has a valid 2569 * timestamp by comparing it to the timestamp we are going to use for 2570 * the new prelinked kernel. If they are equal, we can reuse slices 2571 * from the existing prelinked kernel. 2572 */ 2573 if (!timevalcmp(&existingFileTimes[1], &prelinkFileTimes[1], ==)) { 2574 result = EX_SOFTWARE; 2575 goto finish; 2576 } 2577 2578 result = readMachOSlices(toolArgs->prelinkedKernelPath, 2579 existingSlicesOut, existingArchsOut, NULL, NULL); 2580 if (result != EX_OK) { 2581 existingSlicesOut = NULL; 2582 existingArchsOut = NULL; 2583 goto finish; 2584 } 2585 2586 result = EX_OK; 2587 2588finish: 2589 return result; 2590} 2591 2592static void setVolumeRootInAlertDict(CFURLRef theURL, 2593 CFMutableDictionaryRef theDict); 2594 2595/******************************************************************************* 2596*******************************************************************************/ 2597ExitStatus 2598createPrelinkedKernel( 2599 KextcacheArgs * toolArgs) 2600{ 2601 ExitStatus result = EX_OSERR; 2602 struct timeval prelinkFileTimes[2]; 2603 CFMutableArrayRef generatedArchs = NULL; // must release 2604 CFMutableArrayRef generatedSymbols = NULL; // must release 2605 CFMutableArrayRef existingArchs = NULL; // must release 2606 CFMutableArrayRef existingSlices = NULL; // must release 2607 CFMutableArrayRef prelinkArchs = NULL; // must release 2608 CFMutableArrayRef prelinkSlices = NULL; // must release 2609 CFDataRef prelinkSlice = NULL; // must release 2610 CFDictionaryRef sliceSymbols = NULL; // must release 2611 const NXArchInfo * targetArch = NULL; // do not free 2612 Boolean updateModTime = false; 2613 u_int numArchs = 0; 2614 u_int i = 0; 2615 int j = 0; 2616 2617 bzero(&prelinkFileTimes, sizeof(prelinkFileTimes)); 2618 2619#if !NO_BOOT_ROOT 2620 /* Try a lock on the volume for the prelinked kernel being updated. 2621 * The lock prevents kextd from starting up a competing kextcache. 2622 */ 2623 if (!getenv("_com_apple_kextd_skiplocks")) { 2624 // xxx - updateBoots * related should return only sysexit-type values, not errno 2625 result = takeVolumeForPath(toolArgs->prelinkedKernelPath); 2626 if (result != EX_OK) { 2627 goto finish; 2628 } 2629 } 2630#endif /* !NO_BOOT_ROOT */ 2631 2632 result = createPrelinkedKernelArchs(toolArgs, &prelinkArchs); 2633 if (result != EX_OK) { 2634 goto finish; 2635 } 2636 numArchs = (u_int)CFArrayGetCount(prelinkArchs); 2637 2638 /* If we're generating symbols, we'll regenerate all slices. 2639 */ 2640 if (!toolArgs->symbolDirURL) { 2641 result = createExistingPrelinkedSlices(toolArgs, 2642 &existingSlices, &existingArchs); 2643 if (result != EX_OK) { 2644 SAFE_RELEASE_NULL(existingSlices); 2645 SAFE_RELEASE_NULL(existingArchs); 2646 } 2647 } 2648 2649 prelinkSlices = CFArrayCreateMutable(kCFAllocatorDefault, 2650 numArchs, &kCFTypeArrayCallBacks); 2651 generatedSymbols = CFArrayCreateMutable(kCFAllocatorDefault, 2652 numArchs, &kCFTypeArrayCallBacks); 2653 generatedArchs = CFArrayCreateMutable(kCFAllocatorDefault, 2654 numArchs, NULL); 2655 if (!prelinkSlices || !generatedSymbols || !generatedArchs) { 2656 OSKextLogMemError(); 2657 result = EX_OSERR; 2658 goto finish; 2659 } 2660 2661 for (i = 0; i < numArchs; i++) { 2662 targetArch = CFArrayGetValueAtIndex(prelinkArchs, i); 2663 2664 SAFE_RELEASE_NULL(prelinkSlice); 2665 SAFE_RELEASE_NULL(sliceSymbols); 2666 2667 /* We always create a new prelinked kernel for the current 2668 * running architecture if asked, but we'll reuse existing slices 2669 * for other architectures if possible. 2670 */ 2671 if (existingArchs && 2672 targetArch != OSKextGetRunningKernelArchitecture()) 2673 { 2674 j = (int)CFArrayGetFirstIndexOfValue(existingArchs, 2675 RANGE_ALL(existingArchs), targetArch); 2676 if (j != -1) { 2677 prelinkSlice = CFArrayGetValueAtIndex(existingSlices, j); 2678 CFArrayAppendValue(prelinkSlices, prelinkSlice); 2679 prelinkSlice = NULL; 2680 OSKextLog(/* kext */ NULL, 2681 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2682 "Using existing prelinked slice for arch %s", 2683 targetArch->name); 2684 continue; 2685 } 2686 } 2687 2688 OSKextLog(/* kext */ NULL, 2689 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2690 "Generating a new prelinked slice for arch %s", 2691 targetArch->name); 2692 2693 result = createPrelinkedKernelForArch(toolArgs, &prelinkSlice, 2694 &sliceSymbols, targetArch); 2695 if (result != EX_OK) { 2696 goto finish; 2697 } 2698 2699 CFArrayAppendValue(prelinkSlices, prelinkSlice); 2700 CFArrayAppendValue(generatedSymbols, sliceSymbols); 2701 CFArrayAppendValue(generatedArchs, targetArch); 2702 } 2703 2704 result = getExpectedPrelinkedKernelModTime(toolArgs, 2705 prelinkFileTimes, &updateModTime); 2706 if (result != EX_OK) { 2707 goto finish; 2708 } 2709 2710#if 0 2711 OSKextLogCFString(NULL, 2712 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 2713 CFSTR("%s: writing to %s"), 2714 __func__, toolArgs->prelinkedKernelPath); 2715 if (updateModTime) { 2716 OSKextLogCFString(NULL, 2717 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 2718 CFSTR("%s: setting mod time to %ld"), 2719 __func__, prelinkFileTimes[1].tv_sec); 2720 } 2721#endif 2722 2723 result = writeFatFile(toolArgs->prelinkedKernelPath, prelinkSlices, 2724 prelinkArchs, MKEXT_PERMS, 2725 (updateModTime) ? prelinkFileTimes : NULL); 2726 if (result != EX_OK) { 2727 goto finish; 2728 } 2729 2730#if 1 // 17821398 2731 if (needsPrelinkedKernelCopy(toolArgs)) { 2732 CFMutableStringRef myNewString = NULL; 2733 CFStringRef myTempStr = NULL; 2734 CFIndex myReplacedCount = 0; 2735 2736 /* convert "kernelcache" to "prelinkedkernel", adjusting paths too. 2737 * We do best effort to make a copy, but failure is not fatal at this 2738 * point. 2739 */ 2740 do { 2741 myTempStr = CFStringCreateWithFileSystemRepresentation( 2742 nil, 2743 toolArgs->prelinkedKernelPath); 2744 if (myTempStr == NULL) break; 2745 2746 myNewString = CFStringCreateMutableCopy(kCFAllocatorDefault, 2747 0, 2748 myTempStr); 2749 if (myNewString == NULL) break; 2750 2751 /* We will replace: 2752 * "/System/Library/Caches/com.apple.kext.caches/Startup/kernelcache" 2753 * with: 2754 * "/System/Library/PrelinkedKernels/prelinkedkernel" 2755 * which will preserve any ".SUFFIX" like ".development" 2756 */ 2757 myReplacedCount = CFStringFindAndReplace( 2758 myNewString, 2759 CFSTR(k_kernelcacheFilePath), // find this string 2760 CFSTR(k_prelinkedkernelFilePath), // replace with this 2761 CFRangeMake(0, CFStringGetLength(myNewString)), 2762 0); 2763 2764 if (myReplacedCount == 1) { 2765 ExitStatus myErr; 2766 char tempbuf[PATH_MAX]; 2767 2768 if (CFStringGetFileSystemRepresentation(myNewString, 2769 tempbuf, 2770 sizeof(tempbuf)) == false) { 2771 break; 2772 } 2773 2774 /* now write another copy of prelinked kernel to new location at 2775 * "/System/Library/PrelinkedKernels" 2776 */ 2777 myErr = writeFatFile(&tempbuf[0], prelinkSlices, 2778 prelinkArchs, MKEXT_PERMS, 2779 (updateModTime) ? prelinkFileTimes : NULL); 2780 if (myErr == EX_OK) { 2781 OSKextLog(/* kext */ NULL, 2782 kOSKextLogGeneralFlag | kOSKextLogBasicLevel, 2783 "Created prelinked kernel copy \"%s\"", 2784 tempbuf); 2785 } 2786 } // myReplacedCount 2787 } while(0); 2788 2789 SAFE_RELEASE(myNewString); 2790 SAFE_RELEASE(myTempStr); 2791 } 2792#endif 2793 2794 if (toolArgs->symbolDirURL) { 2795 result = writePrelinkedSymbols(toolArgs->symbolDirURL, 2796 generatedSymbols, generatedArchs); 2797 if (result != EX_OK) { 2798 goto finish; 2799 } 2800 } 2801 2802 OSKextLog(/* kext */ NULL, 2803 kOSKextLogGeneralFlag | kOSKextLogBasicLevel, 2804 "Created prelinked kernel \"%s\"", 2805 toolArgs->prelinkedKernelPath); 2806 if (toolArgs->kernelPath) { 2807 OSKextLog(/* kext */ NULL, 2808 kOSKextLogGeneralFlag | kOSKextLogBasicLevel, 2809 "Created prelinked kernel using \"%s\"", 2810 toolArgs->kernelPath); 2811 } 2812 2813 result = EX_OK; 2814 2815finish: 2816 if (sNoLoadKextAlertDict) { 2817 /* notify kextd that we have some nonsigned kexts going into the 2818 * kernel cache. 2819 */ 2820 if (toolArgs->volumeRootURL) { 2821 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2822 sNoLoadKextAlertDict); 2823 } 2824 postNoteAboutKexts(CFSTR("No Load Kext Notification"), 2825 sNoLoadKextAlertDict ); 2826 } 2827 2828 if (sRevokedKextAlertDict) { 2829 /* notify kextd that we have some kexts with revoked certificate. 2830 */ 2831 if (toolArgs->volumeRootURL) { 2832 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2833 sRevokedKextAlertDict); 2834 } 2835 postNoteAboutKexts(CFSTR("Revoked Cert Kext Notification"), 2836 sRevokedKextAlertDict ); 2837 } 2838#if 0 // not yet 2839 if (sUnsignedKextAlertDict) { 2840 /* notify kextd that we have some unsigned kexts going into the 2841 * kernel cache. 2842 */ 2843 if (toolArgs->volumeRootURL) { 2844 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2845 sUnsignedKextAlertDict); 2846 } 2847 postNoteAboutKexts(CFSTR("Unsigned Kext Notification"), 2848 sUnsignedKextAlertDict); 2849 } 2850#endif 2851 2852 if (sInvalidSignedKextAlertDict) { 2853 /* notify kextd that we have some invalid signed kexts going into the 2854 * kernel cache. 2855 */ 2856 if (toolArgs->volumeRootURL) { 2857 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2858 sInvalidSignedKextAlertDict); 2859 } 2860 postNoteAboutKexts(CFSTR("Invalid Signature Kext Notification"), 2861 sInvalidSignedKextAlertDict); 2862 } 2863 2864 if (sExcludedKextAlertDict) { 2865 /* notify kextd that we have some excluded kexts going into the 2866 * kernel cache. 2867 */ 2868 if (toolArgs->volumeRootURL) { 2869 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2870 sExcludedKextAlertDict); 2871 } 2872 postNoteAboutKexts(CFSTR("Excluded Kext Notification"), 2873 sExcludedKextAlertDict); 2874 } 2875 2876 SAFE_RELEASE(generatedArchs); 2877 SAFE_RELEASE(generatedSymbols); 2878 SAFE_RELEASE(existingArchs); 2879 SAFE_RELEASE(existingSlices); 2880 SAFE_RELEASE(prelinkArchs); 2881 SAFE_RELEASE(prelinkSlices); 2882 SAFE_RELEASE(prelinkSlice); 2883 SAFE_RELEASE(sliceSymbols); 2884 2885#if !NO_BOOT_ROOT 2886 putVolumeForPath(toolArgs->prelinkedKernelPath, result); 2887#endif /* !NO_BOOT_ROOT */ 2888 2889 return result; 2890} 2891 2892static void setVolumeRootInAlertDict(CFURLRef theURL, 2893 CFMutableDictionaryRef theDict) 2894{ 2895 CFStringRef myVolRoot; 2896 2897 myVolRoot = (CFStringRef) 2898 CFDictionaryGetValue(theDict, 2899 CFSTR("VolRootKey")); 2900 if (myVolRoot == NULL) { 2901 myVolRoot = CFURLCopyFileSystemPath(theURL, 2902 kCFURLPOSIXPathStyle); 2903 if (myVolRoot) { 2904 CFDictionarySetValue(theDict, 2905 CFSTR("VolRootKey"), 2906 myVolRoot); 2907 SAFE_RELEASE(myVolRoot); 2908 } 2909 } 2910 return; 2911} 2912 2913/******************************************************************************* 2914*******************************************************************************/ 2915ExitStatus createPrelinkedKernelForArch( 2916 KextcacheArgs * toolArgs, 2917 CFDataRef * prelinkedKernelOut, 2918 CFDictionaryRef * prelinkedSymbolsOut, 2919 const NXArchInfo * archInfo) 2920{ 2921 ExitStatus result = EX_OSERR; 2922 CFMutableArrayRef prelinkKexts = NULL; 2923 CFDataRef kernelImage = NULL; 2924 CFDataRef prelinkedKernel = NULL; 2925 uint32_t flags = 0; 2926 Boolean fatalOut = false; 2927 Boolean kernelSupportsKASLR = false; 2928 macho_seek_result machoResult; 2929 const UInt8 * kernelStart; 2930 const UInt8 * kernelEnd; 2931 2932 /* Retrieve the kernel image for the requested architecture. 2933 */ 2934 kernelImage = readMachOSliceForArch(toolArgs->kernelPath, archInfo, /* checkArch */ TRUE); 2935 if (!kernelImage) { 2936 OSKextLog(/* kext */ NULL, 2937 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 2938 "Failed to read kernel file."); 2939 goto finish; 2940 } 2941 2942 /* Set the architecture in the OSKext library */ 2943 if (!OSKextSetArchitecture(archInfo)) { 2944 OSKextLog(/* kext */ NULL, 2945 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2946 "Can't set architecture %s to create prelinked kernel.", 2947 archInfo->name); 2948 result = EX_OSERR; 2949 goto finish; 2950 } 2951 2952 /***** 2953 * Figure out which kexts we're actually archiving. 2954 * This uses toolArgs->allKexts, which must already be created. 2955 */ 2956 prelinkKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0, 2957 &kCFTypeArrayCallBacks); 2958 if (!prelinkKexts) { 2959 OSKextLogMemError(); 2960 result = EX_OSERR; 2961 goto finish; 2962 } 2963 2964 result = filterKextsForCache(toolArgs, prelinkKexts, 2965 archInfo, &fatalOut); 2966 if (result != EX_OK || fatalOut) { 2967 goto finish; 2968 } 2969 2970 result = EX_OSERR; 2971 2972 if (!CFArrayGetCount(prelinkKexts)) { 2973 OSKextLog(/* kext */ NULL, 2974 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 2975 "No kexts found for architecture %s.", 2976 archInfo->name); 2977 goto finish; 2978 } 2979 2980 /* Create the prelinked kernel from the given kernel and kexts */ 2981 2982 flags |= (toolArgs->noLinkFailures) ? kOSKextKernelcacheNeedAllFlag : 0; 2983 flags |= (toolArgs->skipAuthentication) ? kOSKextKernelcacheSkipAuthenticationFlag : 0; 2984 flags |= (toolArgs->printTestResults) ? kOSKextKernelcachePrintDiagnosticsFlag : 0; 2985 flags |= (toolArgs->includeAllPersonalities) ? kOSKextKernelcacheIncludeAllPersonalitiesFlag : 0; 2986 flags |= (toolArgs->stripSymbols) ? kOSKextKernelcacheStripSymbolsFlag : 0; 2987 2988 kernelStart = CFDataGetBytePtr(kernelImage); 2989 kernelEnd = kernelStart + CFDataGetLength(kernelImage) - 1; 2990 machoResult = macho_find_dysymtab(kernelStart, kernelEnd, NULL); 2991 /* this kernel supports KASLR if there is a LC_DYSYMTAB load command */ 2992 kernelSupportsKASLR = (machoResult == macho_seek_result_found); 2993 if (kernelSupportsKASLR) { 2994 flags |= kOSKextKernelcacheKASLRFlag; 2995 } 2996 2997 prelinkedKernel = OSKextCreatePrelinkedKernel(kernelImage, prelinkKexts, 2998 toolArgs->volumeRootURL, flags, prelinkedSymbolsOut); 2999 if (!prelinkedKernel) { 3000 OSKextLog(/* kext */ NULL, 3001 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 3002 "Failed to generate prelinked kernel."); 3003 result = EX_OSERR; 3004 goto finish; 3005 } 3006 3007 /* Compress the prelinked kernel if needed */ 3008 3009 if (toolArgs->compress) { 3010 Boolean wantsFastLib = wantsFastLibCompressionForTargetVolume(toolArgs->volumeRootURL); 3011 uint32_t compressionType = wantsFastLib ? COMP_TYPE_FASTLIB : COMP_TYPE_LZSS; 3012 3013 *prelinkedKernelOut = compressPrelinkedSlice(compressionType, 3014 prelinkedKernel, 3015 kernelSupportsKASLR); 3016 } else { 3017 *prelinkedKernelOut = CFRetain(prelinkedKernel); 3018 } 3019 3020 if (!*prelinkedKernelOut) { 3021 goto finish; 3022 } 3023 3024 result = EX_OK; 3025 3026finish: 3027 SAFE_RELEASE(kernelImage); 3028 SAFE_RELEASE(prelinkKexts); 3029 SAFE_RELEASE(prelinkedKernel); 3030 3031 return result; 3032} 3033 3034/***************************************************************************** 3035 *****************************************************************************/ 3036ExitStatus 3037getExpectedPrelinkedKernelModTime( 3038 KextcacheArgs * toolArgs, 3039 struct timeval cacheFileTimes[2], 3040 Boolean * updateModTimeOut) 3041{ 3042 struct timeval kextTimes[2]; 3043 struct timeval kernelTimes[2]; 3044 ExitStatus result = EX_SOFTWARE; 3045 Boolean updateModTime = false; 3046 3047 /* bail out if we don't have modtimes for extensions directory or kernel file 3048 */ 3049 if (toolArgs->extensionsDirTimes[1].tv_sec == 0 || 3050 toolArgs->kernelTimes[1].tv_sec == 0) { 3051 result = EX_OK; 3052 goto finish; 3053 } 3054 3055 result = getLatestTimesFromCFURLArray(toolArgs->repositoryURLs, 3056 kextTimes); 3057 if (result != EX_OK) { 3058 goto finish; 3059 } 3060 3061 /* bump kexts modtime by 1 second */ 3062 kextTimes[1].tv_sec++; 3063 3064 /* Check kernel mod time */ 3065 result = getFilePathModTimePlusOne(toolArgs->kernelPath, 3066 &toolArgs->kernelTimes[1], kernelTimes); 3067 if (result != EX_OK) { 3068 goto finish; 3069 } 3070#if 0 3071 OSKextLogCFString(NULL, 3072 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 3073 CFSTR("%s: kernelPath %s"), 3074 __func__, toolArgs->kernelPath); 3075 OSKextLogCFString(NULL, 3076 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 3077 CFSTR("%s: %ld <- latest kext mod time"), 3078 __func__, kextTimes[1].tv_sec); 3079 OSKextLogCFString(NULL, 3080 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 3081 CFSTR("%s: %ld <- latest kernels mod time"), 3082 __func__, kernelTimes[1].tv_sec); 3083#endif 3084 3085 /* Get the access and mod times of the latest modified of the kernel, 3086 * or kext repositories. For example: 3087 * kextTimes -> /System/Library/Extensions/ and /Library/Extensions/ 3088 * kernelTimes -> /System/Library/Kernels/kernel 3089 * cacheFileTimes -> /S/L/Caches/com.apple.kext.caches/Startup/kernelcache 3090 */ 3091 cacheFileTimes[0].tv_sec = kextTimes[0].tv_sec; // access time 3092 cacheFileTimes[0].tv_usec = kextTimes[0].tv_usec; 3093 cacheFileTimes[1].tv_sec = kextTimes[1].tv_sec; // mod time 3094 cacheFileTimes[1].tv_usec = kextTimes[1].tv_usec; 3095 if (timercmp(&kernelTimes[1], &kextTimes[1], >)) { 3096 cacheFileTimes[0].tv_sec = kernelTimes[0].tv_sec; // access time 3097 cacheFileTimes[0].tv_usec = kernelTimes[0].tv_usec; 3098 cacheFileTimes[1].tv_sec = kernelTimes[1].tv_sec; // mod time 3099 cacheFileTimes[1].tv_usec = kernelTimes[1].tv_usec; 3100 } 3101#if 0 3102 OSKextLogCFString(NULL, 3103 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 3104 CFSTR("%s: %ld <- using this mod time"), 3105 __func__, cacheFileTimes[1].tv_sec); 3106#endif 3107 3108 /* Set the mod time of the kernelcache relative to the kernel */ 3109 updateModTime = true; 3110 result = EX_OK; 3111 3112finish: 3113 if (updateModTimeOut) *updateModTimeOut = updateModTime; 3114 3115 return result; 3116} 3117 3118/********************************************************************* 3119 *********************************************************************/ 3120ExitStatus 3121compressPrelinkedKernel( 3122 CFURLRef volumeRootURL, 3123 const char * prelinkPath, 3124 Boolean compress) 3125{ 3126 ExitStatus result = EX_SOFTWARE; 3127 struct timeval prelinkedKernelTimes[2]; 3128 CFMutableArrayRef prelinkedSlices = NULL; // must release 3129 CFMutableArrayRef prelinkedArchs = NULL; // must release 3130 CFDataRef prelinkedSlice = NULL; // must release 3131 const NXArchInfo * archInfo = NULL; // do not free 3132 const u_char * sliceBytes = NULL; // do not free 3133 mode_t fileMode = 0; 3134 int i = 0; 3135 3136 result = readMachOSlices(prelinkPath, &prelinkedSlices, 3137 &prelinkedArchs, &fileMode, prelinkedKernelTimes); 3138 if (result != EX_OK) { 3139 goto finish; 3140 } 3141 3142 /* Compress/uncompress each slice of the prelinked kernel. 3143 */ 3144 3145 for (i = 0; i < CFArrayGetCount(prelinkedSlices); ++i) { 3146 3147 SAFE_RELEASE_NULL(prelinkedSlice); 3148 prelinkedSlice = CFArrayGetValueAtIndex(prelinkedSlices, i); 3149 3150 if (compress) { 3151 const PrelinkedKernelHeader *header = (const PrelinkedKernelHeader *) 3152 CFDataGetBytePtr(prelinkedSlice); 3153 Boolean wantsFastLib = wantsFastLibCompressionForTargetVolume(volumeRootURL); 3154 uint32_t compressionType = wantsFastLib ? COMP_TYPE_FASTLIB : COMP_TYPE_LZSS; 3155 3156 3157 prelinkedSlice = compressPrelinkedSlice(compressionType, 3158 prelinkedSlice, 3159 (OSSwapHostToBigInt32(header->prelinkVersion) == 1)); 3160 if (!prelinkedSlice) { 3161 result = EX_DATAERR; 3162 goto finish; 3163 } 3164 } else { 3165 prelinkedSlice = uncompressPrelinkedSlice(prelinkedSlice); 3166 if (!prelinkedSlice) { 3167 result = EX_DATAERR; 3168 goto finish; 3169 } 3170 } 3171 3172 CFArraySetValueAtIndex(prelinkedSlices, i, prelinkedSlice); 3173 } 3174 SAFE_RELEASE_NULL(prelinkedSlice); 3175 3176 /* Snow Leopard prelinked kernels are not wrapped in a fat header, so we 3177 * have to decompress the prelinked kernel and look at the mach header 3178 * to get the architecture information. 3179 */ 3180 3181 if (!prelinkedArchs && CFArrayGetCount(prelinkedSlices) == 1) { 3182 if (!createCFMutableArray(&prelinkedArchs, NULL)) { 3183 OSKextLogMemError(); 3184 result = EX_OSERR; 3185 goto finish; 3186 } 3187 3188 sliceBytes = CFDataGetBytePtr( 3189 CFArrayGetValueAtIndex(prelinkedSlices, 0)); 3190 3191 archInfo = getThinHeaderPageArch(sliceBytes); 3192 if (archInfo) { 3193 CFArrayAppendValue(prelinkedArchs, archInfo); 3194 } else { 3195 SAFE_RELEASE_NULL(prelinkedArchs); 3196 } 3197 } 3198 3199 /* If we still don't have architecture information, then something 3200 * definitely went wrong. 3201 */ 3202 3203 if (!prelinkedArchs) { 3204 OSKextLog(/* kext */ NULL, 3205 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 3206 "Couldn't determine prelinked kernel's architecture"); 3207 result = EX_SOFTWARE; 3208 goto finish; 3209 } 3210 3211 result = writeFatFile(prelinkPath, prelinkedSlices, 3212 prelinkedArchs, fileMode, prelinkedKernelTimes); 3213 if (result != EX_OK) { 3214 goto finish; 3215 } 3216 3217 result = EX_OK; 3218 3219finish: 3220 SAFE_RELEASE(prelinkedSlices); 3221 SAFE_RELEASE(prelinkedArchs); 3222 SAFE_RELEASE(prelinkedSlice); 3223 3224 return result; 3225} 3226 3227#pragma mark Boot!=Root 3228 3229 3230/******************************************************************************* 3231* usage() 3232*******************************************************************************/ 3233void usage(UsageLevel usageLevel) 3234{ 3235 fprintf(stderr, 3236 "usage: %1$s <mkext_flag> [options] [--] [kext or directory] ...\n" 3237 " %1$s -prelinked-kernel <filename> [options] [--] [kext or directory]\n" 3238 " %1$s -system-prelinked-kernel\n" 3239 " %1$s [options] -prelinked-kernel\n" 3240#if !NO_BOOT_ROOT 3241 " %1$s -invalidate <volume> \n" 3242 " %1$s -update-volume <volume> [options]\n" 3243#endif /* !NO_BOOT_ROOT */ 3244 " %1$s -system-caches [options]\n" 3245 "\n", 3246 progname); 3247 3248 if (usageLevel == kUsageLevelBrief) { 3249 fprintf(stderr, "use %s -%s for an explanation of each option\n", 3250 progname, kOptNameHelp); 3251 } 3252 3253 if (usageLevel == kUsageLevelBrief) { 3254 return; 3255 } 3256 3257 fprintf(stderr, "-%s <filename>: create an mkext (latest supported version)\n", 3258 kOptNameMkext); 3259 fprintf(stderr, "-%s <filename>: create an mkext (version 2)\n", 3260 kOptNameMkext2); 3261 fprintf(stderr, "-%s <filename> (-%c): create an mkext (version 1)\n", 3262 kOptNameMkext1, kOptMkext); 3263 fprintf(stderr, "-%s [<filename>] (-%c):\n" 3264 " create/update prelinked kernel (must be last if no filename given)\n", 3265 kOptNamePrelinkedKernel, kOptPrelinkedKernel); 3266 fprintf(stderr, "-%s:\n" 3267 " create/update system prelinked kernel\n", 3268 kOptNameSystemPrelinkedKernel); 3269#if !NO_BOOT_ROOT 3270 fprintf(stderr, "-%s <volume> (-%c): invalidate system kext caches for <volume>\n", 3271 kOptNameInvalidate, kOptInvalidate); 3272 fprintf(stderr, "-%s <volume> (-%c): update system kext caches for <volume>\n", 3273 kOptNameUpdate, kOptUpdate); 3274 fprintf(stderr, "-%s called us, modify behavior appropriately\n", 3275 kOptNameInstaller); 3276 fprintf(stderr, "-%s skips updating any helper partitions even if they appear out of date\n", 3277 kOptNameCachesOnly); 3278#endif /* !NO_BOOT_ROOT */ 3279#if 0 3280// don't print this system-use option 3281 fprintf(stderr, "-%c <volume>:\n" 3282 " check system kext caches for <volume> (nonzero exit if out of date)\n", 3283 kOptCheckUpdate); 3284#endif 3285 fprintf(stderr, "-%s: update system kext info caches for the root volume\n", 3286 kOptNameSystemCaches); 3287 fprintf(stderr, "\n"); 3288 3289 fprintf(stderr, 3290 "kext or directory: Consider kext or all kexts in directory for inclusion\n"); 3291 fprintf(stderr, "-%s <bundle_id> (-%c):\n" 3292 " include the kext whose CFBundleIdentifier is <bundle_id>\n", 3293 kOptNameBundleIdentifier, kOptBundleIdentifier); 3294 fprintf(stderr, "-%s <volume>:\n" 3295 " Save kext paths in an mkext archive or prelinked kernel " 3296 " relative to <volume>\n", 3297 kOptNameVolumeRoot); 3298 fprintf(stderr, "-%s <kernel_filename> (-%c): Use kernel_filename for a prelinked kernel\n", 3299 kOptNameKernel, kOptKernel); 3300 fprintf(stderr, "-%s (-%c): Include all kexts ever loaded in prelinked kernel\n", 3301 kOptNameAllLoaded, kOptAllLoaded); 3302#if !NO_BOOT_ROOT 3303 fprintf(stderr, "-%s (-%c): Update volumes even if they look up to date\n", 3304 kOptNameForce, kOptForce); 3305 fprintf(stderr, "\n"); 3306#endif /* !NO_BOOT_ROOT */ 3307 3308 fprintf(stderr, "-%s (-%c): Add 'Local-Root' kexts from directories to an mkext file\n", 3309 kOptNameLocalRoot, kOptLocalRoot); 3310 fprintf(stderr, "-%s (-%c): Add 'Local-Root' kexts to an mkext file\n", 3311 kOptNameLocalRootAll, kOptLocalRootAll); 3312 fprintf(stderr, "-%s (-%c): Add 'Network-Root' kexts from directories to an mkext file\n", 3313 kOptNameNetworkRoot, kOptNetworkRoot); 3314 fprintf(stderr, "-%s (-%c): Add 'Network-Root' kexts to an mkext file\n", 3315 kOptNameNetworkRootAll, kOptNetworkRootAll); 3316 fprintf(stderr, "-%s (-%c): Add 'Safe Boot' kexts from directories to an mkext file\n", 3317 kOptNameSafeBoot, kOptSafeBoot); 3318 fprintf(stderr, "-%s (-%c): Add 'Safe Boot' kexts to an mkext file\n", 3319 kOptNameSafeBootAll, kOptSafeBootAll); 3320 fprintf(stderr, "\n"); 3321 3322 fprintf(stderr, "-%s <archname>:\n" 3323 " include architecture <archname> in created cache(s)\n", 3324 kOptNameArch); 3325 fprintf(stderr, "-%c: run at low priority\n", 3326 kOptLowPriorityFork); 3327 fprintf(stderr, "\n"); 3328 3329 fprintf(stderr, "-%s (-%c): quiet mode: print no informational or error messages\n", 3330 kOptNameQuiet, kOptQuiet); 3331 fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n" 3332 " verbose mode; print info about analysis & loading\n", 3333 kOptNameVerbose, kOptVerbose); 3334 fprintf(stderr, "\n"); 3335 3336 fprintf(stderr, "-%s (-%c):\n" 3337 " print diagnostics for kexts with problems\n", 3338 kOptNameTests, kOptTests); 3339 fprintf(stderr, "-%s (-%c): don't authenticate kexts (for use during development)\n", 3340 kOptNameNoAuthentication, kOptNoAuthentication); 3341 fprintf(stderr, "\n"); 3342 3343 fprintf(stderr, "-%s (-%c): print this message and exit\n", 3344 kOptNameHelp, kOptHelp); 3345 3346 return; 3347} 3348 3349#if 1 // 17821398 3350static Boolean needsPrelinkedKernelCopy( KextcacheArgs * toolArgs ) 3351{ 3352 Boolean volSupportsIt = false; 3353 Boolean isCorrectPrefix = false; 3354 Boolean prelinkedKernelsExists = false; 3355 char volRootBuf[PATH_MAX]; 3356 char tempBuf[PATH_MAX]; 3357 3358 volSupportsIt = wantsPrelinkedKernelCopy(toolArgs->volumeRootURL); 3359 3360 while (volSupportsIt) { 3361 /* Now see if the toolArgs->prelinkedKernelPath path starts with 3362 * "/System/Library/Caches/com.apple.kext.caches/Startup/kernelcache" 3363 * We do not want to copy custom kernelcache files. 3364 */ 3365 volRootBuf[0] = 0x00; 3366 if (toolArgs->volumeRootURL) { 3367 if (CFURLGetFileSystemRepresentation(toolArgs->volumeRootURL, 3368 true, 3369 (UInt8 *)volRootBuf, 3370 sizeof(volRootBuf)) == false) { 3371 // this should not happen, but just in case... 3372 volRootBuf[0] = 0x00; 3373 } 3374 } 3375 3376 /* handle case where there is no volumeRootURL or if the root is just "/" */ 3377 if (strlen(volRootBuf) > 1) { 3378 strlcpy(tempBuf, volRootBuf, sizeof(tempBuf)); 3379 if (strlcat(tempBuf, k_kernelcacheFilePath, sizeof(tempBuf)) >= sizeof(tempBuf)) { 3380 // overflow 3381 break; 3382 } 3383 } 3384 else { 3385 strlcpy(tempBuf, k_kernelcacheFilePath, sizeof(tempBuf)); 3386 } 3387 3388 char * myPrefix; 3389 myPrefix = strnstr(toolArgs->prelinkedKernelPath, 3390 &tempBuf[0], 3391 strlen(&tempBuf[0])); 3392 isCorrectPrefix = (myPrefix != NULL); 3393 if (isCorrectPrefix == false) break; 3394 3395 /* make sure "/System/Library/PrelinkedKernels" exists 3396 */ 3397 if (strlen(volRootBuf) > 1) { 3398 strlcpy(tempBuf, volRootBuf, sizeof(tempBuf)); 3399 if (strlcat(tempBuf, kPrelinkedKernelsPath, sizeof(tempBuf)) >= sizeof(tempBuf)) { 3400 // overflow 3401 break; 3402 } 3403 } 3404 else { 3405 strlcpy(tempBuf, kPrelinkedKernelsPath, sizeof(tempBuf)); 3406 } 3407 3408 struct stat statBuf; 3409 if (statPath(tempBuf, &statBuf) == EX_OK) { 3410 prelinkedKernelsExists = true; 3411 } 3412 else { 3413 /* need to create System/Library/PrelinkedKernels/ */ 3414 int my_fd; 3415 3416 my_fd = open(toolArgs->prelinkedKernelPath, O_RDONLY); 3417 if (my_fd != -1) { 3418 if (smkdir(my_fd, tempBuf, 0755) == 0) { 3419 prelinkedKernelsExists = true; 3420 } 3421 close(my_fd); 3422 } 3423 } 3424 break; 3425 } // while (volSupportsIt)... 3426 3427 return(volSupportsIt && isCorrectPrefix && prelinkedKernelsExists); 3428} 3429 3430/* Make sure target volume wants a copy of the prelinked kernel in 3431 * /System/Library/PrelinkedKernels/prelinkedkernel 3432 * This is a temporary hack, using the Yosemite added support for lzvn 3433 * compression to mean "wants /System/Library/PrelinkedKernels". 3434 * 3435 * We will update the path key / value in bootcaches.plist in Yosemite + 1 3436 */ 3437static Boolean wantsPrelinkedKernelCopy(CFURLRef theVolRootURL) 3438{ 3439 return(wantsFastLibCompressionForTargetVolume(theVolRootURL)); 3440} 3441 3442#endif 3443 3444