• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <ziparchive/zip_archive.h>
20 
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 
25 #include <memory>
26 #include <vector>
27 
28 #include "android-base/macros.h"
29 #include "android-base/mapped_file.h"
30 
31 static const char* kErrorMessages[] = {
32     "Success",
33     "Iteration ended",
34     "Zlib error",
35     "Invalid file",
36     "Invalid handle",
37     "Duplicate entries in archive",
38     "Empty archive",
39     "Entry not found",
40     "Invalid offset",
41     "Inconsistent information",
42     "Invalid entry name",
43     "I/O error",
44     "File mapping failed",
45 };
46 
47 enum ErrorCodes : int32_t {
48   kIterationEnd = -1,
49 
50   // We encountered a Zlib error when inflating a stream from this file.
51   // Usually indicates file corruption.
52   kZlibError = -2,
53 
54   // The input file cannot be processed as a zip archive. Usually because
55   // it's too small, too large or does not have a valid signature.
56   kInvalidFile = -3,
57 
58   // An invalid iteration / ziparchive handle was passed in as an input
59   // argument.
60   kInvalidHandle = -4,
61 
62   // The zip archive contained two (or possibly more) entries with the same
63   // name.
64   kDuplicateEntry = -5,
65 
66   // The zip archive contains no entries.
67   kEmptyArchive = -6,
68 
69   // The specified entry was not found in the archive.
70   kEntryNotFound = -7,
71 
72   // The zip archive contained an invalid local file header pointer.
73   kInvalidOffset = -8,
74 
75   // The zip archive contained inconsistent entry information. This could
76   // be because the central directory & local file header did not agree, or
77   // if the actual uncompressed length or crc32 do not match their declared
78   // values.
79   kInconsistentInformation = -9,
80 
81   // An invalid entry name was encountered.
82   kInvalidEntryName = -10,
83 
84   // An I/O related system call (read, lseek, ftruncate, map) failed.
85   kIoError = -11,
86 
87   // We were not able to mmap the central directory or entry contents.
88   kMmapFailed = -12,
89 
90   kLastErrorCode = kMmapFailed,
91 };
92 
93 class MappedZipFile {
94  public:
MappedZipFile(const int fd)95   explicit MappedZipFile(const int fd)
96       : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
97 
MappedZipFile(void * address,size_t length)98   explicit MappedZipFile(void* address, size_t length)
99       : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
100 
HasFd()101   bool HasFd() const { return has_fd_; }
102 
103   int GetFileDescriptor() const;
104 
105   void* GetBasePtr() const;
106 
107   off64_t GetFileLength() const;
108 
109   bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
110 
111  private:
112   // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
113   // from the file. Otherwise, we're opening the archive from a memory mapped
114   // file. In that case, base_ptr_ points to the start of the memory region and
115   // data_length_ defines the file length.
116   const bool has_fd_;
117 
118   const int fd_;
119 
120   void* const base_ptr_;
121   const off64_t data_length_;
122 };
123 
124 class CentralDirectory {
125  public:
CentralDirectory(void)126   CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
127 
GetBasePtr()128   const uint8_t* GetBasePtr() const { return base_ptr_; }
129 
GetMapLength()130   size_t GetMapLength() const { return length_; }
131 
132   void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
133 
134  private:
135   const uint8_t* base_ptr_;
136   size_t length_;
137 };
138 
139 /**
140  * More space efficient string representation of strings in an mmaped zipped file than
141  * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
142  * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
143  * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
144  * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
145  * of the entire address, consuming 8 bytes with alignment.
146  */
147 struct ZipStringOffset {
148   uint32_t name_offset;
149   uint16_t name_length;
150 
GetZipStringZipStringOffset151   const ZipString GetZipString(const uint8_t* start) const {
152     ZipString zip_string;
153     zip_string.name = start + name_offset;
154     zip_string.name_length = name_length;
155     return zip_string;
156   }
157 };
158 
159 struct ZipArchive {
160   // open Zip archive
161   mutable MappedZipFile mapped_zip;
162   const bool close_file;
163 
164   // mapped central directory area
165   off64_t directory_offset;
166   CentralDirectory central_directory;
167   std::unique_ptr<android::base::MappedFile> directory_map;
168 
169   // number of entries in the Zip archive
170   uint16_t num_entries;
171 
172   // We know how many entries are in the Zip archive, so we can have a
173   // fixed-size hash table. We define a load factor of 0.75 and over
174   // allocate so the maximum number entries can never be higher than
175   // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
176   uint32_t hash_table_size;
177   ZipStringOffset* hash_table;
178 
179   ZipArchive(const int fd, bool assume_ownership);
180   ZipArchive(void* address, size_t length);
181   ~ZipArchive();
182 
183   bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
184 };
185