Bug 959047 - Part 1: Align stored files by alignStoredFiles(). r=aklotz

This commit is contained in:
Shian-Yow Wu 2014-01-21 15:10:33 +08:00
parent 2a30d76183
commit 5a452310df
4 changed files with 191 additions and 6 deletions

View File

@ -36,7 +36,7 @@ interface nsIZipEntry;
* entries within them, however it is possible that some zip programs may
* experience problems what that.
*/
[scriptable, uuid(6d4ef074-206c-4649-9884-57bc355864d6)]
[scriptable, uuid(3ca10750-797e-4a22-bcfe-66170b5e96dd)]
interface nsIZipWriter : nsISupports
{
/**
@ -206,4 +206,15 @@ interface nsIZipWriter : nsISupports
* @throws <other-error> on failure to complete the zip file
*/
void close();
/**
* Make all stored(uncompressed) files align to given alignment size.
*
* @param aAlignSize is the alignment size, valid values from 2 to 32768, and
must be power of 2.
*
* @throws NS_ERROR_INVALID_ARG if aAlignSize is invalid
* @throws <other-error> on failure to update the zip file
*/
void alignStoredFiles(in uint16_t aAlignSize);
};

View File

@ -342,3 +342,54 @@ const uint8_t * nsZipHeader::GetExtraField(uint16_t aTag, bool aLocal, uint16_t
return nullptr;
}
/*
* Pad extra field to align data starting position to specified size.
*/
nsresult nsZipHeader::PadExtraField(uint32_t aOffset, uint16_t aAlignSize)
{
uint32_t pad_size;
uint32_t pa_offset;
uint32_t pa_end;
// Check for range and power of 2.
if (aAlignSize < 2 || aAlignSize > 32768 ||
(aAlignSize & (aAlignSize - 1)) != 0) {
return NS_ERROR_INVALID_ARG;
}
// Point to current starting data position.
aOffset += ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
// Calculate aligned offset.
pa_offset = aOffset & ~(aAlignSize - 1);
pa_end = pa_offset + aAlignSize;
pad_size = pa_end - aOffset;
if (pad_size == 0) {
return NS_OK;
}
// Leave enough room(at least 4 bytes) for valid values in extra field.
while (pad_size < 4) {
pad_size += aAlignSize;
}
// Extra field length is 2 bytes.
if (mLocalFieldLength + pad_size > 65535) {
return NS_ERROR_FAILURE;
}
nsAutoArrayPtr<uint8_t> field = mLocalExtraField;
uint32_t pos = mLocalFieldLength;
mLocalExtraField = new uint8_t[mLocalFieldLength + pad_size];
memcpy(mLocalExtraField.get(), field, mLocalFieldLength);
// Use 0xFFFF as tag ID to avoid conflict with other IDs.
// For more information, please read "Extensible data fields" section in:
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
WRITE16(mLocalExtraField.get(), &pos, 0xFFFF);
WRITE16(mLocalExtraField.get(), &pos, pad_size - 4);
memset(mLocalExtraField.get() + pos, 0, pad_size - 4);
mLocalFieldLength += pad_size;
return NS_OK;
}

View File

@ -88,6 +88,7 @@ public:
nsresult WriteCDSHeader(nsIOutputStream *aStream);
nsresult ReadCDSHeader(nsIInputStream *aStream);
const uint8_t * GetExtraField(uint16_t aTag, bool aLocal, uint16_t *aBlockSize);
nsresult PadExtraField(uint32_t aOffset, uint16_t aAlignSize);
};
#endif

View File

@ -3,8 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "StreamFunctions.h"
#include "nsZipWriter.h"
#include <algorithm>
#include "StreamFunctions.h"
#include "nsZipDataStream.h"
#include "nsISeekableStream.h"
#include "nsIAsyncStreamCopier.h"
@ -551,10 +554,7 @@ NS_IMETHODIMP nsZipWriter::RemoveEntry(const nsACString & aZipEntry,
uint32_t read = 0;
char buf[4096];
while (count > 0) {
if (count < sizeof(buf))
read = count;
else
read = sizeof(buf);
read = std::min(count, (uint32_t) sizeof(buf));
rv = inputStream->Read(buf, read, &read);
if (NS_FAILED(rv)) {
@ -737,6 +737,128 @@ NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest,
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;
}
nsresult nsZipWriter::InternalAddEntryDirectory(const nsACString & aZipEntry,
PRTime aModTime,
uint32_t aPermissions)