Merge "Allow loading zip at an offset in fd" am: ed22a0aa70
Change-Id: I38b81c3af281feadd4ce5faaaf75dc2483df02b1
This commit is contained in:
commit
bbd087221f
4 changed files with 158 additions and 16 deletions
|
@ -126,6 +126,9 @@ int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
|
|||
int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
|
||||
bool assume_ownership = true);
|
||||
|
||||
int32_t OpenArchiveFdRange(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
|
||||
off64_t length, off64_t offset, bool assume_ownership = true);
|
||||
|
||||
int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
|
||||
ZipArchiveHandle* handle);
|
||||
/*
|
||||
|
@ -222,6 +225,12 @@ int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begi
|
|||
|
||||
int GetFileDescriptor(const ZipArchiveHandle archive);
|
||||
|
||||
/**
|
||||
* Returns the offset of the zip archive in the backing file descriptor, or 0 if the zip archive is
|
||||
* not backed by a file descriptor.
|
||||
*/
|
||||
off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive);
|
||||
|
||||
const char* ErrorCodeString(int32_t error_code);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
|
|
@ -92,8 +92,8 @@ uint64_t GetOwnerTag(const ZipArchive* archive) {
|
|||
}
|
||||
#endif
|
||||
|
||||
ZipArchive::ZipArchive(const int fd, bool assume_ownership)
|
||||
: mapped_zip(fd),
|
||||
ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
|
||||
: mapped_zip(map),
|
||||
close_file(assume_ownership),
|
||||
directory_offset(0),
|
||||
central_directory(),
|
||||
|
@ -101,7 +101,8 @@ ZipArchive::ZipArchive(const int fd, bool assume_ownership)
|
|||
num_entries(0) {
|
||||
#if defined(__BIONIC__)
|
||||
if (assume_ownership) {
|
||||
android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
|
||||
CHECK(mapped_zip.HasFd());
|
||||
android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -362,14 +363,32 @@ static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_n
|
|||
|
||||
int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
|
||||
bool assume_ownership) {
|
||||
ZipArchive* archive = new ZipArchive(fd, assume_ownership);
|
||||
ZipArchive* archive = new ZipArchive(MappedZipFile(fd), assume_ownership);
|
||||
*handle = archive;
|
||||
return OpenArchiveInternal(archive, debug_file_name);
|
||||
}
|
||||
|
||||
int32_t OpenArchiveFdRange(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
|
||||
off64_t length, off64_t offset, bool assume_ownership) {
|
||||
ZipArchive* archive = new ZipArchive(MappedZipFile(fd, length, offset), assume_ownership);
|
||||
*handle = archive;
|
||||
|
||||
if (length < 0) {
|
||||
ALOGW("Invalid zip length %" PRId64, length);
|
||||
return kIoError;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
ALOGW("Invalid zip offset %" PRId64, offset);
|
||||
return kIoError;
|
||||
}
|
||||
|
||||
return OpenArchiveInternal(archive, debug_file_name);
|
||||
}
|
||||
|
||||
int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
|
||||
const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
|
||||
ZipArchive* archive = new ZipArchive(fd, true);
|
||||
ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
|
||||
*handle = archive;
|
||||
|
||||
if (fd < 0) {
|
||||
|
@ -1030,6 +1049,10 @@ int GetFileDescriptor(const ZipArchiveHandle archive) {
|
|||
return archive->mapped_zip.GetFileDescriptor();
|
||||
}
|
||||
|
||||
off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive) {
|
||||
return archive->mapped_zip.GetFileOffset();
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
class ProcessWriter : public zip_archive::Writer {
|
||||
public:
|
||||
|
@ -1069,31 +1092,65 @@ const void* MappedZipFile::GetBasePtr() const {
|
|||
return base_ptr_;
|
||||
}
|
||||
|
||||
off64_t MappedZipFile::GetFileOffset() const {
|
||||
return fd_offset_;
|
||||
}
|
||||
|
||||
off64_t MappedZipFile::GetFileLength() const {
|
||||
if (has_fd_) {
|
||||
off64_t result = lseek64(fd_, 0, SEEK_END);
|
||||
if (result == -1) {
|
||||
if (data_length_ != -1) {
|
||||
return data_length_;
|
||||
}
|
||||
data_length_ = lseek64(fd_, 0, SEEK_END);
|
||||
if (data_length_ == -1) {
|
||||
ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
|
||||
}
|
||||
return result;
|
||||
return data_length_;
|
||||
} else {
|
||||
if (base_ptr_ == nullptr) {
|
||||
ALOGE("Zip: invalid file map");
|
||||
return -1;
|
||||
}
|
||||
return static_cast<off64_t>(data_length_);
|
||||
return data_length_;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to read |len| bytes into |buf| at offset |off|.
|
||||
bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
|
||||
if (has_fd_) {
|
||||
if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
|
||||
if (off < 0) {
|
||||
ALOGE("Zip: invalid offset %" PRId64, off);
|
||||
return false;
|
||||
}
|
||||
|
||||
off64_t read_offset;
|
||||
if (__builtin_add_overflow(fd_offset_, off, &read_offset)) {
|
||||
ALOGE("Zip: invalid read offset %" PRId64 " overflows, fd offset %" PRId64, off, fd_offset_);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data_length_ != -1) {
|
||||
off64_t read_end;
|
||||
if (len > std::numeric_limits<off64_t>::max() ||
|
||||
__builtin_add_overflow(off, static_cast<off64_t>(len), &read_end)) {
|
||||
ALOGE("Zip: invalid read length %" PRId64 " overflows, offset %" PRId64,
|
||||
static_cast<off64_t>(len), off);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (read_end > data_length_) {
|
||||
ALOGE("Zip: invalid read length %" PRId64 " exceeds data length %" PRId64 ", offset %"
|
||||
PRId64, static_cast<off64_t>(len), data_length_, off);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!android::base::ReadFullyAtOffset(fd_, buf, len, read_offset)) {
|
||||
ALOGE("Zip: failed to read at offset %" PRId64, off);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (off < 0 || off > static_cast<off64_t>(data_length_)) {
|
||||
if (off < 0 || off > data_length_) {
|
||||
ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
|
||||
return false;
|
||||
}
|
||||
|
@ -1111,7 +1168,8 @@ void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_off
|
|||
bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
|
||||
if (mapped_zip.HasFd()) {
|
||||
directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
|
||||
cd_start_offset, cd_size, PROT_READ);
|
||||
mapped_zip.GetFileOffset() + cd_start_offset,
|
||||
cd_size, PROT_READ);
|
||||
if (!directory_map) {
|
||||
ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
|
||||
cd_start_offset, cd_size, strerror(errno));
|
||||
|
|
|
@ -34,10 +34,14 @@
|
|||
class MappedZipFile {
|
||||
public:
|
||||
explicit MappedZipFile(const int fd)
|
||||
: has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
|
||||
: has_fd_(true), fd_(fd), fd_offset_(0), base_ptr_(nullptr), data_length_(-1) {}
|
||||
|
||||
explicit MappedZipFile(const int fd, off64_t length, off64_t offset)
|
||||
: has_fd_(true), fd_(fd), fd_offset_(offset), base_ptr_(nullptr), data_length_(length) {}
|
||||
|
||||
explicit MappedZipFile(const void* address, size_t length)
|
||||
: has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
|
||||
: has_fd_(false), fd_(-1), fd_offset_(0), base_ptr_(address),
|
||||
data_length_(static_cast<off64_t>(length)) {}
|
||||
|
||||
bool HasFd() const { return has_fd_; }
|
||||
|
||||
|
@ -45,6 +49,8 @@ class MappedZipFile {
|
|||
|
||||
const void* GetBasePtr() const;
|
||||
|
||||
off64_t GetFileOffset() const;
|
||||
|
||||
off64_t GetFileLength() const;
|
||||
|
||||
bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
|
||||
|
@ -57,9 +63,10 @@ class MappedZipFile {
|
|||
const bool has_fd_;
|
||||
|
||||
const int fd_;
|
||||
const off64_t fd_offset_;
|
||||
|
||||
const void* const base_ptr_;
|
||||
const off64_t data_length_;
|
||||
mutable off64_t data_length_;
|
||||
};
|
||||
|
||||
class CentralDirectory {
|
||||
|
@ -91,7 +98,7 @@ struct ZipArchive {
|
|||
uint16_t num_entries;
|
||||
std::unique_ptr<CdEntryMapInterface> cd_entry_map;
|
||||
|
||||
ZipArchive(const int fd, bool assume_ownership);
|
||||
ZipArchive(MappedZipFile&& map, bool assume_ownership);
|
||||
ZipArchive(const void* address, size_t length);
|
||||
~ZipArchive();
|
||||
|
||||
|
|
|
@ -181,6 +181,32 @@ TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
|
|||
close(fd);
|
||||
}
|
||||
|
||||
TEST(ziparchive, OpenAssumeFdRangeOwnership) {
|
||||
int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
|
||||
ASSERT_NE(-1, fd);
|
||||
const off64_t length = lseek64(fd, 0, SEEK_END);
|
||||
ASSERT_NE(-1, length);
|
||||
ZipArchiveHandle handle;
|
||||
ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
|
||||
static_cast<size_t>(length), 0));
|
||||
CloseArchive(handle);
|
||||
ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
|
||||
ASSERT_EQ(EBADF, errno);
|
||||
}
|
||||
|
||||
TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
|
||||
int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
|
||||
ASSERT_NE(-1, fd);
|
||||
const off64_t length = lseek(fd, 0, SEEK_END);
|
||||
ASSERT_NE(-1, length);
|
||||
ZipArchiveHandle handle;
|
||||
ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
|
||||
static_cast<size_t>(length), 0, false));
|
||||
CloseArchive(handle);
|
||||
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(ziparchive, Iteration_std_string_view) {
|
||||
ZipArchiveHandle handle;
|
||||
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
|
||||
|
@ -327,6 +353,48 @@ TEST(ziparchive, TestInvalidDeclaredLength) {
|
|||
CloseArchive(handle);
|
||||
}
|
||||
|
||||
TEST(ziparchive, OpenArchiveFdRange) {
|
||||
TemporaryFile tmp_file;
|
||||
ASSERT_NE(-1, tmp_file.fd);
|
||||
|
||||
const std::string leading_garbage(21, 'x');
|
||||
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
|
||||
leading_garbage.size()));
|
||||
|
||||
std::string valid_content;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
|
||||
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
|
||||
|
||||
const std::string ending_garbage(42, 'x');
|
||||
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
|
||||
ending_garbage.size()));
|
||||
|
||||
ZipArchiveHandle handle;
|
||||
ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
|
||||
ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
|
||||
valid_content.size(),
|
||||
static_cast<off64_t>(leading_garbage.size())));
|
||||
|
||||
// An entry that's deflated.
|
||||
ZipEntry data;
|
||||
ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
|
||||
const uint32_t a_size = data.uncompressed_length;
|
||||
ASSERT_EQ(a_size, kATxtContents.size());
|
||||
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
|
||||
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
|
||||
ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
|
||||
|
||||
// An entry that's stored.
|
||||
ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
|
||||
const uint32_t b_size = data.uncompressed_length;
|
||||
ASSERT_EQ(b_size, kBTxtContents.size());
|
||||
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
|
||||
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
|
||||
ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
|
||||
|
||||
CloseArchive(handle);
|
||||
}
|
||||
|
||||
TEST(ziparchive, ExtractToMemory) {
|
||||
ZipArchiveHandle handle;
|
||||
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
|
||||
|
|
Loading…
Reference in a new issue