From 16f738ecee689c6feb2acb7e4ef4d9bb4144ae7d Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 8 Jun 2008 11:04:43 +0200 Subject: Initial commit --- zip/ZipArchive/ZipStorage.cpp | 615 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 615 insertions(+) create mode 100644 zip/ZipArchive/ZipStorage.cpp (limited to 'zip/ZipArchive/ZipStorage.cpp') diff --git a/zip/ZipArchive/ZipStorage.cpp b/zip/ZipArchive/ZipStorage.cpp new file mode 100644 index 0000000..d416fd6 --- /dev/null +++ b/zip/ZipArchive/ZipStorage.cpp @@ -0,0 +1,615 @@ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyrighted 2000 - 2007 by Artpol Software - Tadeusz Dracz +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// For the licensing details refer to the License.txt file. +// +// Web Site: http://www.artpol-software.com +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ZipStorage.h" +#include "ZipArchive.h" +#include "ZipPlatform.h" + +char CZipStorage::m_gszExtHeaderSignat[] = {0x50, 0x4b, 0x07, 0x08}; +const ZIP_FILE_USIZE CZipStorage::SignatureNotFound = ZIP_FILE_USIZE(-1); + + +CZipStorage::CZipStorage() +{ + Initialize(); +} + +void CZipStorage::Initialize() +{ + m_pSplitChangeVolumeFunc = m_pSpanChangeVolumeFunc = m_pChangeVolumeFunc = NULL; + m_iWriteBufferSize = 65536; + m_pFile = NULL; + m_szSplitExtension = _T("zip"); + m_iLocateBufferSize = 32768; + m_uBytesBeforeZip = 0; + m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED; + m_szArchiveName.Empty(); +} + +CZipStorage::~CZipStorage() +{ + +} + +DWORD CZipStorage::Read(void *pBuf, DWORD iSize, bool bAtOnce) +{ + if (iSize == 0) + return 0; + DWORD iRead; + for(;;) + { + iRead = m_pFile->Read(pBuf, iSize); + if (!iRead) + { + if (IsSegmented()) + ChangeVolume(); + else + ThrowError(CZipException::badZipFile); + } + else + break; + } + + if (iRead == iSize) + return iRead; + else if (bAtOnce || !IsSegmented()) + ThrowError(CZipException::badZipFile); + + while (iRead < iSize) + { + ChangeVolume(); + UINT iNewRead = m_pFile->Read((char*)pBuf + iRead, iSize - iRead); + if (!iNewRead && iRead < iSize) + ThrowError(CZipException::badZipFile); + iRead += iNewRead; + } + + return iRead; +} + +void CZipStorage::Open(LPCTSTR lpszPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize) +{ + m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED; + m_pWriteBuffer.Allocate(m_iWriteBufferSize); + m_uBytesInWriteBuffer = 0; + m_bNewSegm = false; + m_pFile = &m_internalfile; + m_bInMemory = false; + m_szArchiveName = lpszPathName; + m_pChangeVolumeFunc = NULL; + + if (iMode == CZipArchive::zipCreate || iMode == CZipArchive::zipCreateSegm + || iMode == CZipArchive::zipCreateAppend) // create new archive + { + m_bReadOnly = false; + m_uCurrentVolume = 0; + if (iMode == CZipArchive::zipCreate || iMode == CZipArchive::zipCreateAppend) + { + m_iSegmMode = noSegments; + OpenFile(lpszPathName, (iMode == CZipArchive::zipCreate ? CZipFile::modeCreate : CZipFile::modeNoTruncate) | CZipFile::modeReadWrite); + } + else // create a segmented archive + { + m_bNewSegm = true; + m_uBytesWritten = 0; + if (uVolumeSize == ZIP_AUTODETECT_VOLUME_SIZE) // spanned archive + { + if (!m_pSpanChangeVolumeFunc) + ThrowError(CZipException::noCallback); + if (!ZipPlatform::IsDriveRemovable(lpszPathName)) + ThrowError(CZipException::nonRemovable); + m_iSegmMode = spannedArchive; + m_pChangeVolumeFunc = m_pSpanChangeVolumeFunc; + } + else + { + m_uSplitData = uVolumeSize; + m_iSegmMode = splitArchive; + m_pChangeVolumeFunc = m_pSplitChangeVolumeFunc; + } + + NextVolume(4); + Write(m_gszExtHeaderSignat, 4, true); + } + } + else // open existing + { + m_bReadOnly = iMode == CZipArchive::zipOpenReadOnly; + OpenFile(lpszPathName, CZipFile::modeNoTruncate | + (m_bReadOnly ? CZipFile::modeRead : CZipFile::modeReadWrite)); + // m_uData and m_iSegmMode are automatically set during reading the central dir + m_iSegmMode = uVolumeSize == 0 ? suggestedAuto : suggestedSplit; + } + +} + + +void CZipStorage::Open(CZipAbstractFile& af, int iMode) +{ + m_pWriteBuffer.Allocate(m_iWriteBufferSize); + m_uBytesInWriteBuffer = 0; + m_bNewSegm = false; + m_pFile = ⁡ + m_bInMemory = true; + + if (iMode == CZipArchive::zipCreate || iMode == CZipArchive::zipCreateAppend) + { + m_uCurrentVolume = 0; + m_iSegmMode = noSegments; + if (iMode == CZipArchive::zipCreate) + af.SetLength(0); + else + af.SeekToEnd(); + } + else // open existing + { + af.SeekToBegin(); + m_iSegmMode = suggestedAuto; + } +} + + +void CZipStorage::ChangeVolume(ZIP_VOLUME_TYPE uNumber) +{ + if (uNumber == m_uCurrentVolume || m_iSegmMode == noSegments) // the second condition may happen in some bad archives + return; + + m_uCurrentVolume = uNumber; + OpenFile(IsSpanned() ? ChangeSpannedRead() : ChangeSplitRead(), + CZipFile::modeNoTruncate | CZipFile::modeRead); +} + +void CZipStorage::ThrowError(int err) +{ + CZipException::Throw(err, m_pFile->GetFilePath()); +} + +bool CZipStorage::OpenFile(LPCTSTR lpszName, UINT uFlags, bool bThrow) +{ + return m_pFile->Open(lpszName, uFlags | CZipFile::shareDenyWrite, bThrow); +} + + +CZipString CZipStorage::ChangeSpannedRead() +{ + CZipString szTemp = m_pFile->GetFilePath(); + m_pFile->Close(); + CallCallback(0, CZipSegmCallback::scVolumeNeededForRead, szTemp); + return szTemp; +} + +CZipString CZipStorage::ChangeSplitRead() +{ + bool lastPart = (ZIP_SIZE_TYPE)m_uCurrentVolume == m_uSplitData; + CZipString szTemp = GetSplitVolumeName(lastPart); + if (m_pChangeVolumeFunc) + { + int iCode = CZipSegmCallback::scVolumeNeededForRead; + for(;;) + { + CallCallback(lastPart ? ZIP_SPLIT_LAST_VOLUME : 0, iCode, szTemp); + if (ZipPlatform::FileExists(m_pChangeVolumeFunc->m_szExternalFile)) + { + szTemp = m_pChangeVolumeFunc->m_szExternalFile; + break; + } + else + iCode = CZipSegmCallback::scFileNotFound; + } + } + m_pFile->Close(); + return szTemp; +} + +CZipString CZipStorage::RenameLastFileInSplitArchive() +{ + ASSERT(IsSplit()); + // give to the last volume the zip extension + CZipString szFileName = m_pFile->GetFilePath(); + CZipString szNewFileName = GetSplitVolumeName(true); + if (m_pChangeVolumeFunc) + { + int code = CZipSegmCallback::scVolumeNeededForWrite; + for(;;) + { + CallCallback(ZIP_SPLIT_LAST_VOLUME, code, szNewFileName); + szNewFileName = m_pChangeVolumeFunc->m_szExternalFile; + if (ZipPlatform::FileExists(szNewFileName)) + code = CZipSegmCallback::scFileNameDuplicated; + else + break; + } + } + if (!m_bInMemory) + { + m_pFile->Flush(); + m_pFile->Close(); + } + if (!m_pChangeVolumeFunc && ZipPlatform::FileExists(szNewFileName)) + ZipPlatform::RemoveFile(szNewFileName); + ZipPlatform::RenameFile(szFileName, szNewFileName); + return szNewFileName; +} + +CZipString CZipStorage::Close(bool bAfterException) +{ + bool bClose = true; + CZipString sz; + if (!bAfterException) + { + Flush(); + if (IsSplit() && m_bNewSegm) + { + sz = RenameLastFileInSplitArchive(); + bClose = false;// already closed in RenameLastFileInSplitArchive + } + } + if (sz.IsEmpty()) + sz = m_pFile->GetFilePath(); + if (bClose && !m_bInMemory) + { + if (!bAfterException) + FlushFile(); + m_pFile->Close(); + } + + m_pWriteBuffer.Release(); + m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED; + m_iSegmMode = noSegments; + m_pFile = NULL; + m_uBytesBeforeZip = 0; + return sz; +} + +CZipString CZipStorage::GetSplitVolumeName(bool bLast) const +{ + CZipString szFilePath = m_szArchiveName; + CZipPathComponent zpc(szFilePath); + CZipString szExt; + if (bLast) + szExt = m_szSplitExtension; + else + { + DWORD vol = m_uCurrentVolume + 1; + if (vol < 100) + szExt.Format(_T("z%.2u"), vol); + else + szExt.Format(_T("z%u"), vol); + } + zpc.SetExtension(szExt); + return zpc.GetFullPath(); +} + +void CZipStorage::NextVolume(ZIP_SIZE_TYPE uNeeded) +{ + Flush(); + ASSERT(m_iSegmMode != noSegments); + bool bSpan = IsSpanned(); + if (m_uBytesWritten) + { + m_uBytesWritten = 0; + m_uCurrentVolume++; + ZIP_VOLUME_TYPE uMaxVolumes = (ZIP_VOLUME_TYPE)(bSpan ? 999 : 0xFFFF); + if (m_uCurrentVolume >= uMaxVolumes) // m_uCurrentVolume is a zero-based index + ThrowError(CZipException::tooManyVolumes); + } + + CZipString szFileName; + + if (bSpan) + szFileName = m_szArchiveName; + else + szFileName = GetSplitVolumeName(false); + + if (!m_pFile->IsClosed()) + { + m_pFile->Flush(); + m_pFile->Close(); + } + + if (m_pChangeVolumeFunc) + { + int iCode = CZipSegmCallback::scVolumeNeededForWrite; + for(;;) + { + CallCallback(uNeeded, iCode, szFileName); + if (!bSpan) + // allow the user to change the filename + szFileName = m_pChangeVolumeFunc->m_szExternalFile; + + if (ZipPlatform::FileExists(szFileName)) + iCode = CZipSegmCallback::scFileNameDuplicated; + else + { + if (bSpan) + { + CZipString label; + label.Format(_T("pkback# %.3d"), m_uCurrentVolume + 1); + if (!ZipPlatform::SetVolLabel(szFileName, label)) + { + iCode = CZipSegmCallback::scCannotSetVolLabel; + continue; + } + } + + if (OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite, false)) + break; + else + iCode = CZipSegmCallback::scFileCreationFailure; + } + + } + m_uCurrentVolSize = bSpan ? GetFreeVolumeSpace() : m_uSplitData; + } + else + { + if (bSpan) + ThrowError(CZipException::internalError); + m_uCurrentVolSize = m_uSplitData; + OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite); + } +} + +void CZipStorage::CallCallback(ZIP_SIZE_TYPE uNeeded, int iCode, CZipString szTemp) +{ + if (!m_pChangeVolumeFunc) + ThrowError(CZipException::internalError); + m_pChangeVolumeFunc->m_szExternalFile = szTemp; + m_pChangeVolumeFunc->m_uVolumeNeeded = (ZIP_VOLUME_TYPE)(m_uCurrentVolume + 1); + m_pChangeVolumeFunc->m_iCode = iCode; + if (!m_pChangeVolumeFunc->Callback(uNeeded)) + CZipException::Throw(CZipException::aborted, szTemp); +} + +ZIP_SIZE_TYPE CZipStorage::GetFreeVolumeSpace() const +{ + ASSERT (IsSpanned()); + CZipString szTemp = m_pFile->GetFilePath(); + if (szTemp.IsEmpty()) // called once when creating a segmented archive + return 0; + else + { + CZipPathComponent zpc(szTemp); + ULONGLONG ret = ZipPlatform::GetDeviceFreeSpace(zpc.GetFilePath()); + if (ret > (ZIP_SIZE_TYPE)(-1)) + return (ZIP_SIZE_TYPE)(-1); + else + return (ZIP_SIZE_TYPE)ret; + } +} + + +void CZipStorage::UpdateSegmMode(ZIP_VOLUME_TYPE uLastDisk) +{ + m_uCurrentVolume = uLastDisk; + if (uLastDisk) + { + // segmentation detected + CZipString szFilePath = m_pFile->GetFilePath(); + if (m_iSegmMode == suggestedAuto) + m_iSegmMode = ZipPlatform::IsDriveRemovable(szFilePath) ? + spannedArchive : splitArchive; + else + { + ASSERT(m_iSegmMode == suggestedSplit); + m_iSegmMode = splitArchive; + } + + if (IsSpanned()) + { + if (!m_pSpanChangeVolumeFunc) + ThrowError(CZipException::noCallback); + m_pChangeVolumeFunc = m_pSpanChangeVolumeFunc; + } + else /*if (IsSplit())*/ + { + m_uSplitData = uLastDisk; // volume with .zip extension ( the last one) + m_pChangeVolumeFunc = m_pSplitChangeVolumeFunc; + } + CZipPathComponent zpc(szFilePath); + m_szSplitExtension = zpc.GetFileExt(); + m_pWriteBuffer.Release(); // no need for this in this case + } + else + m_iSegmMode = noSegments; + +} + +ZIP_SIZE_TYPE CZipStorage::AssureFree(ZIP_SIZE_TYPE uNeeded) +{ + ZIP_SIZE_TYPE uFree; + while ((uFree = VolumeLeft()) < uNeeded) + { + if (IsSplit() && !m_uBytesWritten && !m_uBytesInWriteBuffer) + // in the splitArchive mode, if the size of the archive is less + // than the size of the packet to be written at once, + // increase once the size of the volume + m_uCurrentVolSize = uNeeded; + else + NextVolume(uNeeded); + } + return uFree; +} + +void CZipStorage::Write(const void *pBuf, DWORD iSize, bool bAtOnce) +{ + if (!IsSegmented()) + WriteInternalBuffer((char*)pBuf, iSize); + else + { + // if not at once, one byte is enough free space + DWORD iNeeded = bAtOnce ? iSize : 1; + DWORD uTotal = 0; + + while (uTotal < iSize) + { + ZIP_SIZE_TYPE uFree = AssureFree(iNeeded); + DWORD uLeftToWrite = iSize - uTotal; + DWORD uToWrite = uFree < uLeftToWrite ? (DWORD)uFree : uLeftToWrite; + WriteInternalBuffer((char*)pBuf + uTotal, uToWrite); + if (bAtOnce) + return; + else + uTotal += uToWrite; + } + + } +} + + +void CZipStorage::WriteInternalBuffer(const char *pBuf, DWORD uSize) +{ + DWORD uWritten = 0; + while (uWritten < uSize) + { + DWORD uFreeInBuffer = GetFreeInBuffer(); + if (uFreeInBuffer == 0) + { + Flush(); + uFreeInBuffer = m_pWriteBuffer.GetSize(); + } + DWORD uLeftToWrite = uSize - uWritten; + DWORD uToCopy = uLeftToWrite < uFreeInBuffer ? uLeftToWrite : uFreeInBuffer; + memcpy((char*)m_pWriteBuffer + m_uBytesInWriteBuffer, pBuf + uWritten, uToCopy); + uWritten += uToCopy; + m_uBytesInWriteBuffer += uToCopy; + } +} + +ZIP_SIZE_TYPE CZipStorage::VolumeLeft() const +{ + // for spanned archives m_uCurrentVolSize is updated after each flush() + ZIP_SIZE_TYPE uBytes = m_uBytesInWriteBuffer + (IsSpanned() ? 0 : m_uBytesWritten); + return uBytes > m_uCurrentVolSize ? 0 : m_uCurrentVolSize - uBytes; +} + +void CZipStorage::Flush() +{ + if (m_uBytesInWriteBuffer) + { + m_pFile->Write(m_pWriteBuffer, m_uBytesInWriteBuffer); + if (m_iSegmMode != noSegments) + m_uBytesWritten += m_uBytesInWriteBuffer; + m_uBytesInWriteBuffer = 0; + } + if (IsSpanned()) + // after writing it is difficult to predict the free space due to + // not completly written clusters, write operation may start from a new cluster + m_uCurrentVolSize = GetFreeVolumeSpace(); + +} + +ZIP_FILE_USIZE CZipStorage::LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth) +{ + const int recordSize = 4; + CZipAutoBuffer buffer(m_iLocateBufferSize); + ZIP_FILE_USIZE uFileLength = m_pFile->GetLength(); + ZIP_SIZE_TYPE max = (ZIP_SIZE_TYPE)(uFileLength < uMaxDepth ? uFileLength : uMaxDepth); + ZIP_SIZE_TYPE position = (ZIP_SIZE_TYPE)(uFileLength - m_pFile->GetPosition()); + int offset = 0; + int leftToFind = recordSize - 1; + int toRead = m_iLocateBufferSize; + bool found = false; // for fast checking if leftToFind needs resetting + while ( position < max ) + { + position += toRead; + if ( position > max ) + { + int diff = (int) ( position - max ); + toRead -= diff; + offset = diff; + position = max; + } + Seek(position, seekFromEnd); + int actuallyRead = m_pFile->Read((char*)buffer + offset, toRead); + if (actuallyRead != toRead) + ThrowError(CZipException::badZipFile); + int pos = m_iLocateBufferSize - 1; + while ( pos >= offset ) + { + if ( buffer[pos] == szSignature[leftToFind] ) + { + if ( leftToFind == 0 ) + return (ZIP_FILE_USIZE)(uFileLength - ( position - ( pos - offset ) )); + if ( !found ) + found = true; + leftToFind--; + pos--; + } + else if ( found ) + { + leftToFind = recordSize - 1; + found = false; + // do not decrease position, the current pos may be the first to find + } + else + pos--; + } + } + return SignatureNotFound; +} + +ULONGLONG CZipStorage::Seek(ULONGLONG lOff, SeekType iSeekType) +{ + if (iSeekType == seekCurrent) + { + ZIP_SIZE_TYPE uPosition = (ZIP_SIZE_TYPE)m_pFile->GetPosition(); + if (IsSegmented() == -1) + { + ZIP_FILE_USIZE uLength = m_pFile->GetLength(); + while (uPosition + lOff >= uLength) + { + ZIP_SIZE_TYPE uCanSeek = (ZIP_SIZE_TYPE)(uLength - uPosition); + lOff -= uCanSeek; + ChangeVolume(); + uPosition = 0; + uLength = m_pFile->GetLength(); + } + return lOff > 0 ? m_pFile->Seek((ZIP_FILE_USIZE)lOff) : 0; + } + else + return m_pFile->Seek((ZIP_FILE_SIZE)lOff, CZipAbstractFile::current); + } + else + { + if (m_uCurrentVolume == 0 && m_uBytesBeforeZip > 0) + lOff += m_uBytesBeforeZip; + return m_pFile->Seek((ZIP_FILE_USIZE)lOff, iSeekType == seekFromBeginning); + } +} + +void CZipStorage::FinalizeSegm() +{ + ASSERT(IsSegmented() == 1); // spanned archive in creation + ASSERT(!m_bInMemory); + + CZipString szFileName; + if (IsSplit() && m_bNewSegm) + szFileName = RenameLastFileInSplitArchive(); + else + { + szFileName = m_pFile->GetFilePath(); + // the file is already closed + m_pFile->Close(); + } + m_bNewSegm = false; + if (m_uCurrentVolume == 0) // one-volume segmented archive was converted to normal archive + m_iSegmMode = noSegments; + else + m_uSplitData = m_uCurrentVolume; + + OpenFile(szFileName, CZipFile::modeNoTruncate | (m_iSegmMode == noSegments ? CZipFile::modeReadWrite : CZipFile::modeRead)); +} + -- cgit v1.2.3