mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 959047 - Part 1: Align stored files by alignStoredFiles(). r=aklotz
This commit is contained in:
parent
2a30d76183
commit
5a452310df
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user