예제 #1
0
파일: Thread.c 프로젝트: HankG/ciderpress
/*
 * Extract the thread to the specified file pointer.
 *
 * If the archive is a stream, the stream must be positioned at the
 * start of pThread's data.  If not, it will be seeked first.
 */
static NuError Nu_ExtractThreadToDataSink(NuArchive* pArchive,
    const NuRecord* pRecord, const NuThread* pThread,
    NuProgressData* pProgress, NuDataSink* pDataSink)
{
    NuError err;
    NuFunnel* pFunnel = NULL;

    /* if it's not a stream, seek to the appropriate spot in the file */
    if (!Nu_IsStreaming(pArchive)) {
        err = Nu_SeekArchive(pArchive, pArchive->archiveFp,
                pThread->fileOffset, SEEK_SET);
        if (err != kNuErrNone) {
            Nu_ReportError(NU_BLOB, err, "Unable to seek input to %ld",
                pThread->fileOffset);
            goto bail;
        }
    }

    /*
     * Set up an output funnel to write to.
     */
    err = Nu_FunnelNew(pArchive, pDataSink, Nu_DataSinkGetConvertEOL(pDataSink),
            pArchive->valEOL, pProgress, &pFunnel);
    BailError(err);

    /*
     * Write it.
     */
    err = Nu_ExpandStream(pArchive, pRecord, pThread, pArchive->archiveFp,
            pFunnel);
    if (err != kNuErrNone) {
        if (err != kNuErrSkipped && err != kNuErrAborted)
            Nu_ReportError(NU_BLOB, err, "ExpandStream failed");
        goto bail;
    }

bail:
    (void) Nu_FunnelFree(pArchive, pFunnel);
    return err;
}
예제 #2
0
/*
 * Compress "srcLen" bytes from "pStraw" to "fp".
 */
NuError
Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    ulong srcLen, ulong* pDstLen, ushort* pCrc)
{
    NuError err = kNuErrNone;
    z_stream zstream;
    int zerr;
    Bytef* outbuf = nil;

    Assert(pArchive != nil);
    Assert(pStraw != nil);
    Assert(fp != nil);
    Assert(srcLen > 0);
    Assert(pDstLen != nil);
    Assert(pCrc != nil);

    err = Nu_AllocCompressionBufferIFN(pArchive);
    if (err != kNuErrNone)
        return err;

    /* allocate a similarly-sized buffer for the output */
    outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize);
    BailAlloc(outbuf);

    /*
     * Initialize the zlib stream.
     */
    zstream.zalloc = Nu_zalloc;
    zstream.zfree = Nu_zfree;
    zstream.opaque = pArchive;
    zstream.next_in = nil;
    zstream.avail_in = 0;
    zstream.next_out = outbuf;
    zstream.avail_out = kNuGenCompBufSize;
    zstream.data_type = Z_UNKNOWN;

    zerr = deflateInit(&zstream, kNuDeflateLevel);
    if (zerr != Z_OK) {
        err = kNuErrInternal;
        if (zerr == Z_VERSION_ERROR) {
            Nu_ReportError(NU_BLOB, err,
                "installed zlib is not compatible with linked version (%s)",
                ZLIB_VERSION);
        } else {
            Nu_ReportError(NU_BLOB, err,
                "call to deflateInit failed (zerr=%d)", zerr);
        }
        goto bail;
    }

    /*
     * Loop while we have data.
     */
    do {
        ulong getSize;
        int flush;

        /* should be able to read a full buffer every time */
        if (zstream.avail_in == 0 && srcLen) {
            getSize = (srcLen > kNuGenCompBufSize) ? kNuGenCompBufSize : srcLen;
            DBUG(("+++ reading %ld bytes\n", getSize));

            err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getSize);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "deflate read failed");
                goto z_bail;
            }

            srcLen -= getSize;

            *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getSize);

            zstream.next_in = pArchive->compBuf;
            zstream.avail_in = getSize;
        }

        if (srcLen == 0)
            flush = Z_FINISH;       /* tell zlib that we're done */
        else
            flush = Z_NO_FLUSH;     /* more to come! */

        zerr = deflate(&zstream, flush);
        if (zerr != Z_OK && zerr != Z_STREAM_END) {
            err = kNuErrInternal;
            Nu_ReportError(NU_BLOB, err, "zlib deflate call failed (zerr=%d)",
                zerr);
            goto z_bail;
        }

        /* write when we're full or when we're done */
        if (zstream.avail_out == 0 ||
            (zerr == Z_STREAM_END && zstream.avail_out != kNuGenCompBufSize))
        {
            DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf));
            err = Nu_FWrite(fp, outbuf, zstream.next_out - outbuf);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "fwrite failed in deflate");
                goto z_bail;
            }

            zstream.next_out = outbuf;
            zstream.avail_out = kNuGenCompBufSize;
        }
    } while (zerr == Z_OK);

    Assert(zerr == Z_STREAM_END);       /* other errors should've been caught */

    *pDstLen = zstream.total_out;

z_bail:
    deflateEnd(&zstream);        /* free up any allocated structures */

bail:
    if (outbuf != nil)
        free(outbuf);
    return err;
}
예제 #3
0
/*
 * Expand from "infp" to "pFunnel".
 */
NuError
Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc)
{
    NuError err = kNuErrNone;
    z_stream zstream;
    int zerr;
    ulong compRemaining;
    Bytef* outbuf;

    Assert(pArchive != nil);
    Assert(pThread != nil);
    Assert(infp != nil);
    Assert(pFunnel != nil);

    err = Nu_AllocCompressionBufferIFN(pArchive);
    if (err != kNuErrNone)
        return err;

    /* allocate a similarly-sized buffer for the output */
    outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize);
    BailAlloc(outbuf);

    compRemaining = pThread->thCompThreadEOF;

    /*
     * Initialize the zlib stream.
     */
    zstream.zalloc = Nu_zalloc;
    zstream.zfree = Nu_zfree;
    zstream.opaque = pArchive;
    zstream.next_in = nil;
    zstream.avail_in = 0;
    zstream.next_out = outbuf;
    zstream.avail_out = kNuGenCompBufSize;
    zstream.data_type = Z_UNKNOWN;

    zerr = inflateInit(&zstream);
    if (zerr != Z_OK) {
        err = kNuErrInternal;
        if (zerr == Z_VERSION_ERROR) {
            Nu_ReportError(NU_BLOB, err,
                "installed zlib is not compatible with linked version (%s)",
                ZLIB_VERSION);
        } else {
            Nu_ReportError(NU_BLOB, err,
                "call to inflateInit failed (zerr=%d)", zerr);
        }
        goto bail;
    }

    /*
     * Loop while we have data.
     */
    do {
        ulong getSize;

        /* read as much as we can */
        if (zstream.avail_in == 0) {
            getSize = (compRemaining > kNuGenCompBufSize) ?
                        kNuGenCompBufSize : compRemaining;
            DBUG(("+++ reading %ld bytes (%ld left)\n", getSize,
                compRemaining));

            err = Nu_FRead(infp, pArchive->compBuf, getSize);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "inflate read failed");
                goto z_bail;
            }

            compRemaining -= getSize;

            zstream.next_in = pArchive->compBuf;
            zstream.avail_in = getSize;
        }

        /* uncompress the data */
        zerr = inflate(&zstream, Z_NO_FLUSH);
        if (zerr != Z_OK && zerr != Z_STREAM_END) {
            err = kNuErrInternal;
            Nu_ReportError(NU_BLOB, err, "zlib inflate call failed (zerr=%d)",
                zerr);
            goto z_bail;
        }

        /* write every time there's anything (buffer will usually be full) */
        if (zstream.avail_out != kNuGenCompBufSize) {
            DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf));
            err = Nu_FunnelWrite(pArchive, pFunnel, outbuf,
                    zstream.next_out - outbuf);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "write failed in inflate");
                goto z_bail;
            }

            if (pCrc != nil)
                *pCrc = Nu_CalcCRC16(*pCrc, outbuf, zstream.next_out - outbuf);

            zstream.next_out = outbuf;
            zstream.avail_out = kNuGenCompBufSize;
        }
    } while (zerr == Z_OK);

    Assert(zerr == Z_STREAM_END);       /* other errors should've been caught */

    if (zstream.total_out != pThread->actualThreadEOF) {
        err = kNuErrBadData;
        Nu_ReportError(NU_BLOB, err,
            "size mismatch on inflated file (%ld vs %ld)",
            zstream.total_out, pThread->actualThreadEOF);
        goto z_bail;
    }

z_bail:
    inflateEnd(&zstream);        /* free up any allocated structures */

bail:
    if (outbuf != nil)
        free(outbuf);
    return err;
}
예제 #4
0
파일: Thread.c 프로젝트: HankG/ciderpress
/*
 * Skip past some or all of the thread data in the archive.  For file
 * archives, we scan all the threads, but for streaming archives we only
 * want to scan up to the filename thread.  (If the filename thread comes
 * after one of the data threads, we have a problem!)
 *
 * The tricky part here is that we don't want to skip over a filename
 * thread.  We actually want to read it in, so that we have something to
 * show to the application.  (Someday I'll get AndyN for putting me
 * through this...)
 */
NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord, long numThreads)
{
    NuError err = kNuErrNone;
    NuThread* pThread;
    FILE* fp;

    Assert(pArchive != NULL);
    Assert(pRecord != NULL);

    fp = pArchive->archiveFp;

    Assert(numThreads <= (long)pRecord->recTotalThreads);

    pThread = pRecord->pThreads;
    while (numThreads--) {
        if (pRecord->threadFilenameMOR == NULL &&
            NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) ==
                kNuThreadIDFilename)
        {
            /* it's the first filename thread, read the whole thing */
            if (pThread->thCompThreadEOF > kNuReasonableFilenameLen) {
                err = kNuErrBadRecord;
                Nu_ReportError(NU_BLOB, err, "Bad thread filename len (%u)",
                    pThread->thCompThreadEOF);
                goto bail;
            }
            pRecord->threadFilenameMOR = Nu_Malloc(pArchive,
                                        pThread->thCompThreadEOF +1);
            BailAlloc(pRecord->threadFilenameMOR);

            /* note there is no CRC on a filename thread */
            (void) Nu_ReadBytes(pArchive, fp, pRecord->threadFilenameMOR,
                    pThread->thCompThreadEOF);
            if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "Failed reading filename thread");
                goto bail;
            }

            /* null-terminate on the actual len, not the buffer len */
            pRecord->threadFilenameMOR[pThread->thThreadEOF] = '\0';

            Nu_StripHiIfAllSet(pRecord->threadFilenameMOR);

            /* prefer this one over the record one, but only one should exist */
            if (pRecord->filenameMOR != NULL) {
                DBUG(("--- HEY: got record filename and thread filename\n"));
            }
            pRecord->filenameMOR = pRecord->threadFilenameMOR;

        } else {
            /* not a filename (or not first filename), skip past it */
            err = Nu_SeekArchive(pArchive, pArchive->archiveFp,
                    pThread->thCompThreadEOF, SEEK_CUR);
            BailError(err);
        }

        pThread++;
    }

    /*
     * Should've had one by now.  Supposedly, older versions of ShrinkIt
     * wouldn't prompt for a disk image name on DOS 3.3 volumes, so you'd
     * end up with a disk image that had no name attached.  This will tend
     * to confuse things, so we go ahead and give it a name.
     */
    if (pRecord->filenameMOR == NULL) {
        DBUG(("+++ no filename found, using default record name\n"));
        pRecord->filenameMOR = kNuDefaultRecordName;
    }

    pArchive->currentOffset += pRecord->totalCompLength;

    if (!Nu_IsStreaming(pArchive)) {
        Assert(pArchive->currentOffset == ftell(pArchive->archiveFp));
    }

bail:
    return err;
}
예제 #5
0
파일: Thread.c 프로젝트: HankG/ciderpress
/*
 * 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;
}