CFDataRef createMkext1ForArch(const NXArchInfo * arch, CFArrayRef archiveKexts,
    boolean_t compress)
{
    CFMutableDataRef       result            = NULL;
    CFMutableDictionaryRef kextsByIdentifier = NULL;
    Mkext1Context          context;
    mkext1_header        * mkextHeader       = NULL;  // do not free
    const uint8_t        * adler_point = 0;
    CFIndex count, i;

    result = CFDataCreateMutable(kCFAllocatorDefault, /* capaacity */ 0);
    if (!result || !createCFMutableDictionary(&kextsByIdentifier)) {
        OSKextLogMemError();
        goto finish;
    }

   /* mkext1 can only contain 1 kext for a given bundle identifier, so we
    * have to pick out the most recent versions.
    */
    count = CFArrayGetCount(archiveKexts);
    for (i = 0; i < count; i++) {
        OSKextRef   theKext = (OSKextRef)CFArrayGetValueAtIndex(archiveKexts, i);
        CFStringRef bundleIdentifier = OSKextGetIdentifier(theKext);
        OSKextRef   savedKext = (OSKextRef)CFDictionaryGetValue(kextsByIdentifier,
            bundleIdentifier);
        OSKextVersion thisVersion, savedVersion;


        if (!OSKextSupportsArchitecture(theKext, arch)) {
            continue;
        }

        if (!savedKext) {
            CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext);
            continue;
        }
        
        thisVersion = OSKextGetVersion(theKext);
        savedVersion = OSKextGetVersion(savedKext);
        
        if (thisVersion > savedVersion) {
            CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext);
        }
    }

   /* Add room for the mkext header and kext descriptors.
    */
    CFDataSetLength(result, sizeof(mkext1_header) +
        CFDictionaryGetCount(kextsByIdentifier) * sizeof(mkext_kext));

    context.mkext = result;
    context.kextIndex = 0;
    context.compressOffset = (uint32_t)CFDataGetLength(result);
    context.arch = arch;
    context.fatal = false;
    context.compress = compress;
    CFDictionaryApplyFunction(kextsByIdentifier, addToMkext1, &context);
    if (context.fatal) {
        SAFE_RELEASE_NULL(result);
        goto finish;
    }

    mkextHeader = (mkext1_header *)CFDataGetBytePtr(result);
    mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC);
    mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN);
    mkextHeader->version = OSSwapHostToBigInt32(0x01008000);   // 'vers' 1.0.0
    mkextHeader->numkexts =
        OSSwapHostToBigInt32((__uint32_t)CFDictionaryGetCount(kextsByIdentifier));
    mkextHeader->cputype = OSSwapHostToBigInt32(arch->cputype);
    mkextHeader->cpusubtype = OSSwapHostToBigInt32(arch->cpusubtype);
    mkextHeader->length = OSSwapHostToBigInt32((__uint32_t)CFDataGetLength(result));

    adler_point = (UInt8 *)&mkextHeader->version;
    mkextHeader->adler32 = OSSwapHostToBigInt32(local_adler32(
        (UInt8 *)&mkextHeader->version,
        (int)(CFDataGetLength(result) - (adler_point - (uint8_t *)mkextHeader))));

    OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag,
        "Created mkext for %s containing %lu kexts.",
        arch->name,
        CFDictionaryGetCount(kextsByIdentifier));

finish:
    SAFE_RELEASE(kextsByIdentifier);
    return result;
}
ExitStatus readArgs(int argc, char * const * argv, KextstatArgs * toolArgs)
{
    ExitStatus   result        = EX_USAGE;
    CFStringRef  scratchString = NULL;  // must release
    int          optChar       = 0;
    
    bzero(toolArgs, sizeof(*toolArgs));

   /*****
    * Allocate collection objects needed for command line argument processing.
    */
    if (!createCFMutableArray(&toolArgs->bundleIDs, &kCFTypeArrayCallBacks)) {
        goto finish;
    }

   /*****
    * Process command-line arguments.
    */
    result = EX_USAGE;

    while ((optChar = getopt_long_only(argc, argv, kOptChars,
        sOptInfo, NULL)) != -1) {

        SAFE_RELEASE_NULL(scratchString);

        switch (optChar) {

            case kOptHelp:
                usage(kUsageLevelFull);
                result = kKextstatExitHelp;
                goto finish;
                break;

            case kOptNoKernelComponents:
                toolArgs->flagNoKernelComponents = true;
                break;

            case kOptListOnly:
                toolArgs->flagListOnly = true;
                break;

            case kOptBundleIdentifier:
                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
                    optarg, kCFStringEncodingUTF8);
                if (!scratchString) {
                    OSKextLogMemError();
                    result = EX_OSERR;
                    goto finish;
                }
                CFArrayAppendValue(toolArgs->bundleIDs, scratchString);
                break;
            
            case kOptArchitecture:
                toolArgs->flagShowArchitecture = true;
                break;
                
            }
    }

    argc -= optind;
    argv += optind;

    if (argc) {
        OSKextLog(/* kext */ NULL,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Extra arguments starting at %s....", argv[0]);
        usage(kUsageLevelBrief);
        goto finish;
    }

    result = EX_OK;

finish:
    SAFE_RELEASE_NULL(scratchString);

    if (result == EX_USAGE) {
        usage(kUsageLevelBrief);
    }
    return result;
}
Example #3
0
/*********************************************************************
* Internal function for use by KextManagerLoadKextWithIdentifier()
* and KextManagerLoadKextWithURL().
*********************************************************************/
OSReturn __KextManagerSendLoadKextRequest(
    CFMutableDictionaryRef  requestDict,
    CFArrayRef              dependencyKextAndFolderURLs)
{
    OSReturn           result           = kOSReturnError;
    mach_port_t        kextd_port       = MACH_PORT_NULL;
    CFDataRef          requestData      = NULL;  // must release
    CFMutableArrayRef  dependencyPaths  = NULL;  // must release
    CFURLRef           dependencyAbsURL = NULL;  // must release
    CFStringRef        dependencyPath   = NULL;  // must release
    CFErrorRef         error            = NULL;  // must release

    if (!requestDict) {
        result = kOSKextReturnInvalidArgument;
        goto finish;
    }

    result = get_kextd_port(&kextd_port);
    if (result != kOSReturnSuccess) {
        goto finish;
    }

    if (dependencyKextAndFolderURLs &&
        CFArrayGetCount(dependencyKextAndFolderURLs)) {

        CFIndex count, index;
        
        dependencyPaths = CFArrayCreateMutable(kCFAllocatorDefault,
            /* capacity */ 0, &kCFTypeArrayCallBacks);
        if (!dependencyPaths) {
            result = kOSKextReturnNoMemory;
            goto finish;
        }
        CFDictionarySetValue(requestDict, kKextLoadDependenciesKey,
            dependencyPaths);

        count = CFArrayGetCount(dependencyKextAndFolderURLs);
        for (index = 0; index < count; index++) {
            CFURLRef thisURL = (CFURLRef)CFArrayGetValueAtIndex(
                dependencyKextAndFolderURLs, index);

            SAFE_RELEASE_NULL(dependencyPath);
            SAFE_RELEASE_NULL(dependencyAbsURL);

            dependencyAbsURL = CFURLCopyAbsoluteURL(thisURL);
            if (!dependencyAbsURL) {
                result = kOSKextReturnNoMemory;
                goto finish;
            }
            dependencyPath = CFURLCopyFileSystemPath(dependencyAbsURL,
                kCFURLPOSIXPathStyle);
            if (!dependencyPath) {
                result = kOSKextReturnNoMemory;
                goto finish;
            }

            CFArrayAppendValue(dependencyPaths, dependencyPath);
        }
    }

    requestData = CFPropertyListCreateData(kCFAllocatorDefault,
         requestDict, kCFPropertyListBinaryFormat_v1_0,
         /* options */ 0,
         &error);
    if (!requestData) {
        // any point in logging error reason here? nothing caller can do....
        result = kOSKextReturnSerialization;
        goto finish;
    }

    result = kextmanager_load_kext(kextd_port,
        (char *)CFDataGetBytePtr(requestData),
        CFDataGetLength(requestData));

finish:
    SAFE_RELEASE(requestData);
    SAFE_RELEASE(dependencyPaths);
    SAFE_RELEASE(dependencyPath);
    SAFE_RELEASE(dependencyAbsURL);
    SAFE_RELEASE(error);

    return result;
}
ExitStatus readArgs(
    int            * argc,
    char * const  ** argv,
    KcgenArgs  * toolArgs)
{
    ExitStatus   result         = EX_USAGE;
    ExitStatus   scratchResult  = EX_USAGE;
    CFStringRef  scratchString  = NULL;  // must release
    CFNumberRef  scratchNumber  = NULL;  // must release
    CFURLRef     scratchURL     = NULL;  // must release
    size_t       len            = 0;
    int32_t      i              = 0;
    int          optchar        = 0;
    int          longindex      = -1;

    bzero(toolArgs, sizeof(*toolArgs));
    
   /*****
    * Allocate collection objects.
    */
    if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks)             ||
        !createCFMutableSet(&toolArgs->optionalKextIDs, &kCFTypeSetCallBacks)     ||
        !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks)         ||
        !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks)  ||
        !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks)   ||
        !createCFMutableArray(&toolArgs->targetArchs, NULL)) {

        OSKextLogMemError();
        result = EX_OSERR;
        exit(result);
    }

    /*****
    * Process command line arguments.
    */
    while ((optchar = getopt_long_only(*argc, *argv,
        kOptChars, sOptInfo, &longindex)) != -1) {

        SAFE_RELEASE_NULL(scratchString);
        SAFE_RELEASE_NULL(scratchNumber);
        SAFE_RELEASE_NULL(scratchURL);

        /* When processing short (single-char) options, there is no way to
         * express optional arguments.  Instead, we suppress missing option
         * argument errors by adding a leading ':' to the option string.
         * When getopt detects a missing argument, it will return a ':' so that
         * we can screen for options that are not required to have an argument.
         */
        if (optchar == ':') {
            switch (optopt) {
                case kOptPrelinkedKernel:
                    optchar = optopt;
                    break;
                default:
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Error - option requires an argument -- -%c.", 
                        optopt);
                    break;
            }
        }

        switch (optchar) {
  
            case kOptArch:
                if (!addArchForName(toolArgs, optarg)) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Error - unknown architecture %s.", optarg);
                    goto finish;
                }
                break;
  
            case kOptBundleIdentifier:
                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
                   optarg, kCFStringEncodingUTF8);
                if (!scratchString) {
                    OSKextLogMemError();
                    result = EX_OSERR;
                    goto finish;
                }
                CFSetAddValue(toolArgs->kextIDs, scratchString);
                break;
  
            case kOptPrelinkedKernel:
                scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv,
                    /* isLongopt */ longindex != -1);
                if (scratchResult != EX_OK) {
                    result = scratchResult;
                    goto finish;
                }
                break;
  
            case kOptHelp:
                usage(kUsageLevelFull);
                result = kKcgenExitHelp;
                goto finish;
    
            case kOptKernel:
                if (toolArgs->kernelPath) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                        "Warning - kernel file already specified; using last.");
                } else {
                    toolArgs->kernelPath = malloc(PATH_MAX);
                    if (!toolArgs->kernelPath) {
                        OSKextLogMemError();
                        result = EX_OSERR;
                        goto finish;
                    }
                }

                len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX);
                if (len >= PATH_MAX) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Error - kernel filename length exceeds PATH_MAX");
                    goto finish;
                }
                break;
    
            case kOptTests:
                toolArgs->printTestResults = true;
                break;
  
            case kOptQuiet:
                beQuiet();
                break;

            case kOptVerbose:
                scratchResult = setLogFilterForOpt(*argc, *argv,
                    /* forceOnFlags */ kOSKextLogKextOrGlobalMask);
                if (scratchResult != EX_OK) {
                    result = scratchResult;
                    goto finish;
                }
                break;

            case kOptNoAuthentication:
                OSKextLog(/* kext */ NULL,
                    kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                    "Note: -%s is implicitly set for %s.", kOptNameNoAuthentication, progname);
                break;

            case 0:
                switch (longopt) {
                    case kLongOptOptionalBundleIdentifier:
                        scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
                           optarg, kCFStringEncodingUTF8);
                        if (!scratchString) {
                            OSKextLogMemError();
                            result = EX_OSERR;
                            goto finish;
                        }
                        CFSetAddValue(toolArgs->optionalKextIDs, scratchString);
                        break;
          
                    case kLongOptCompressed:
                        toolArgs->compress = true;
                        break;

                    case kLongOptUncompressed:
                        toolArgs->uncompress = true;
                        break;

                    case kLongOptSymbols:
                        if (toolArgs->symbolDirURL) {
                            OSKextLog(/* kext */ NULL,
                                kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                                "Warning - symbol directory already specified; using last.");
                            SAFE_RELEASE_NULL(toolArgs->symbolDirURL);
                        }

                        scratchURL = CFURLCreateFromFileSystemRepresentation(
                            kCFAllocatorDefault,
                            (const UInt8 *)optarg, strlen(optarg), true);
                        if (!scratchURL) {
                            OSKextLogStringError(/* kext */ NULL);
                            result = EX_OSERR;
                            goto finish;
                        }

                        toolArgs->symbolDirURL = CFRetain(scratchURL);
                        toolArgs->generatePrelinkedSymbols = true;
                        break;

                    case kLongOptVolumeRoot:
                        if (toolArgs->volumeRootURL) {
                            OSKextLog(/* kext */ NULL,
                                kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                                "Warning: volume root already specified; using last.");
                            SAFE_RELEASE_NULL(toolArgs->volumeRootURL);
                        }

                        scratchURL = CFURLCreateFromFileSystemRepresentation(
                            kCFAllocatorDefault,
                            (const UInt8 *)optarg, strlen(optarg), true);
                        if (!scratchURL) {
                            OSKextLogStringError(/* kext */ NULL);
                            result = EX_OSERR;
                            goto finish;
                        }

                        toolArgs->volumeRootURL = CFRetain(scratchURL);
                        break;

                    case kLongOptAllPersonalities:
                        OSKextLog(/* kext */ NULL,
                            kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                            "Note: -%s is implicitly set for %s.", kOptNameAllPersonalities, progname);
                        break;

                    case kLongOptNoLinkFailures:
                        OSKextLog(/* kext */ NULL,
                            kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                            "Note: -%s is implicitly set for %s.", kOptNameNoLinkFailures, progname);
                        break;

                    case kLongOptStripSymbols:
                        toolArgs->stripSymbols = true;
                        break;

                    case kLongOptMaxSliceSize:
                        toolArgs->maxSliceSize = atol(optarg);
                        break;

                    default:
                       /* Because we use ':', getopt_long doesn't print an error message.
                        */
                        OSKextLog(/* kext */ NULL,
                            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                            "Error - unrecognized option %s", (*argv)[optind-1]);
                        goto finish;
                        break;
                }
                break;

            default:
               /* Because we use ':', getopt_long doesn't print an error message.
                */
                OSKextLog(/* kext */ NULL,
                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                    "Error - unrecognized option %s", (*argv)[optind-1]);
                goto finish;
                break;

        }
        
       /* Reset longindex, because getopt_long_only() is stupid and doesn't.
        */
        longindex = -1;
    }

   /* Update the argc & argv seen by main().
    */
    *argc -= optind;
    *argv += optind;

   /*
    * Record the kext & directory names from the command line.
    */
    for (i = 0; i < *argc; i++) {
        SAFE_RELEASE_NULL(scratchURL);
        SAFE_RELEASE_NULL(scratchString);

        scratchURL = CFURLCreateFromFileSystemRepresentation(
            kCFAllocatorDefault,
            (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true);
        if (!scratchURL) {
            OSKextLogMemError();
            result = EX_OSERR;
            goto finish;
        }
        CFArrayAppendValue(toolArgs->argURLs, scratchURL);

        scratchString = CFURLCopyPathExtension(scratchURL);
        if (scratchString && CFEqual(scratchString, CFSTR("kext"))) {
            CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL);
        } else {
            CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL);
        }
    }

    result = EX_OK;

finish:
    SAFE_RELEASE(scratchString);
    SAFE_RELEASE(scratchNumber);
    SAFE_RELEASE(scratchURL);

    if (result == EX_USAGE) {
        usage(kUsageLevelBrief);
    }
    return result;
}