summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNarayan Kamath2015-04-17 05:53:14 -0500
committerElliott Hughes2015-06-04 15:25:52 -0500
commit785a128aec74dc7bee7d76adafa69c94a4b268aa (patch)
tree41c87b7ce5f729623763544ea2320fdb81744436 /libziparchive/zip_archive.cc
parentb8216007c66a219de310830aaee05bf5a3d90466 (diff)
downloadplatform-system-core-785a128aec74dc7bee7d76adafa69c94a4b268aa.tar.gz
platform-system-core-785a128aec74dc7bee7d76adafa69c94a4b268aa.tar.xz
platform-system-core-785a128aec74dc7bee7d76adafa69c94a4b268aa.zip
Avoid mapping output to memory while writing to a file.
It's unnecessary, and causes issues when the uncompressed output is large. Bug: http://b/21558406 Change-Id: I99cfb3933b094c2826c7e6c6de9aab03478fcc53 (cherry picked from commit f899bd534b2dc51b9db8d27c76394b192fe51155)
Diffstat (limited to 'libziparchive/zip_archive.cc')
-rw-r--r--libziparchive/zip_archive.cc271
1 files changed, 181 insertions, 90 deletions
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 858234446..4ba91dfb9 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -85,7 +85,8 @@ struct EocdRecord {
85 // Length of the central directory comment. 85 // Length of the central directory comment.
86 uint16_t comment_length; 86 uint16_t comment_length;
87 private: 87 private:
88 DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord); 88 EocdRecord() = default;
89 DISALLOW_COPY_AND_ASSIGN(EocdRecord);
89} __attribute__((packed)); 90} __attribute__((packed));
90 91
91// A structure representing the fixed length fields for a single 92// A structure representing the fixed length fields for a single
@@ -138,7 +139,8 @@ struct CentralDirectoryRecord {
138 // beginning of this archive. 139 // beginning of this archive.
139 uint32_t local_file_header_offset; 140 uint32_t local_file_header_offset;
140 private: 141 private:
141 DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord); 142 CentralDirectoryRecord() = default;
143 DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
142} __attribute__((packed)); 144} __attribute__((packed));
143 145
144// The local file header for a given entry. This duplicates information 146// The local file header for a given entry. This duplicates information
@@ -175,7 +177,8 @@ struct LocalFileHeader {
175 // will appear immediately after the entry file name. 177 // will appear immediately after the entry file name.
176 uint16_t extra_field_length; 178 uint16_t extra_field_length;
177 private: 179 private:
178 DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader); 180 LocalFileHeader() = default;
181 DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
179} __attribute__((packed)); 182} __attribute__((packed));
180 183
181struct DataDescriptor { 184struct DataDescriptor {
@@ -189,10 +192,10 @@ struct DataDescriptor {
189 // Uncompressed size of the entry. 192 // Uncompressed size of the entry.
190 uint32_t uncompressed_size; 193 uint32_t uncompressed_size;
191 private: 194 private:
192 DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor); 195 DataDescriptor() = default;
196 DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
193} __attribute__((packed)); 197} __attribute__((packed));
194 198
195#undef DISALLOW_IMPLICIT_CONSTRUCTORS
196 199
197static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD 200static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD
198 201
@@ -324,35 +327,6 @@ struct ZipArchive {
324 } 327 }
325}; 328};
326 329
327static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) {
328 static const uint32_t kBufSize = 32768;
329 uint8_t buf[kBufSize];
330
331 uint32_t count = 0;
332 uint64_t crc = 0;
333 while (count < length) {
334 uint32_t remaining = length - count;
335
336 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
337 // value.
338 ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining;
339 ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
340
341 if (actual != get_size) {
342 ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size);
343 return kIoError;
344 }
345
346 memcpy(begin + count, buf, get_size);
347 crc = crc32(crc, buf, get_size);
348 count += get_size;
349 }
350
351 *crc_out = crc;
352
353 return 0;
354}
355
356/* 330/*
357 * Round up to the next highest power of 2. 331 * Round up to the next highest power of 2.
358 * 332 *
@@ -972,6 +946,128 @@ int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
972 return kIterationEnd; 946 return kIterationEnd;
973} 947}
974 948
949class Writer {
950 public:
951 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
952 virtual ~Writer() {}
953 protected:
954 Writer() = default;
955 private:
956 DISALLOW_COPY_AND_ASSIGN(Writer);
957};
958
959// A Writer that writes data to a fixed size memory region.
960// The size of the memory region must be equal to the total size of
961// the data appended to it.
962class MemoryWriter : public Writer {
963 public:
964 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
965 buf_(buf), size_(size), bytes_written_(0) {
966 }
967
968 virtual bool Append(uint8_t* buf, size_t buf_size) override {
969 if (bytes_written_ + buf_size > size_) {
970 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
971 size_, bytes_written_ + buf_size);
972 return false;
973 }
974
975 memcpy(buf_ + bytes_written_, buf, buf_size);
976 bytes_written_ += buf_size;
977 return true;
978 }
979
980 private:
981 uint8_t* const buf_;
982 const size_t size_;
983 size_t bytes_written_;
984};
985
986// A Writer that appends data to a file |fd| at its current position.
987// The file will be truncated to the end of the written data.
988class FileWriter : public Writer {
989 public:
990
991 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
992 // guaranteeing that the file descriptor is valid and that there's enough
993 // space on the volume to write out the entry completely and that the file
994 // is truncated to the correct length.
995 //
996 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
997 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
998 const uint32_t declared_length = entry->uncompressed_length;
999 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
1000 if (current_offset == -1) {
1001 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
1002 return nullptr;
1003 }
1004
1005 int result = 0;
1006#if defined(__linux__)
1007 if (declared_length > 0) {
1008 // Make sure we have enough space on the volume to extract the compressed
1009 // entry. Note that the call to ftruncate below will change the file size but
1010 // will not allocate space on disk and this call to fallocate will not
1011 // change the file size.
1012 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
1013 if (result == -1) {
1014 ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
1015 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
1016 return std::unique_ptr<FileWriter>(nullptr);
1017 }
1018 }
1019#endif // __linux__
1020
1021 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
1022 if (result == -1) {
1023 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
1024 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
1025 return std::unique_ptr<FileWriter>(nullptr);
1026 }
1027
1028 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
1029 }
1030
1031 virtual bool Append(uint8_t* buf, size_t buf_size) override {
1032 if (total_bytes_written_ + buf_size > declared_length_) {
1033 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
1034 declared_length_, total_bytes_written_ + buf_size);
1035 return false;
1036 }
1037
1038 // Keep track of the start position so we can calculate the
1039 // total number of bytes written.
1040 const uint8_t* const start = buf;
1041 size_t bytes_written = 0;
1042 while (buf_size > 0) {
1043 ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, buf, buf_size));
1044 if (bytes_written == -1) {
1045 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
1046 return false;
1047 }
1048
1049 buf_size -= bytes_written;
1050 buf += bytes_written;
1051 }
1052
1053 total_bytes_written_ += static_cast<size_t>(
1054 reinterpret_cast<uintptr_t>(buf) - reinterpret_cast<uintptr_t>(start));
1055
1056 return true;
1057 }
1058 private:
1059 FileWriter(const int fd, const size_t declared_length) :
1060 Writer(),
1061 fd_(fd),
1062 declared_length_(declared_length),
1063 total_bytes_written_(0) {
1064 }
1065
1066 const int fd_;
1067 const size_t declared_length_;
1068 size_t total_bytes_written_;
1069};
1070
975// This method is using libz macros with old-style-casts 1071// This method is using libz macros with old-style-casts
976#pragma GCC diagnostic push 1072#pragma GCC diagnostic push
977#pragma GCC diagnostic ignored "-Wold-style-cast" 1073#pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -980,9 +1076,8 @@ static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
980} 1076}
981#pragma GCC diagnostic pop 1077#pragma GCC diagnostic pop
982 1078
983static int32_t InflateToFile(int fd, const ZipEntry* entry, 1079static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
984 uint8_t* begin, uint32_t length, 1080 Writer* writer, uint64_t* crc_out) {
985 uint64_t* crc_out) {
986 const size_t kBufSize = 32768; 1081 const size_t kBufSize = 32768;
987 std::vector<uint8_t> read_buf(kBufSize); 1082 std::vector<uint8_t> read_buf(kBufSize);
988 std::vector<uint8_t> write_buf(kBufSize); 1083 std::vector<uint8_t> write_buf(kBufSize);
@@ -1057,12 +1152,10 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry,
1057 if (zstream.avail_out == 0 || 1152 if (zstream.avail_out == 0 ||
1058 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { 1153 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
1059 const size_t write_size = zstream.next_out - &write_buf[0]; 1154 const size_t write_size = zstream.next_out - &write_buf[0];
1060 // The file might have declared a bogus length. 1155 if (!writer->Append(&write_buf[0], write_size)) {
1061 if (write_size + write_count > length) { 1156 // The file might have declared a bogus length.
1062 return -1; 1157 return kInconsistentInformation;
1063 } 1158 }
1064 memcpy(begin + write_count, &write_buf[0], write_size);
1065 write_count += write_size;
1066 1159
1067 zstream.next_out = &write_buf[0]; 1160 zstream.next_out = &write_buf[0];
1068 zstream.avail_out = kBufSize; 1161 zstream.avail_out = kBufSize;
@@ -1083,8 +1176,41 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry,
1083 return 0; 1176 return 0;
1084} 1177}
1085 1178
1086int32_t ExtractToMemory(ZipArchiveHandle handle, 1179static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
1087 ZipEntry* entry, uint8_t* begin, uint32_t size) { 1180 uint64_t *crc_out) {
1181 static const uint32_t kBufSize = 32768;
1182 std::vector<uint8_t> buf(kBufSize);
1183
1184 const uint32_t length = entry->uncompressed_length;
1185 uint32_t count = 0;
1186 uint64_t crc = 0;
1187 while (count < length) {
1188 uint32_t remaining = length - count;
1189
1190 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
1191 // value.
1192 const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
1193 const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
1194
1195 if (actual != block_size) {
1196 ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
1197 return kIoError;
1198 }
1199
1200 if (!writer->Append(&buf[0], block_size)) {
1201 return kIoError;
1202 }
1203 crc = crc32(crc, &buf[0], block_size);
1204 count += block_size;
1205 }
1206
1207 *crc_out = crc;
1208
1209 return 0;
1210}
1211
1212int32_t ExtractToWriter(ZipArchiveHandle handle,
1213 ZipEntry* entry, Writer* writer) {
1088 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); 1214 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
1089 const uint16_t method = entry->method; 1215 const uint16_t method = entry->method;
1090 off64_t data_offset = entry->offset; 1216 off64_t data_offset = entry->offset;
@@ -1098,9 +1224,9 @@ int32_t ExtractToMemory(ZipArchiveHandle handle,
1098 int32_t return_value = -1; 1224 int32_t return_value = -1;
1099 uint64_t crc = 0; 1225 uint64_t crc = 0;
1100 if (method == kCompressStored) { 1226 if (method == kCompressStored) {
1101 return_value = CopyFileToFile(archive->fd, begin, size, &crc); 1227 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
1102 } else if (method == kCompressDeflated) { 1228 } else if (method == kCompressDeflated) {
1103 return_value = InflateToFile(archive->fd, entry, begin, size, &crc); 1229 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
1104 } 1230 }
1105 1231
1106 if (!return_value && entry->has_data_descriptor) { 1232 if (!return_value && entry->has_data_descriptor) {
@@ -1120,55 +1246,20 @@ int32_t ExtractToMemory(ZipArchiveHandle handle,
1120 return return_value; 1246 return return_value;
1121} 1247}
1122 1248
1249int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1250 uint8_t* begin, uint32_t size) {
1251 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1252 return ExtractToWriter(handle, entry, writer.get());
1253}
1254
1123int32_t ExtractEntryToFile(ZipArchiveHandle handle, 1255int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1124 ZipEntry* entry, int fd) { 1256 ZipEntry* entry, int fd) {
1125 const uint32_t declared_length = entry->uncompressed_length; 1257 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1126 1258 if (writer.get() == nullptr) {
1127 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
1128 if (current_offset == -1) {
1129 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd,
1130 strerror(errno));
1131 return kIoError; 1259 return kIoError;
1132 } 1260 }
1133 1261
1134 int result = 0; 1262 return ExtractToWriter(handle, entry, writer.get());
1135#if defined(__linux__)
1136 // Make sure we have enough space on the volume to extract the compressed
1137 // entry. Note that the call to ftruncate below will change the file size but
1138 // will not allocate space on disk.
1139 if (declared_length > 0) {
1140 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
1141 if (result == -1) {
1142 ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
1143 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
1144 return kIoError;
1145 }
1146 }
1147#endif // defined(__linux__)
1148
1149 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
1150 if (result == -1) {
1151 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
1152 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
1153 return kIoError;
1154 }
1155
1156 // Don't attempt to map a region of length 0. We still need the
1157 // ftruncate() though, since the API guarantees that we will truncate
1158 // the file to the end of the uncompressed output.
1159 if (declared_length == 0) {
1160 return 0;
1161 }
1162
1163 android::FileMap map;
1164 if (!map.create(kTempMappingFileName, fd, current_offset, declared_length, false)) {
1165 return kMmapFailed;
1166 }
1167
1168 const int32_t error = ExtractToMemory(handle, entry,
1169 reinterpret_cast<uint8_t*>(map.getDataPtr()),
1170 map.getDataLength());
1171 return error;
1172} 1263}
1173 1264
1174const char* ErrorCodeString(int32_t error_code) { 1265const char* ErrorCodeString(int32_t error_code) {