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