mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 18:04:46 +00:00
Bug 1875218 - Remove Zip. r=gsvelto
Now that both MappableExtractFile and MappableDeflate have been removed, nothing is using the Zip reader code anymore. Differential Revision: https://phabricator.services.mozilla.com/D198913
This commit is contained in:
parent
106d2531c0
commit
8b1d9774e0
@ -1,277 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include "Logging.h"
|
||||
#include "Zip.h"
|
||||
|
||||
already_AddRefed<Zip> Zip::Create(const char* filename) {
|
||||
/* Open and map the file in memory */
|
||||
AutoCloseFD fd(open(filename, O_RDONLY));
|
||||
if (fd == -1) {
|
||||
ERROR("Error opening %s: %s", filename, strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1) {
|
||||
ERROR("Error stating %s: %s", filename, strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
size_t size = st.st_size;
|
||||
if (size <= sizeof(CentralDirectoryEnd)) {
|
||||
ERROR("Error reading %s: too short", filename);
|
||||
return nullptr;
|
||||
}
|
||||
void* mapped = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (mapped == MAP_FAILED) {
|
||||
ERROR("Error mmapping %s: %s", filename, strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
DEBUG_LOG("Mapped %s @%p", filename, mapped);
|
||||
|
||||
return Create(filename, mapped, size);
|
||||
}
|
||||
|
||||
already_AddRefed<Zip> Zip::Create(const char* filename, void* mapped,
|
||||
size_t size) {
|
||||
RefPtr<Zip> zip = new Zip(filename, mapped, size);
|
||||
|
||||
// If neither the first Local File entry nor central directory entries
|
||||
// have been found, the zip was invalid.
|
||||
if (!zip->nextFile && !zip->entries) {
|
||||
ERROR("%s - Invalid zip", filename);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ZipCollection::Singleton.Register(zip);
|
||||
return zip.forget();
|
||||
}
|
||||
|
||||
Zip::Zip(const char* filename, void* mapped, size_t size)
|
||||
: name(filename ? strdup(filename) : nullptr),
|
||||
mapped(mapped),
|
||||
size(size),
|
||||
nextFile(LocalFile::validate(mapped)) // first Local File entry
|
||||
,
|
||||
nextDir(nullptr),
|
||||
entries(nullptr) {
|
||||
pthread_mutex_init(&mutex, nullptr);
|
||||
// If the first local file entry couldn't be found (which can happen
|
||||
// with optimized jars), check the first central directory entry.
|
||||
if (!nextFile) GetFirstEntry();
|
||||
}
|
||||
|
||||
Zip::~Zip() {
|
||||
if (name) {
|
||||
munmap(mapped, size);
|
||||
DEBUG_LOG("Unmapped %s @%p", name, mapped);
|
||||
free(name);
|
||||
}
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
bool Zip::GetStream(const char* path, Zip::Stream* out) const {
|
||||
AutoLock lock(&mutex);
|
||||
|
||||
DEBUG_LOG("%s - GetFile %s", name, path);
|
||||
/* Fast path: if the Local File header on store matches, we can return the
|
||||
* corresponding stream right away.
|
||||
* However, the Local File header may not contain enough information, in
|
||||
* which case the 3rd bit on the generalFlag is set. Unfortunately, this
|
||||
* bit is also set in some archives even when we do have the data (most
|
||||
* notably the android packages as built by the Mozilla build system).
|
||||
* So instead of testing the generalFlag bit, only use the fast path when
|
||||
* we haven't read the central directory entries yet, and when the
|
||||
* compressed size as defined in the header is not filled (which is a
|
||||
* normal condition for the bit to be set). */
|
||||
if (nextFile && nextFile->GetName().Equals(path) && !entries &&
|
||||
(nextFile->compressedSize != 0)) {
|
||||
DEBUG_LOG("%s - %s was next file: fast path", name, path);
|
||||
/* Fill Stream info from Local File header content */
|
||||
const char* data = reinterpret_cast<const char*>(nextFile->GetData());
|
||||
out->compressedBuf = data;
|
||||
out->compressedSize = nextFile->compressedSize;
|
||||
out->uncompressedSize = nextFile->uncompressedSize;
|
||||
out->CRC32 = nextFile->CRC32;
|
||||
out->type = static_cast<Stream::Type>(uint16_t(nextFile->compression));
|
||||
|
||||
/* Find the next Local File header. It is usually simply following the
|
||||
* compressed stream, but in cases where the 3rd bit of the generalFlag
|
||||
* is set, there is a Data Descriptor header before. */
|
||||
data += nextFile->compressedSize;
|
||||
if ((nextFile->generalFlag & 0x8) && DataDescriptor::validate(data)) {
|
||||
data += sizeof(DataDescriptor);
|
||||
}
|
||||
nextFile = LocalFile::validate(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the directory entry we have in store doesn't match, scan the Central
|
||||
* Directory for the entry corresponding to the given path */
|
||||
if (!nextDir || !nextDir->GetName().Equals(path)) {
|
||||
const DirectoryEntry* entry = GetFirstEntry();
|
||||
DEBUG_LOG("%s - Scan directory entries in search for %s", name, path);
|
||||
while (entry && !entry->GetName().Equals(path)) {
|
||||
entry = entry->GetNext();
|
||||
}
|
||||
nextDir = entry;
|
||||
}
|
||||
if (!nextDir) {
|
||||
DEBUG_LOG("%s - Couldn't find %s", name, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find the Local File header corresponding to the Directory entry that
|
||||
* was found. */
|
||||
nextFile =
|
||||
LocalFile::validate(static_cast<const char*>(mapped) + nextDir->offset);
|
||||
if (!nextFile) {
|
||||
ERROR("%s - Couldn't find the Local File header for %s", name, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fill Stream info from Directory entry content */
|
||||
const char* data = reinterpret_cast<const char*>(nextFile->GetData());
|
||||
out->compressedBuf = data;
|
||||
out->compressedSize = nextDir->compressedSize;
|
||||
out->uncompressedSize = nextDir->uncompressedSize;
|
||||
out->CRC32 = nextDir->CRC32;
|
||||
out->type = static_cast<Stream::Type>(uint16_t(nextDir->compression));
|
||||
|
||||
/* Store the next directory entry */
|
||||
nextDir = nextDir->GetNext();
|
||||
nextFile = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Zip::DirectoryEntry* Zip::GetFirstEntry() const {
|
||||
if (entries) return entries;
|
||||
|
||||
const CentralDirectoryEnd* end = nullptr;
|
||||
const char* _end =
|
||||
static_cast<const char*>(mapped) + size - sizeof(CentralDirectoryEnd);
|
||||
|
||||
/* Scan for the Central Directory End */
|
||||
for (; _end > mapped && !end; _end--)
|
||||
end = CentralDirectoryEnd::validate(_end);
|
||||
if (!end) {
|
||||
ERROR("%s - Couldn't find end of central directory record", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
entries =
|
||||
DirectoryEntry::validate(static_cast<const char*>(mapped) + end->offset);
|
||||
if (!entries) {
|
||||
ERROR("%s - Couldn't find central directory record", name);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
bool Zip::VerifyCRCs() const {
|
||||
AutoLock lock(&mutex);
|
||||
|
||||
for (const DirectoryEntry* entry = GetFirstEntry(); entry;
|
||||
entry = entry->GetNext()) {
|
||||
const LocalFile* file =
|
||||
LocalFile::validate(static_cast<const char*>(mapped) + entry->offset);
|
||||
uint32_t crc = crc32(0, nullptr, 0);
|
||||
|
||||
DEBUG_LOG("%.*s: crc=%08x", int(entry->filenameSize),
|
||||
reinterpret_cast<const char*>(entry) + sizeof(*entry),
|
||||
uint32_t(entry->CRC32));
|
||||
|
||||
if (entry->compression == Stream::Type::STORE) {
|
||||
crc = crc32(crc, static_cast<const uint8_t*>(file->GetData()),
|
||||
entry->compressedSize);
|
||||
DEBUG_LOG(" STORE size=%d crc=%08x", int(entry->compressedSize), crc);
|
||||
|
||||
} else if (entry->compression == Stream::Type::DEFLATE) {
|
||||
z_stream zstream;
|
||||
Bytef buffer[1024];
|
||||
zstream.avail_in = entry->compressedSize;
|
||||
zstream.next_in =
|
||||
reinterpret_cast<Bytef*>(const_cast<void*>(file->GetData()));
|
||||
zstream.zalloc = nullptr;
|
||||
zstream.zfree = nullptr;
|
||||
zstream.opaque = nullptr;
|
||||
|
||||
if (inflateInit2(&zstream, -MAX_WBITS) != Z_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
zstream.avail_out = sizeof(buffer);
|
||||
zstream.next_out = buffer;
|
||||
|
||||
int ret = inflate(&zstream, Z_SYNC_FLUSH);
|
||||
crc = crc32(crc, buffer, sizeof(buffer) - zstream.avail_out);
|
||||
|
||||
if (ret == Z_STREAM_END) {
|
||||
break;
|
||||
} else if (ret != Z_OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inflateEnd(&zstream);
|
||||
DEBUG_LOG(" DEFLATE size=%d crc=%08x", int(zstream.total_out), crc);
|
||||
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected stream type");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry->CRC32 != crc) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ZipCollection ZipCollection::Singleton;
|
||||
|
||||
static pthread_mutex_t sZipCollectionMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
already_AddRefed<Zip> ZipCollection::GetZip(const char* path) {
|
||||
{
|
||||
AutoLock lock(&sZipCollectionMutex);
|
||||
/* Search the list of Zips we already have for a match */
|
||||
for (const auto& zip : Singleton.zips) {
|
||||
if (zip->GetName() && (strcmp(zip->GetName(), path) == 0)) {
|
||||
return RefPtr<Zip>(zip).forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Zip::Create(path);
|
||||
}
|
||||
|
||||
void ZipCollection::Register(Zip* zip) {
|
||||
AutoLock lock(&sZipCollectionMutex);
|
||||
DEBUG_LOG("ZipCollection::Register(\"%s\")", zip->GetName());
|
||||
Singleton.zips.push_back(zip);
|
||||
}
|
||||
|
||||
void ZipCollection::Forget(const Zip* zip) {
|
||||
AutoLock lock(&sZipCollectionMutex);
|
||||
if (zip->refCount() > 1) {
|
||||
// Someone has acquired a reference before we had acquired the lock,
|
||||
// ignore this request.
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG("ZipCollection::Forget(\"%s\")", zip->GetName());
|
||||
const auto it = std::find(Singleton.zips.begin(), Singleton.zips.end(), zip);
|
||||
if (*it == zip) {
|
||||
Singleton.zips.erase(it);
|
||||
} else {
|
||||
DEBUG_LOG("ZipCollection::Forget: didn't find \"%s\" in bookkeeping",
|
||||
zip->GetName());
|
||||
}
|
||||
}
|
@ -1,388 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef Zip_h
|
||||
#define Zip_h
|
||||
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
#include <pthread.h>
|
||||
#include "Utils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
/**
|
||||
* Forward declaration
|
||||
*/
|
||||
class ZipCollection;
|
||||
|
||||
/**
|
||||
* Class to handle access to Zip archive streams. The Zip archive is mapped
|
||||
* in memory, and streams are direct references to that mapped memory.
|
||||
* Zip files are assumed to be correctly formed. No boundary checks are
|
||||
* performed, which means hand-crafted malicious Zip archives can make the
|
||||
* code fail in bad ways. However, since the only intended use is to load
|
||||
* libraries from Zip archives, there is no interest in making this code
|
||||
* safe, since the libraries could contain malicious code anyways.
|
||||
*/
|
||||
class Zip : public mozilla::external::AtomicRefCounted<Zip> {
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(Zip)
|
||||
/**
|
||||
* Create a Zip instance for the given file name. Returns nullptr in case
|
||||
* of failure.
|
||||
*/
|
||||
static already_AddRefed<Zip> Create(const char* filename);
|
||||
|
||||
/**
|
||||
* Create a Zip instance using the given buffer.
|
||||
*/
|
||||
static already_AddRefed<Zip> Create(void* buffer, size_t size) {
|
||||
return Create(nullptr, buffer, size);
|
||||
}
|
||||
|
||||
private:
|
||||
static already_AddRefed<Zip> Create(const char* filename, void* buffer,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* Private constructor
|
||||
*/
|
||||
Zip(const char* filename, void* buffer, size_t size);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Zip();
|
||||
|
||||
/**
|
||||
* Class used to access Zip archive item streams
|
||||
*/
|
||||
class Stream {
|
||||
public:
|
||||
/**
|
||||
* Stream types
|
||||
*/
|
||||
enum Type { STORE = 0, DEFLATE = 8 };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Stream()
|
||||
: compressedBuf(nullptr),
|
||||
compressedSize(0),
|
||||
uncompressedSize(0),
|
||||
CRC32(0),
|
||||
type(STORE) {}
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
const void* GetBuffer() { return compressedBuf; }
|
||||
size_t GetSize() { return compressedSize; }
|
||||
size_t GetUncompressedSize() { return uncompressedSize; }
|
||||
size_t GetCRC32() { return CRC32; }
|
||||
Type GetType() { return type; }
|
||||
|
||||
/**
|
||||
* Returns a z_stream for use with inflate functions using the given
|
||||
* buffer as inflate output. The caller is expected to allocate enough
|
||||
* memory for the Stream uncompressed size.
|
||||
*/
|
||||
z_stream GetZStream(void* buf) {
|
||||
z_stream zStream;
|
||||
zStream.avail_in = compressedSize;
|
||||
zStream.next_in =
|
||||
reinterpret_cast<Bytef*>(const_cast<void*>(compressedBuf));
|
||||
zStream.avail_out = uncompressedSize;
|
||||
zStream.next_out = static_cast<Bytef*>(buf);
|
||||
zStream.zalloc = nullptr;
|
||||
zStream.zfree = nullptr;
|
||||
zStream.opaque = nullptr;
|
||||
return zStream;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class Zip;
|
||||
const void* compressedBuf;
|
||||
size_t compressedSize;
|
||||
size_t uncompressedSize;
|
||||
size_t CRC32;
|
||||
Type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a stream from the Zip archive.
|
||||
*/
|
||||
bool GetStream(const char* path, Stream* out) const;
|
||||
|
||||
/**
|
||||
* Returns the file name of the archive
|
||||
*/
|
||||
const char* GetName() const { return name; }
|
||||
|
||||
/**
|
||||
* Returns whether all files have correct CRC checksum.
|
||||
*/
|
||||
bool VerifyCRCs() const;
|
||||
|
||||
private:
|
||||
/* File name of the archive */
|
||||
char* name;
|
||||
/* Address where the Zip archive is mapped */
|
||||
void* mapped;
|
||||
/* Size of the archive */
|
||||
size_t size;
|
||||
|
||||
/**
|
||||
* Strings (file names, comments, etc.) in the Zip headers are NOT zero
|
||||
* terminated. This class is a helper around them.
|
||||
*/
|
||||
class StringBuf {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
StringBuf(const char* buf, size_t length) : buf(buf), length(length) {}
|
||||
|
||||
/**
|
||||
* Returns whether the string has the same content as the given zero
|
||||
* terminated string.
|
||||
*/
|
||||
bool Equals(const char* str) const {
|
||||
return (strncmp(str, buf, length) == 0 && str[length] == '\0');
|
||||
}
|
||||
|
||||
private:
|
||||
const char* buf;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
/* All the following types need to be packed */
|
||||
#pragma pack(1)
|
||||
public:
|
||||
/**
|
||||
* A Zip archive is an aggregate of entities which all start with a
|
||||
* signature giving their type. This template is to be used as a base
|
||||
* class for these entities.
|
||||
*/
|
||||
template <typename T>
|
||||
class SignedEntity {
|
||||
public:
|
||||
/**
|
||||
* Equivalent to reinterpret_cast<const T *>(buf), with an additional
|
||||
* check of the signature.
|
||||
*/
|
||||
static const T* validate(const void* buf) {
|
||||
const T* ret = static_cast<const T*>(buf);
|
||||
if (ret->signature == T::magic) return ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
explicit SignedEntity(uint32_t magic) : signature(magic) {}
|
||||
|
||||
private:
|
||||
le_uint32 signature;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* Header used to describe a Local File entry. The header is followed by
|
||||
* the file name and an extra field, then by the data stream.
|
||||
*/
|
||||
struct LocalFile : public SignedEntity<LocalFile> {
|
||||
/* Signature for a Local File header */
|
||||
static const uint32_t magic = 0x04034b50;
|
||||
|
||||
/**
|
||||
* Returns the file name
|
||||
*/
|
||||
StringBuf GetName() const {
|
||||
return StringBuf(reinterpret_cast<const char*>(this) + sizeof(*this),
|
||||
filenameSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the data associated with this header
|
||||
*/
|
||||
const void* GetData() const {
|
||||
return reinterpret_cast<const char*>(this) + sizeof(*this) +
|
||||
filenameSize + extraFieldSize;
|
||||
}
|
||||
|
||||
le_uint16 minVersion;
|
||||
le_uint16 generalFlag;
|
||||
le_uint16 compression;
|
||||
le_uint16 lastModifiedTime;
|
||||
le_uint16 lastModifiedDate;
|
||||
le_uint32 CRC32;
|
||||
le_uint32 compressedSize;
|
||||
le_uint32 uncompressedSize;
|
||||
le_uint16 filenameSize;
|
||||
le_uint16 extraFieldSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* In some cases, when a zip archive is created, compressed size and CRC
|
||||
* are not known when writing the Local File header. In these cases, the
|
||||
* 3rd bit of the general flag in the Local File header is set, and there
|
||||
* is an additional header following the compressed data.
|
||||
*/
|
||||
struct DataDescriptor : public SignedEntity<DataDescriptor> {
|
||||
/* Signature for a Data Descriptor header */
|
||||
static const uint32_t magic = 0x08074b50;
|
||||
|
||||
le_uint32 CRC32;
|
||||
le_uint32 compressedSize;
|
||||
le_uint32 uncompressedSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Header used to describe a Central Directory Entry. The header is
|
||||
* followed by the file name, an extra field, and a comment.
|
||||
*/
|
||||
struct DirectoryEntry : public SignedEntity<DirectoryEntry> {
|
||||
/* Signature for a Central Directory Entry header */
|
||||
static const uint32_t magic = 0x02014b50;
|
||||
|
||||
/**
|
||||
* Returns the file name
|
||||
*/
|
||||
StringBuf GetName() const {
|
||||
return StringBuf(reinterpret_cast<const char*>(this) + sizeof(*this),
|
||||
filenameSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Central Directory Entry following this one.
|
||||
*/
|
||||
const DirectoryEntry* GetNext() const {
|
||||
return validate(reinterpret_cast<const char*>(this) + sizeof(*this) +
|
||||
filenameSize + extraFieldSize + fileCommentSize);
|
||||
}
|
||||
|
||||
le_uint16 creatorVersion;
|
||||
le_uint16 minVersion;
|
||||
le_uint16 generalFlag;
|
||||
le_uint16 compression;
|
||||
le_uint16 lastModifiedTime;
|
||||
le_uint16 lastModifiedDate;
|
||||
le_uint32 CRC32;
|
||||
le_uint32 compressedSize;
|
||||
le_uint32 uncompressedSize;
|
||||
le_uint16 filenameSize;
|
||||
le_uint16 extraFieldSize;
|
||||
le_uint16 fileCommentSize;
|
||||
le_uint16 diskNum;
|
||||
le_uint16 internalAttributes;
|
||||
le_uint32 externalAttributes;
|
||||
le_uint32 offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Header used to describe the End of Central Directory Record.
|
||||
*/
|
||||
struct CentralDirectoryEnd : public SignedEntity<CentralDirectoryEnd> {
|
||||
/* Signature for the End of Central Directory Record */
|
||||
static const uint32_t magic = 0x06054b50;
|
||||
|
||||
le_uint16 diskNum;
|
||||
le_uint16 startDisk;
|
||||
le_uint16 recordsOnDisk;
|
||||
le_uint16 records;
|
||||
le_uint32 size;
|
||||
le_uint32 offset;
|
||||
le_uint16 commentSize;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
/**
|
||||
* Returns the first Directory entry
|
||||
*/
|
||||
const DirectoryEntry* GetFirstEntry() const;
|
||||
|
||||
/* Pointer to the Local File Entry following the last one GetStream() used.
|
||||
* This is used by GetStream to avoid scanning the Directory Entries when the
|
||||
* requested entry is that one. */
|
||||
mutable const LocalFile* nextFile;
|
||||
|
||||
/* Likewise for the next Directory entry */
|
||||
mutable const DirectoryEntry* nextDir;
|
||||
|
||||
/* Pointer to the Directory entries */
|
||||
mutable const DirectoryEntry* entries;
|
||||
|
||||
mutable pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for bookkeeping Zip instances
|
||||
*/
|
||||
class ZipCollection {
|
||||
public:
|
||||
static ZipCollection Singleton;
|
||||
|
||||
/**
|
||||
* Get a Zip instance for the given path. If there is an existing one
|
||||
* already, return that one, otherwise create a new one.
|
||||
*/
|
||||
static already_AddRefed<Zip> GetZip(const char* path);
|
||||
|
||||
protected:
|
||||
friend class Zip;
|
||||
friend class mozilla::detail::RefCounted<Zip,
|
||||
mozilla::detail::AtomicRefCount>;
|
||||
|
||||
/**
|
||||
* Register the given Zip instance. This method is meant to be called
|
||||
* by Zip::Create.
|
||||
*/
|
||||
static void Register(Zip* zip);
|
||||
|
||||
/**
|
||||
* Forget about the given Zip instance. This method is meant to be called
|
||||
* by the Zip destructor.
|
||||
*/
|
||||
static void Forget(const Zip* zip);
|
||||
|
||||
private:
|
||||
/* Zip instances bookkept in this collection */
|
||||
std::vector<RefPtr<Zip>> zips;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
template <>
|
||||
inline void RefCounted<Zip, AtomicRefCount>::Release() const {
|
||||
MOZ_ASSERT(static_cast<int32_t>(mRefCnt) > 0);
|
||||
const auto count = --mRefCnt;
|
||||
if (count == 1) {
|
||||
// No external references are left, attempt to remove it from the
|
||||
// collection. If it's successfully removed from the collection, Release()
|
||||
// will be called with mRefCnt = 1, which will finally delete this zip.
|
||||
ZipCollection::Forget(static_cast<const Zip*>(this));
|
||||
} else if (count == 0) {
|
||||
#ifdef DEBUG
|
||||
mRefCnt = detail::DEAD;
|
||||
#endif
|
||||
delete static_cast<const Zip*>(this);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
template <>
|
||||
inline RefCounted<Zip, AtomicRefCount>::~RefCounted() {
|
||||
MOZ_ASSERT(mRefCnt == detail::DEAD);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* Zip_h */
|
@ -10,11 +10,8 @@ SOURCES += [
|
||||
"ElfLoader.cpp",
|
||||
"Logging.cpp",
|
||||
"Mappable.cpp",
|
||||
"Zip.cpp",
|
||||
]
|
||||
|
||||
Library("linker")
|
||||
|
||||
FINAL_LIBRARY = "mozglue"
|
||||
|
||||
TEST_DIRS += ["tests"]
|
||||
|
@ -1,61 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include "Zip.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
Logging Logging::Singleton;
|
||||
|
||||
/**
|
||||
* test.zip is a basic test zip file with a central directory. It contains
|
||||
* four entries, in the following order:
|
||||
* "foo", "bar", "baz", "qux".
|
||||
* The entries are going to be read out of order.
|
||||
*/
|
||||
extern const unsigned char TEST_ZIP[];
|
||||
extern const unsigned int TEST_ZIP_SIZE;
|
||||
const char* test_entries[] = {"baz", "foo", "bar", "qux"};
|
||||
|
||||
/**
|
||||
* no_central_dir.zip is a hand crafted test zip with no central directory
|
||||
* entries. The Zip reader is expected to be able to traverse these entries
|
||||
* if requested in order, without reading a central directory
|
||||
* - First entry is a file "a", STOREd.
|
||||
* - Second entry is a file "b", STOREd, using a data descriptor. CRC is
|
||||
* unknown, but compressed and uncompressed sizes are known in the local
|
||||
* file header.
|
||||
* - Third entry is a file "c", DEFLATEd, using a data descriptor. CRC,
|
||||
* compressed and uncompressed sizes are known in the local file header.
|
||||
* This is the kind of entry that can be found in a zip that went through
|
||||
* zipalign if it had a data descriptor originally.
|
||||
* - Fourth entry is a file "d", STOREd.
|
||||
*/
|
||||
extern const unsigned char NO_CENTRAL_DIR_ZIP[];
|
||||
extern const unsigned int NO_CENTRAL_DIR_ZIP_SIZE;
|
||||
const char* no_central_dir_entries[] = {"a", "b", "c", "d"};
|
||||
|
||||
TEST(Zip, TestZip)
|
||||
{
|
||||
Zip::Stream s;
|
||||
RefPtr<Zip> z = Zip::Create((void*)TEST_ZIP, TEST_ZIP_SIZE);
|
||||
for (auto& entry : test_entries) {
|
||||
ASSERT_TRUE(z->GetStream(entry, &s))
|
||||
<< "Could not get entry \"" << entry << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Zip, NoCentralDir)
|
||||
{
|
||||
Zip::Stream s;
|
||||
RefPtr<Zip> z =
|
||||
Zip::Create((void*)NO_CENTRAL_DIR_ZIP, NO_CENTRAL_DIR_ZIP_SIZE);
|
||||
for (auto& entry : no_central_dir_entries) {
|
||||
ASSERT_TRUE(z->GetStream(entry, &s))
|
||||
<< "Could not get entry \"" << entry << "\"";
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
.macro zip_data name, path
|
||||
.global \name
|
||||
.data
|
||||
.balign 16
|
||||
\name:
|
||||
.incbin "\path"
|
||||
.L\name\()_END:
|
||||
.size \name, .L\name\()_END-\name
|
||||
.global \name\()_SIZE
|
||||
.data
|
||||
.balign 4
|
||||
\name\()_SIZE:
|
||||
.int .L\name\()_END-\name
|
||||
.endm
|
||||
|
||||
zip_data TEST_ZIP, "test.zip"
|
||||
zip_data NO_CENTRAL_DIR_ZIP, "no_central_dir.zip"
|
@ -1,20 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"../Zip.cpp",
|
||||
"TestZip.cpp",
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
"TestZipData.S",
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [".."]
|
||||
|
||||
ASFLAGS += ["-I", SRCDIR]
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user