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