void CCentralDir::Write() { if (m_bOnDisk) return; if (!m_pStorage->IsSpanMode()) { m_pStorage->Flush(); m_pStorage->m_file.SeekToEnd(); } if (m_szComment.GetLength() > USHRT_MAX) m_szComment = m_szComment.Left(USHRT_MAX); m_uCommentSize = (WORD)m_szComment.GetLength(); m_uEntriesNumber = (WORD)m_headers.GetSize(); m_uSize = 0; bool bDontAllowDiskChange = false; // if there is a disk spanning archive in creation and it is only one-volume, // (current disk is 0 so far, no bytes has been written so we know they are // all in the buffer) make sure that it will be after writting central dir // and make it a non disk spanning archive if (m_pStorage->IsSpanMode() && !m_pStorage->GetCurrentDisk()) { DWORD uVolumeFree = m_pStorage->VolumeLeft(); // calculate the size of data descriptors already in the buffer or on the disk // (they will be removed in the non disk spanning archive): // multi span signature at the beginnig (4 bytes) + the size of the data // descr. for each file (multi span signature + 12 bytes data) // the count of bytes to add: central dir size - total to remove; DWORD uToGrow = GetSize(true) - (4 + m_uEntriesNumber * (4 + 12)); if (uVolumeFree >= uToGrow) // lets make sure it will be one-disk archive { // can the operation be done only in the buffer? if (!m_pStorage->m_iBytesWritten && // no bytes on the disk yet (m_pStorage->GetFreeInBuffer() >= uToGrow)) // is the buffer big enough? { RemoveDataDescr(true); bDontAllowDiskChange = true; // if the disk change occurs somehow, we'll throw an error later } else { m_pStorage->Flush(); m_pStorage->m_file.Flush(); if (RemoveDataDescr(false)) bDontAllowDiskChange = true; // if the disk change occurs somehow, we'll throw an error later } } } WriteHeaders(); m_uThisDisk = (WORD)m_pStorage->GetCurrentDisk(); DWORD uSize = WriteCentralEnd(); if (bDontAllowDiskChange && (m_pStorage->GetCurrentDisk() != 0)) ThrowError(ZIP_BADZIPFILE); // if after adding a central directory there is a disk change, // update the information and write it again if (m_uThisDisk != m_pStorage->GetCurrentDisk()) { m_uThisDisk = (WORD)m_pStorage->GetCurrentDisk(); if (m_uEntriesNumber) { m_uDiskEntriesNo = 0; } else { m_uDiskWithCD = m_uThisDisk; m_uOffset = 0; } if (m_pStorage->m_uBytesInWriteBuffer >= uSize) // if the data is still in the buffer, simply remove it m_pStorage->m_uBytesInWriteBuffer -= uSize; else { m_pStorage->Flush(); m_pStorage->m_iBytesWritten -= uSize; m_pStorage->m_file.SeekToBegin(); } WriteCentralEnd(); } }
void CZipCentralDir::Write(CZipActionCallback* pCallback) { if (m_info.m_bOnDisk) return; if (!m_pStorage->IsSpanMode()) { m_pStorage->Flush(); m_pStorage->m_pFile->SeekToEnd(); } // else // // we are at the end already m_info.m_uEntriesNumber = (WORD)m_headers.GetSize(); m_info.m_uSize = 0; bool bDontAllowDiskChange = false; // if there is a disk spanning archive in creation and it is only one-volume, // (current disk is 0 so far, no bytes has been written so we know they are // all in the buffer) make sure that it will be after writing central dir // and make it a non disk spanning archive if (m_pStorage->IsSpanMode() && m_pStorage->GetCurrentDisk() == 0) { DWORD uVolumeFree = m_pStorage->VolumeLeft(); // calculate the size of data descriptors already in the buffer or on the disk // (they will be removed in the non disk spanning archive): // multi span signature at the beginnig (4 bytes) + the size of the data // descr. for each file (multi span signature + 12 bytes data) // the number of bytes to add: central dir size - total to remove; DWORD uToGrow = GetSize(true) - (4 + m_info.m_uEntriesNumber * (4 + 12)); if (uVolumeFree >= uToGrow) // lets make sure it will be one-disk archive { // can the operation be done only in the buffer? if (!m_pStorage->m_iBytesWritten && // no bytes on the disk yet (m_pStorage->GetFreeInBuffer() >= uToGrow)) // is the buffer big enough? { RemoveDataDescr(true); bDontAllowDiskChange = true; // if the disk change occurs somehow, we'll throw an error later } else { m_pStorage->Flush(); if (RemoveDataDescr(false)) bDontAllowDiskChange = true; // if the disk change occurs somehow, we'll throw an error later } } } try { WriteHeaders(pCallback, bDontAllowDiskChange || !m_pStorage->IsSpanMode()); m_info.m_uThisDisk = (WORD)m_pStorage->GetCurrentDisk(); DWORD uSize = WriteCentralEnd(); if (bDontAllowDiskChange) { if (m_pStorage->GetCurrentDisk() != 0) ThrowError(CZipException::badZipFile); } // if after adding a central directory there is a disk change, // update the information and write it again if (m_info.m_uThisDisk != m_pStorage->GetCurrentDisk()) { m_info.DiskChange(m_pStorage->GetCurrentDisk()); if (m_pStorage->m_uBytesInWriteBuffer >= uSize) // if the data is still in the buffer, simply remove it m_pStorage->m_uBytesInWriteBuffer -= uSize; else { m_pStorage->Flush(); m_pStorage->m_iBytesWritten -= uSize; m_pStorage->m_pFile->SeekToBegin(); } WriteCentralEnd(); } } catch (...) { if (bDontAllowDiskChange) { m_pStorage->FinalizeSpan(); m_info.m_uThisDisk = 0; } throw; } m_info.m_bOnDisk = true; }