Example #1
0
/*
 * gr - get record attributes
 */
static NuError
GetRecordFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    const NuRecord* pRecord;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 2);

    err = NuGetRecord(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], nil, 0), &pRecord);
    if (err == kNuErrNone) {
        printf("Exerciser: success, call returned:\n");
        printf("\tfileSysID   : %d\n", pRecord->recFileSysID);
        printf("\tfileSysInfo : 0x%04x ('%c')\n", pRecord->recFileSysInfo,
            NuGetSepFromSysInfo(pRecord->recFileSysInfo));
        printf("\taccess      : 0x%02lx\n", pRecord->recAccess);
        printf("\tfileType    : 0x%04lx\n", pRecord->recFileType);
        printf("\textraType   : 0x%04lx\n", pRecord->recExtraType);
        printf("\tcreateWhen  : ...\n");
        printf("\tmodWhen     : ...\n");        /* too lazy */
        printf("\tarchiveWhen : ...\n");
    }
    return err;
}
Example #2
0
/*
 * Extract a thread from the archive as part of a "bulk" extract operation.
 *
 * Streaming archives must be properly positioned.
 */
NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread)
{
    NuError err;
    NuDataSink* pDataSink = NULL;
    UNICHAR* recFilenameStorageUNI = NULL;
    NuValue eolConv;

    /*
     * Create a file data sink for the file.  We use whatever EOL conversion
     * is set as the default for the entire archive.  (If you want to
     * specify your own EOL conversion for each individual file, you will
     * need to extract them individually, creating a data sink for each.)
     *
     * One exception: we turn EOL conversion off for disk image threads.
     * It's *very* unlikely this would be desirable, and could be a problem
     * if the user is extracting a collection of disks and files.
     */
    eolConv = pArchive->valConvertExtractedEOL;
    if (NuGetThreadID(pThread) == kNuThreadIDDiskImage)
        eolConv = kNuConvertOff;
    recFilenameStorageUNI = Nu_CopyMORToUNI(pRecord->filenameMOR);
    err = Nu_DataSinkFile_New(true, eolConv, recFilenameStorageUNI,
            NuGetSepFromSysInfo(pRecord->recFileSysInfo), &pDataSink);
    BailError(err);

    err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink);
    BailError(err);

bail:
    if (pDataSink != NULL) {
        NuError err2 = Nu_DataSinkFree(pDataSink);
        if (err == kNuErrNone)
            err = err2;
    }
    Nu_Free(pArchive, recFilenameStorageUNI);

    return err;
}
Example #3
0
/*
 * Extract the specified thread to "pDataSink".  If the sink is to a file,
 * this will take care of opening (and, if appropriate, creating) the file.
 *
 * If we're operating on a streaming archive, the file pointer must be
 * positioned at the start of the thread's data.  If not, it will be
 * seeked appropriately.
 *
 * This calls the "should we extract" and "what pathname should we use"
 * filters for every thread, which means we can reject specific kinds
 * of forks and/or give them different names.  This is a good thing.
 */
static NuError Nu_ExtractThreadCommon(NuArchive* pArchive,
    const NuRecord* pRecord, const NuThread* pThread, NuDataSink* pDataSink)
{
    NuError err = kNuErrNone;
    NuSelectionProposal selProposal;
    NuPathnameProposal pathProposal;
    NuProgressData progressData;
    NuProgressData* pProgressData;
    NuDataSink* pOrigDataSink;
    UNICHAR* newPathStorageUNI = NULL;
    UNICHAR* recFilenameStorageUNI = NULL;
    const UNICHAR* newPathnameUNI;
    NuResult result;
    uint8_t newFssep;
    Boolean doFreeSink = false;

    Assert(pRecord != NULL);
    Assert(pThread != NULL);
    Assert(pDataSink != NULL);

    memset(&progressData, 0, sizeof(progressData));
    pProgressData = NULL;

    /*
     * If we're just trying to verify the archive contents, create a
     * data sink that goes nowhere at all.
     */
    if (pArchive->testMode) {
        err = Nu_DataSinkVoid_New(
                Nu_DataSinkGetDoExpand(pDataSink),
                Nu_DataSinkGetConvertEOL(pDataSink),
                &pDataSink);
        BailError(err);
        doFreeSink = true;
    }

    pOrigDataSink = pDataSink;  /* save a copy for the "retry" loop */

    /*
     * Decide if we want to extract this thread.  This is mostly for
     * use by the "bulk" extract, not the per-thread extract, but it
     * still applies if they so desire.
     */
    if (pArchive->selectionFilterFunc != NULL) {
        selProposal.pRecord = pRecord;
        selProposal.pThread = pThread;
        result = (*pArchive->selectionFilterFunc)(pArchive, &selProposal);

        if (result == kNuSkip)
            return Nu_SkipThread(pArchive, pRecord, pThread);
        if (result == kNuAbort) {
            err = kNuErrAborted;
            goto bail;
        }
    }

    newPathnameUNI = NULL;
    newFssep = 0;

    recFilenameStorageUNI = Nu_CopyMORToUNI(pRecord->filenameMOR);

retry_name:
    if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) {
        /*
         * We're extracting.  Figure out the name of the file to write it to.
         * If they want to use the sleazy FILE* back door, create a new
         * data sink and use that instead.
         *
         * Start by resetting everything to defaults, in case this isn't
         * our first time through the "rename" loop.
         */
        newPathnameUNI = Nu_DataSinkFile_GetPathname(pDataSink);
        newFssep = Nu_DataSinkFile_GetFssep(pDataSink);
        pDataSink = pOrigDataSink;

        /* if they don't have a pathname func defined, we just use default */
        if (pArchive->outputPathnameFunc != NULL) {
            pathProposal.pathnameUNI = recFilenameStorageUNI;
            pathProposal.filenameSeparator =
                                NuGetSepFromSysInfo(pRecord->recFileSysInfo);
            pathProposal.pRecord = pRecord;
            pathProposal.pThread = pThread;
            pathProposal.newPathnameUNI = NULL;
            pathProposal.newFilenameSeparator = '\0';
            /*pathProposal.newStorage = (NuThreadID)-1;*/
            pathProposal.newDataSink = NULL;

            result = (*pArchive->outputPathnameFunc)(pArchive, &pathProposal);

            if (result == kNuSkip)
                return Nu_SkipThread(pArchive, pRecord, pThread);
            if (result == kNuAbort) {
                err = kNuErrAborted;
                goto bail;
            }

            /* we don't own this string, so make a copy */
            if (pathProposal.newPathnameUNI != NULL) {
                Nu_Free(pArchive, newPathStorageUNI);
                newPathStorageUNI = strdup(pathProposal.newPathnameUNI);
                newPathnameUNI = newPathStorageUNI;
            } else {
                newPathnameUNI = NULL;
            }
            if (pathProposal.newFilenameSeparator != '\0')
                newFssep = pathProposal.newFilenameSeparator;

            /* if they want to send this somewhere else, let them */
            if (pathProposal.newDataSink != NULL)
                pDataSink = pathProposal.newDataSink;
        }

        /* at least one of these must be set */
        Assert(!(newPathnameUNI == NULL && pathProposal.newDataSink == NULL));
    }

    /*
     * Prepare the progress data if this is a data thread.
     */
    if (newPathnameUNI == NULL) {
        /* using a data sink; get the pathname out of the record */
        newPathnameUNI = recFilenameStorageUNI;
        newFssep = NuGetSepFromSysInfo(pRecord->recFileSysInfo);
    }
    if (pThread->thThreadClass == kNuThreadClassData) {
        pProgressData = &progressData;
        err = Nu_ProgressDataInit_Expand(pArchive, pProgressData, pRecord,
                newPathnameUNI, newFssep, recFilenameStorageUNI,
                Nu_DataSinkGetConvertEOL(pOrigDataSink));
        BailError(err);

        /* send initial progress so they see the right name if "open" fails */
        pProgressData->state = kNuProgressOpening;
        err = Nu_SendInitialProgress(pArchive, pProgressData);
        BailError(err);
    }

    if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) {
        /*
         * We're extracting to a file.  Open it, creating it if necessary and
         * allowed.
         */
        FILE* fileFp = NULL;

        err = Nu_OpenOutputFile(pArchive, pRecord, pThread, newPathnameUNI,
                newFssep, &fileFp);
        if (err == kNuErrRename) {
            /* they want to rename; the OutputPathname callback handles this */
            Nu_Free(pArchive, newPathStorageUNI);
            newPathStorageUNI = NULL;
            /* reset these just to be careful */
            newPathnameUNI = NULL;
            fileFp = NULL;
            goto retry_name;
        } else if (err != kNuErrNone) {
            goto bail;
        }

        Assert(fileFp != NULL);
        (void) Nu_DataSinkFile_SetFP(pDataSink, fileFp);

        DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to '%s'\n",
            NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind),
            pRecord->filename, pThread->fileOffset, newPathname));
    } else {
        DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to sink\n",
            NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind),
            pRecord->filename, pThread->fileOffset));
    }

    /* extract to the file */
    err = Nu_ExtractThreadToDataSink(pArchive, pRecord, pThread,
            pProgressData, pDataSink);
    BailError(err);

    if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) {
        /*
         * Close the file, adjusting the modification date and access
         * permissions as appropriate.
         */
        err = Nu_CloseOutputFile(pArchive, pRecord,
                Nu_DataSinkFile_GetFP(pDataSink), newPathnameUNI);
        Nu_DataSinkFile_SetFP(pDataSink, NULL);
        BailError(err);
    }

bail:
    if (err != kNuErrNone && pProgressData != NULL) {
        /* send a final progress message, indicating failure */
        if (err == kNuErrSkipped)
            pProgressData->state = kNuProgressSkipped;
        else if (err == kNuErrAborted)
            pProgressData->state = kNuProgressAborted;
        else
            pProgressData->state = kNuProgressFailed;
        (void) Nu_SendInitialProgress(pArchive, pProgressData);
    }

    /* if this was an ordinary file, and it's still open, close it */
    if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile)
        Nu_DataSinkFile_Close(pDataSink);

    Nu_Free(pArchive, newPathStorageUNI);
    Nu_Free(pArchive, recFilenameStorageUNI);

    if (doFreeSink)
        Nu_DataSinkFree(pDataSink);
    return err;
}