/* * Called when writing a file to the zip is complete. */ nsresult nsZipWriter::EntryCompleteCallback(nsZipHeader* aHeader, nsresult aStatus) { if (NS_SUCCEEDED(aStatus)) { if (!mEntryHash.Put(aHeader->mName, mHeaders.Count())) { SeekCDS(); return NS_ERROR_OUT_OF_MEMORY; } if (!mHeaders.AppendObject(aHeader)) { mEntryHash.Remove(aHeader->mName); SeekCDS(); return NS_ERROR_OUT_OF_MEMORY; } mCDSDirty = PR_TRUE; mCDSOffset += aHeader->mCSize + aHeader->GetFileHeaderLength(); if (mInQueue) BeginProcessingNextItem(); return NS_OK; } nsresult rv = SeekCDS(); if (mInQueue) FinishQueue(aStatus); return rv; }
/* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime, * in int32_t aCompression, in nsIInputStream aStream, * in boolean aQueue, in unsigned long aPermissions); */ nsresult nsZipWriter::AddEntryStream(const nsACString & aZipEntry, PRTime aModTime, int32_t aCompression, nsIInputStream *aStream, bool aQueue, uint32_t aPermissions) { NS_ENSURE_ARG_POINTER(aStream); if (!mStream) return NS_ERROR_NOT_INITIALIZED; if (aQueue) { nsZipQueueItem item; item.mOperation = OPERATION_ADD; item.mZipEntry = aZipEntry; item.mModTime = aModTime; item.mCompression = aCompression; item.mPermissions = aPermissions; item.mStream = aStream; if (!mQueue.AppendElement(item)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } if (mInQueue) return NS_ERROR_IN_PROGRESS; if (mEntryHash.Get(aZipEntry, nullptr)) return NS_ERROR_FILE_ALREADY_EXISTS; nsRefPtr<nsZipHeader> header = new nsZipHeader(); NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); header->Init(aZipEntry, aModTime, ZIP_ATTRS(aPermissions, ZIP_ATTRS_FILE), mCDSOffset); nsresult rv = header->WriteFileHeader(mStream); if (NS_FAILED(rv)) { SeekCDS(); return rv; } nsRefPtr<nsZipDataStream> stream = new nsZipDataStream(); if (!stream) { SeekCDS(); return NS_ERROR_OUT_OF_MEMORY; } rv = stream->Init(this, mStream, header, aCompression); if (NS_FAILED(rv)) { SeekCDS(); return rv; } rv = stream->ReadStream(aStream); if (NS_FAILED(rv)) SeekCDS(); return rv; }
/* * Starts processing on the next item in the queue. */ void nsZipWriter::BeginProcessingNextItem() { while (!mQueue.IsEmpty()) { nsZipQueueItem next = mQueue[0]; mQueue.RemoveElementAt(0); if (next.mOperation == OPERATION_REMOVE) { int32_t pos = -1; if (mEntryHash.Get(next.mZipEntry, &pos)) { if (pos < mHeaders.Count() - 1) { nsresult rv = BeginProcessingRemoval(pos); if (NS_FAILED(rv)) FinishQueue(rv); return; } mCDSOffset = mHeaders[pos]->mOffset; nsresult rv = SeekCDS(); if (NS_FAILED(rv)) { FinishQueue(rv); return; } mEntryHash.Remove(mHeaders[pos]->mName); mHeaders.RemoveObjectAt(pos); } else { FinishQueue(NS_ERROR_FILE_NOT_FOUND); return; } } else if (next.mOperation == OPERATION_ADD) { if (mEntryHash.Get(next.mZipEntry, nullptr)) { FinishQueue(NS_ERROR_FILE_ALREADY_EXISTS); return; } bool complete = false; nsresult rv = BeginProcessingAddition(&next, &complete); if (NS_FAILED(rv)) { SeekCDS(); FinishQueue(rv); return; } if (!complete) return; } } FinishQueue(NS_OK); }
/* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext, * in nsresult aStatusCode); */ NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatusCode) { if (NS_FAILED(aStatusCode)) { FinishQueue(aStatusCode); Cleanup(); } nsresult rv = mStream->Flush(); if (NS_FAILED(rv)) { FinishQueue(rv); Cleanup(); return rv; } rv = SeekCDS(); if (NS_FAILED(rv)) { FinishQueue(rv); return rv; } BeginProcessingNextItem(); return NS_OK; }
NS_IMETHODIMP nsZipWriter::Open(nsIFile *aFile, int32_t aIoFlags) { if (mStream) return NS_ERROR_ALREADY_INITIALIZED; NS_ENSURE_ARG_POINTER(aFile); // Need to be able to write to the file if (aIoFlags & PR_RDONLY) return NS_ERROR_FAILURE; nsresult rv = aFile->Clone(getter_AddRefs(mFile)); NS_ENSURE_SUCCESS(rv, rv); bool exists; rv = mFile->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); if (!exists && !(aIoFlags & PR_CREATE_FILE)) return NS_ERROR_FILE_NOT_FOUND; if (exists && !(aIoFlags & (PR_TRUNCATE | PR_WRONLY))) { rv = ReadFile(mFile); NS_ENSURE_SUCCESS(rv, rv); mCDSDirty = false; } else { mCDSOffset = 0; mCDSDirty = true; mComment.Truncate(); } // Silently drop PR_APPEND aIoFlags &= 0xef; nsCOMPtr<nsIOutputStream> stream; rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), mFile, aIoFlags); if (NS_FAILED(rv)) { mHeaders.Clear(); mEntryHash.Clear(); return rv; } rv = NS_NewBufferedOutputStream(getter_AddRefs(mStream), stream, 64 * 1024); if (NS_FAILED(rv)) { stream->Close(); mHeaders.Clear(); mEntryHash.Clear(); return rv; } if (mCDSOffset > 0) { rv = SeekCDS(); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
/* * Make all stored(uncompressed) files align to given alignment size. */ NS_IMETHODIMP nsZipWriter::AlignStoredFiles(uint16_t aAlignSize) { nsresult rv; // Check for range and power of 2. if (aAlignSize < 2 || aAlignSize > 32768 || (aAlignSize & (aAlignSize - 1)) != 0) { return NS_ERROR_INVALID_ARG; } for (int i = 0; i < mHeaders.Count(); i++) { nsZipHeader *header = mHeaders[i]; // Check whether this entry is file and compression method is stored. bool isdir; rv = header->GetIsDirectory(&isdir); if (NS_FAILED(rv)) { return rv; } if (isdir || header->mMethod != 0) { continue; } // Pad extra field to align data starting position to specified size. uint32_t old_len = header->mLocalFieldLength; rv = header->PadExtraField(header->mOffset, aAlignSize); if (NS_FAILED(rv)) { continue; } // No padding means data already aligned. uint32_t shift = header->mLocalFieldLength - old_len; if (shift == 0) { continue; } // Flush any remaining data before we start. rv = mStream->Flush(); if (NS_FAILED(rv)) { return rv; } // Open zip file for reading. nsCOMPtr<nsIInputStream> inputStream; rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), mFile); if (NS_FAILED(rv)) { return rv; } nsCOMPtr<nsISeekableStream> in_seekable = do_QueryInterface(inputStream); nsCOMPtr<nsISeekableStream> out_seekable = do_QueryInterface(mStream); uint32_t data_offset = header->mOffset + header->GetFileHeaderLength() - shift; uint32_t count = mCDSOffset - data_offset; uint32_t read; char buf[4096]; // Shift data to aligned postion. while (count > 0) { read = std::min(count, (uint32_t) sizeof(buf)); rv = in_seekable->Seek(nsISeekableStream::NS_SEEK_SET, data_offset + count - read); if (NS_FAILED(rv)) { break; } rv = inputStream->Read(buf, read, &read); if (NS_FAILED(rv)) { break; } rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET, data_offset + count - read + shift); if (NS_FAILED(rv)) { break; } rv = ZW_WriteData(mStream, buf, read); if (NS_FAILED(rv)) { break; } count -= read; } inputStream->Close(); if (NS_FAILED(rv)) { Cleanup(); return rv; } // Update current header rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET, header->mOffset); if (NS_FAILED(rv)) { Cleanup(); return rv; } rv = header->WriteFileHeader(mStream); if (NS_FAILED(rv)) { Cleanup(); return rv; } // Update offset of all other headers int pos = i + 1; while (pos < mHeaders.Count()) { mHeaders[pos]->mOffset += shift; pos++; } mCDSOffset += shift; rv = SeekCDS(); if (NS_FAILED(rv)) { return rv; } mCDSDirty = true; } return NS_OK; }
NS_IMETHODIMP nsZipWriter::RemoveEntry(const nsACString & aZipEntry, bool aQueue) { if (!mStream) return NS_ERROR_NOT_INITIALIZED; if (aQueue) { nsZipQueueItem item; item.mOperation = OPERATION_REMOVE; item.mZipEntry = aZipEntry; if (!mQueue.AppendElement(item)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } if (mInQueue) return NS_ERROR_IN_PROGRESS; int32_t pos; if (mEntryHash.Get(aZipEntry, &pos)) { // Flush any remaining data before we seek. nsresult rv = mStream->Flush(); NS_ENSURE_SUCCESS(rv, rv); if (pos < mHeaders.Count() - 1) { // This is not the last entry, pull back the data. nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream); rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mHeaders[pos]->mOffset); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIInputStream> inputStream; rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), mFile); NS_ENSURE_SUCCESS(rv, rv); seekable = do_QueryInterface(inputStream); rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mHeaders[pos + 1]->mOffset); if (NS_FAILED(rv)) { inputStream->Close(); return rv; } uint32_t count = mCDSOffset - mHeaders[pos + 1]->mOffset; uint32_t read = 0; char buf[4096]; while (count > 0) { read = std::min(count, (uint32_t) sizeof(buf)); rv = inputStream->Read(buf, read, &read); if (NS_FAILED(rv)) { inputStream->Close(); Cleanup(); return rv; } rv = ZW_WriteData(mStream, buf, read); if (NS_FAILED(rv)) { inputStream->Close(); Cleanup(); return rv; } count -= read; } inputStream->Close(); // Rewrite header offsets and update hash uint32_t shift = (mHeaders[pos + 1]->mOffset - mHeaders[pos]->mOffset); mCDSOffset -= shift; int32_t pos2 = pos + 1; while (pos2 < mHeaders.Count()) { mEntryHash.Put(mHeaders[pos2]->mName, pos2-1); mHeaders[pos2]->mOffset -= shift; pos2++; } } else { // Remove the last entry is just a case of moving the CDS mCDSOffset = mHeaders[pos]->mOffset; rv = SeekCDS(); NS_ENSURE_SUCCESS(rv, rv); } mEntryHash.Remove(mHeaders[pos]->mName); mHeaders.RemoveObjectAt(pos); mCDSDirty = true; return NS_OK; } return NS_ERROR_FILE_NOT_FOUND; }