Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56b900b2 authored by Ryan Mitchell's avatar Ryan Mitchell Committed by Automerger Merge Worker
Browse files

Merge "Allow loading zip at an offset in fd" into rvc-dev am: a355d925

Change-Id: I4d01408f09827c44239576fc44475ac657f893e3
parents 09da8ea1 a355d925
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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)
+70 −12
Original line number Diff line number Diff line
@@ -162,8 +162,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(),
@@ -173,7 +173,8 @@ ZipArchive::ZipArchive(const int fd, bool assume_ownership)
      hash_table(nullptr) {
#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
}
@@ -442,14 +443,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) {
@@ -1126,6 +1145,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:
@@ -1165,31 +1188,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;
    }
@@ -1207,7 +1264,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));
+11 −4
Original line number Diff line number Diff line
@@ -97,10 +97,14 @@ enum ErrorCodes : int32_t {
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_; }

@@ -108,6 +112,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;
@@ -120,9 +126,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 {
@@ -180,7 +187,7 @@ struct ZipArchive {
  uint32_t hash_table_size;
  ZipStringOffset* hash_table;

  ZipArchive(const int fd, bool assume_ownership);
  ZipArchive(MappedZipFile&& map, bool assume_ownership);
  ZipArchive(const void* address, size_t length);
  ~ZipArchive();

+68 −0
Original line number Diff line number Diff line
@@ -108,6 +108,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));
@@ -254,6 +280,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));