Merge "Allow loading zip at an offset in fd" am: ed22a0aa70

Change-Id: I38b81c3af281feadd4ce5faaaf75dc2483df02b1
This commit is contained in:
Ryan Mitchell 2020-03-18 23:26:16 +00:00 committed by Automerger Merge Worker
commit bbd087221f
4 changed files with 158 additions and 16 deletions

View file

@ -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)

View file

@ -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));

View file

@ -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();

View file

@ -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));