Example #1
0
/*
 * Verify that a conflicting thread with the specified threadID does not
 * exist in this record, now or in the future.
 *
 * The set of interesting threads is equal to the current threads, minus
 * any that have been deleted, plus any that have been added already.
 *
 * If a matching threadID is found, this returns an error.
 */
static NuError Nu_FindNoFutureThread(NuArchive* pArchive,
    const NuRecord* pRecord, NuThreadID threadID)
{
    NuError err = kNuErrNone;
    const NuThread* pThread;
    const NuThreadMod* pThreadMod;
    int idx;

    /*
     * Start by scanning the existing threads (if any).
     */
    for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) {
        pThread = Nu_GetThread(pRecord, idx);
        Assert(pThread != NULL);

        if (NuGetThreadID(pThread) == threadID) {
            /* found a match, see if it has been deleted */
            pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord,
                            pThread->threadIdx);
            if (pThreadMod != NULL &&
                pThreadMod->entry.kind == kNuThreadModDelete)
            {
                /* it's deleted, ignore it */
                continue;
            }
            DBUG(("--- found existing thread matching 0x%08lx\n", threadID));
            err = kNuErrThreadAdd;
            goto bail;
        }
    }

    /*
     * Now look for "add" threadMods with a matching threadID.
     */
    pThreadMod = pRecord->pThreadMods;
    while (pThreadMod != NULL) {
        if (pThreadMod->entry.kind == kNuThreadModAdd &&
            pThreadMod->entry.add.threadID == threadID)
        {
            DBUG(("--- found 'add' threadMod matching 0x%08lx\n", threadID));
            err = kNuErrThreadAdd;
            goto bail;
        }

        pThreadMod = pThreadMod->pNext;
    }

bail:
    return err;
}
Example #2
0
/*
 * Search through a given NuRecord for the first thread with a matching
 * threadID.
 */
NuError Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID,
    NuThread** ppThread)
{
    NuThread* pThread;
    int idx;

    for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) {
        pThread = Nu_GetThread(pRecord, idx);
        Assert(pThread != NULL);

        if (NuGetThreadID(pThread) == threadID) {
            *ppThread = pThread;
            return kNuErrNone;
        }
    }

    return kNuErrThreadIDNotFound;
}
Example #3
0
/*
 * Delete an individual thread.
 *
 * You aren't allowed to delete threads that have been updated.  Deleting
 * newly-added threads isn't possible, since they aren't really threads yet.
 *
 * Don't worry about deleting filename threads here; we take care of that
 * later on.  Besides, it's sort of handy to hang on to the filename for
 * as long as possible.
 */
NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx)
{
    NuError err;
    NuThreadMod* pThreadMod = NULL;
    NuRecord* pFoundRecord;
    NuThread* pFoundThread;

    if (Nu_IsReadOnly(pArchive))
        return kNuErrArchiveRO;
    err = Nu_GetTOCIfNeeded(pArchive);
    BailError(err);

    /*
     * Find the thread in the "copy" set.  (If there isn't a copy set,
     * make one.)
     */
    err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord,
            &pFoundThread);
    BailError(err);

    /*
     * Deletion of modified threads (updates or previous deletes) isn't
     * allowed.  Deletion of threads from deleted records can't happen,
     * because deleted records are completely removed from the "copy" set.
     */
    if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != NULL) {
        DBUG(("--- Tried to delete a deleted or modified thread\n"));
        err = kNuErrModThreadChange;
        goto bail;
    }

    /*
     * Looks good.  Add a new "delete" ThreadMod to the list.
     */
    err = Nu_ThreadModDelete_New(pArchive, threadIdx,
                NuGetThreadID(pFoundThread), &pThreadMod);
    BailError(err);
    Nu_RecordAddThreadMod(pFoundRecord, pThreadMod);
    pThreadMod = NULL;   /* successful, don't free */

bail:
    Nu_ThreadModFree(pArchive, pThreadMod);
    return err;
}
Example #4
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 #5
0
/*
 * NuContents callback function.  Print the contents of an individual record.
 */
NuResult
PrintEntry(NuArchive* pArchive, void* vpRecord)
{
    const NuRecord* pRecord = (const NuRecord*) vpRecord;
    int idx;

    (void)pArchive; /* shut up, gcc */

    printf("RecordIdx %ld: '%s'\n",
        pRecord->recordIdx, pRecord->filename);

    for (idx = 0; idx < (int) pRecord->recTotalThreads; idx++) {
        const NuThread* pThread;
        NuThreadID threadID;
        const char* threadLabel;

        pThread = NuGetThread(pRecord, idx);
        assert(pThread != nil);

        threadID = NuGetThreadID(pThread);
        switch (NuThreadIDGetClass(threadID)) {
        case kNuThreadClassMessage:
            threadLabel = "message class";
            break;
        case kNuThreadClassControl:
            threadLabel = "control class";
            break;
        case kNuThreadClassData:
            threadLabel = "data class";
            break;
        case kNuThreadClassFilename:
            threadLabel = "filename class";
            break;
        default:
            threadLabel = "(unknown class)";
            break;
        }

        switch (threadID) {
        case kNuThreadIDComment:
            threadLabel = "comment";
            break;
        case kNuThreadIDIcon:
            threadLabel = "icon";
            break;
        case kNuThreadIDMkdir:
            threadLabel = "mkdir";
            break;
        case kNuThreadIDDataFork:
            threadLabel = "data fork";
            break;
        case kNuThreadIDDiskImage:
            threadLabel = "disk image";
            break;
        case kNuThreadIDRsrcFork:
            threadLabel = "rsrc fork";
            break;
        case kNuThreadIDFilename:
            threadLabel = "filename";
            break;
        default:
            break;
        }

        printf("  ThreadIdx %ld - 0x%08lx (%s)\n", pThread->threadIdx,
            threadID, threadLabel);
    }

    return kNuOK;
}
Example #6
0
/*
 * Update the contents of a pre-sized thread, such as a filename or
 * comment thread.
 *
 * The data from the source must fit within the limits of the existing
 * thread.  The source data is never compressed, and must not come from
 * a compressed source.
 *
 * You aren't allowed to update threads that have been deleted.  Updating
 * newly-added threads isn't possible, since they aren't really threads yet.
 */
NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx,
    NuDataSource* pDataSource, int32_t* pMaxLen)
{
    NuError err;
    NuThreadMod* pThreadMod = NULL;
    NuRecord* pFoundRecord;
    NuThread* pFoundThread;

    if (pDataSource == NULL) {
        err = kNuErrInvalidArg;
        goto bail;
    }

    /* presized threads always contain uncompressed data */
    if (Nu_DataSourceGetThreadFormat(pDataSource) !=
        kNuThreadFormatUncompressed)
    {
        err = kNuErrBadFormat;
        Nu_ReportError(NU_BLOB, err,
            "presized threads can't hold compressed data");
        goto bail;
    }

    if (Nu_IsReadOnly(pArchive))
        return kNuErrArchiveRO;
    err = Nu_GetTOCIfNeeded(pArchive);
    BailError(err);

    /*
     * Find the thread in the "copy" set.  (If there isn't a copy set,
     * make one.)
     */
    err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord,
            &pFoundThread);
    BailError(err);

    if (!Nu_IsPresizedThreadID(NuGetThreadID(pFoundThread)) ||
        !(pFoundThread->thCompThreadEOF >= pFoundThread->thThreadEOF))
    {
        err = kNuErrNotPreSized;
        Nu_ReportError(NU_BLOB, err, "invalid thread for update");
        goto bail;
    }

    if (pMaxLen != NULL)
        *pMaxLen = pFoundThread->thCompThreadEOF;

    /*
     * Check to see if somebody is trying to delete this, or has already
     * updated it.
     */
    if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != NULL) {
        DBUG(("--- Tried to modify a deleted or modified thread\n"));
        err = kNuErrModThreadChange;
        goto bail;
    }

    /*
     * Verify that "otherLen" in the data source is less than or equal
     * to our len, if we can.  If the data source is a file on disk,
     * we're not really supposed to look at it until we flush.  We
     * could sneak a peek right now, which would prevent us from aborting
     * the entire operation when it turns out the file won't fit, but
     * that violates our semantics (and besides, the application really
     * should've done that already).
     *
     * If the data source is from a file, we just assume it'll fit and
     * let the chips fall where they may later on.
     */
    if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile) {
        if (pFoundThread->thCompThreadEOF <
            Nu_DataSourceGetOtherLen(pDataSource))
        {
            err = kNuErrPreSizeOverflow;
            Nu_ReportError(NU_BLOB, err, "can't put %u bytes into %u",
                Nu_DataSourceGetOtherLen(pDataSource),
                pFoundThread->thCompThreadEOF);
            goto bail;
        }

        /* check for zero-length and excessively long filenames */
        if (NuGetThreadID(pFoundThread) == kNuThreadIDFilename &&
            (Nu_DataSourceGetOtherLen(pDataSource) == 0 ||
             Nu_DataSourceGetOtherLen(pDataSource) > kNuReasonableFilenameLen))
        {
            err = kNuErrInvalidFilename;
            Nu_ReportError(NU_BLOB, err, "invalid filename (%u bytes)",
                Nu_DataSourceGetOtherLen(pDataSource));
            goto bail;
        }
    }

    /*
     * Looks like it'll fit, and it's the right kind of data.  Create
     * an "update" threadMod.  Note this copies the data source.
     */
    Assert(pFoundThread->thThreadFormat == kNuThreadFormatUncompressed);
    err = Nu_ThreadModUpdate_New(pArchive, threadIdx, pDataSource, &pThreadMod);
    BailError(err);
    Assert(pThreadMod != NULL);

    /* add the thread mod to the record */
    Nu_RecordAddThreadMod(pFoundRecord, pThreadMod);

    /*
     * NOTE: changes to filename threads will be picked up later and
     * incorporated into the record's threadFilename.  We don't worry
     * about the record header filename, because we might be doing an
     * update-in-place and that prevents us from removing the filename
     * (doing so would change the size of the archive).  No need to
     * do any filename-specific changes here.
     */

bail:
    return err;
}
Example #7
0
/*
 * Normalize a path for the conventions on the output filesystem.  This
 * adds optional file type preservation.
 *
 * The path from the archive is in "pPathProposal".  Thew new pathname
 * will be placed in the "new pathname" section of "pPathProposal".
 *
 * The new pathname may be shorter (because characters were removed) or
 * longer (if we add a "#XXYYYYZ" extension or replace chars with '%' codes).
 *
 * This returns the new pathname, which is held in NulibState's temporary
 * pathname buffer.
 */
const char* NormalizePath(NulibState* pState, NuPathnameProposal* pPathProposal)
{
    NuError err = kNuErrNone;
    char* pathBuf;
    const char* startp;
    const char* endp;
    char* dstp;
    char localFssep;
    int newBufLen;

    Assert(pState != NULL);
    Assert(pPathProposal != NULL);
    Assert(pPathProposal->pathnameUNI != NULL);

    localFssep = NState_GetSystemPathSeparator(pState);

    /*
     * Set up temporary buffer space.  The maximum possible expansion
     * requires converting all chars to '%' codes and adding the longest
     * possible preservation string.
     */
    newBufLen = strlen(pPathProposal->pathnameUNI)*3 + kMaxPathGrowth +1;
    NState_SetTempPathnameLen(pState, newBufLen);
    pathBuf = NState_GetTempPathnameBuf(pState);
    Assert(pathBuf != NULL);
    if (pathBuf == NULL)
        return NULL;

    startp = pPathProposal->pathnameUNI;
    dstp = pathBuf;
    while (*startp == pPathProposal->filenameSeparator) {
        /* ignore leading path sep; always extract to current dir */
        startp++;
    }

    /* normalize all directory components and the filename component */
    while (startp != NULL) {
        endp = strchr(startp, pPathProposal->filenameSeparator);
        if (endp != NULL) {
            /* normalize directory component */
            err = NormalizeDirectoryName(pState, startp, endp - startp,
                    pPathProposal->filenameSeparator, &dstp,
                    NState_GetTempPathnameLen(pState));
            if (err != kNuErrNone)
                goto bail;

            *dstp++ = localFssep;

            startp = endp +1;
        } else {
            /* normalize filename */
            err = NormalizeFileName(pState, startp, strlen(startp),
                    pPathProposal->filenameSeparator, &dstp,
                    NState_GetTempPathnameLen(pState));
            if (err != kNuErrNone)
                goto bail;

            /* add/replace extension if necessary */
            *dstp++ = '\0';
            if (NState_GetModPreserveType(pState)) {
                AddPreservationString(pState, pPathProposal, pathBuf);
            } else if (NuGetThreadID(pPathProposal->pThread) == kNuThreadIDRsrcFork)
            {
#ifndef HAS_RESOURCE_FORKS
                /* add this in lieu of the preservation extension */
                strcat(pathBuf, kResourceStr);
#endif
            }

            startp = NULL;   /* we're done */
        }
    }

    pPathProposal->newPathnameUNI = pathBuf;
    pPathProposal->newFilenameSeparator = localFssep;

    /* check for overflow */
    Assert(dstp - pathBuf <= newBufLen);

    /*
     * If "junk paths" is set, drop everything but the last component.
     */
    if (NState_GetModJunkPaths(pState)) {
        char* lastFssep;
        lastFssep = strrchr(pathBuf, localFssep);
        if (lastFssep != NULL) {
            Assert(*(lastFssep+1) != '\0'); /* should already have been caught*/
            memmove(pathBuf, lastFssep+1, strlen(lastFssep+1)+1);
        }
    }

bail:
    if (err != kNuErrNone)
        return NULL;
    return pathBuf;
}
Example #8
0
/*
 * Extract all of the records from the archive, pulling out and displaying
 * comment threads.
 *
 * The "bulk extract" call doesn't deal with comments.  Since we want to
 * show them while we're extracting the files, we have to manually find
 * and extract them.
 */
static NuError ExtractAllRecords(NulibState* pState, NuArchive* pArchive)
{
    NuError err;
    const NuRecord* pRecord;
    const NuThread* pThread;
    NuRecordIdx recordIdx;
    NuAttr numRecords;
    int idx, threadIdx;

    DBUG(("--- doing manual extract\n"));
    Assert(NState_GetCommand(pState) == kCommandExtract);   /* no "-p" here */

    err = NuGetAttr(pArchive, kNuAttrNumRecords, &numRecords);
    for (idx = 0; idx < (int) numRecords; idx++) {
        err = NuGetRecordIdxByPosition(pArchive, idx, &recordIdx);
        if (err != kNuErrNone) {
            fprintf(stderr, "ERROR: couldn't get record #%d (err=%d)\n",
                idx, err);
            goto bail;
        }

        err = NuGetRecord(pArchive, recordIdx, &pRecord);
        if (err != kNuErrNone) {
            fprintf(stderr, "ERROR: unable to get recordIdx %u\n", recordIdx);
            goto bail;
        }

        /* do we want to extract this record? */
        if (!IsSpecified(pState, pRecord))
            continue;
        NState_IncMatchCount(pState);

        /*
         * Look for a comment thread.
         */
        for (threadIdx = 0; (uint32_t)threadIdx < pRecord->recTotalThreads;
            threadIdx++)
        {
            pThread = NuGetThread(pRecord, threadIdx);
            Assert(pThread != NULL);

            if (NuGetThreadID(pThread) == kNuThreadIDComment &&
                pThread->actualThreadEOF > 0)
            {
                UNICHAR* filenameUNI = CopyMORToUNI(pRecord->filenameMOR);
                printf("----- '%s':\n", filenameUNI);
                free(filenameUNI);
                err = NuExtractThread(pArchive, pThread->threadIdx,
                        NState_GetCommentSink(pState));
                if (err != kNuErrNone) {
                    printf("[comment extraction failed, continuing\n");
                } else {
                    printf("\n-----\n");
                }
            }
        }

        /* extract the record, using the usual mechanisms */
        err = NuExtractRecord(pArchive, recordIdx);
        if (err != kNuErrNone)
            goto bail;
    }

bail:
    return err;
}