blob: 826f91b4a2fd18debe1e917e6679a92e7141f23e [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 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#include "flatten/Archive.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070018
Adam Lesinskia40e9722015-11-24 19:11:46 -080019#include <cstdio>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070020#include <memory>
21#include <string>
22#include <vector>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023
Adam Lesinski06460ef2017-03-14 18:52:13 -070024#include "android-base/errors.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070025#include "android-base/macros.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080026#include "androidfw/StringPiece.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070027#include "ziparchive/zip_writer.h"
28
29#include "util/Files.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080030
31using android::StringPiece;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070032
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033namespace aapt {
34
35namespace {
36
Adam Lesinskice5e56e2016-10-21 17:56:45 -070037class DirectoryWriter : public IArchiveWriter {
38 public:
39 DirectoryWriter() = default;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040
Adam Lesinski06460ef2017-03-14 18:52:13 -070041 bool Open(const StringPiece& out_dir) {
Adam Lesinskid5083f62017-01-16 15:07:21 -080042 dir_ = out_dir.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070043 file::FileType type = file::GetFileType(dir_);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070044 if (type == file::FileType::kNonexistant) {
Adam Lesinski06460ef2017-03-14 18:52:13 -070045 error_ = "directory does not exist";
Adam Lesinskicacb28f2016-10-19 12:18:14 -070046 return false;
47 } else if (type != file::FileType::kDirectory) {
Adam Lesinski06460ef2017-03-14 18:52:13 -070048 error_ = "not a directory";
Adam Lesinskicacb28f2016-10-19 12:18:14 -070049 return false;
50 }
51 return true;
52 }
53
Adam Lesinskice5e56e2016-10-21 17:56:45 -070054 bool StartEntry(const StringPiece& path, uint32_t flags) override {
55 if (file_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070057 }
58
Adam Lesinskice5e56e2016-10-21 17:56:45 -070059 std::string full_path = dir_;
60 file::AppendPath(&full_path, path);
61 file::mkdirs(file::GetStem(full_path));
Adam Lesinskia40e9722015-11-24 19:11:46 -080062
Adam Lesinskice5e56e2016-10-21 17:56:45 -070063 file_ = {fopen(full_path.data(), "wb"), fclose};
64 if (!file_) {
Adam Lesinski06460ef2017-03-14 18:52:13 -070065 error_ = android::base::SystemErrorCodeToString(errno);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070066 return false;
67 }
68 return true;
69 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070070
Adam Lesinski06460ef2017-03-14 18:52:13 -070071 bool Write(const void* data, int len) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070072 if (!file_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070073 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070074 }
75
Adam Lesinski06460ef2017-03-14 18:52:13 -070076 if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
77 error_ = android::base::SystemErrorCodeToString(errno);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078 file_.reset(nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070079 return false;
80 }
81 return true;
82 }
83
Adam Lesinskice5e56e2016-10-21 17:56:45 -070084 bool FinishEntry() override {
85 if (!file_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070086 return false;
87 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070088 file_.reset(nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070089 return true;
90 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070091
Adam Lesinski06460ef2017-03-14 18:52:13 -070092 bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
93 if (!StartEntry(path, flags)) {
94 return false;
95 }
96
97 const void* data = nullptr;
98 size_t len = 0;
99 while (in->Next(&data, &len)) {
100 if (!Write(data, static_cast<int>(len))) {
101 return false;
102 }
103 }
104 return !in->HadError();
105 }
106
107 bool HadError() const override { return !error_.empty(); }
108
109 std::string GetError() const override { return error_; }
110
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700111 private:
112 DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
113
114 std::string dir_;
115 std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
Adam Lesinski06460ef2017-03-14 18:52:13 -0700116 std::string error_;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117};
118
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700119class ZipFileWriter : public IArchiveWriter {
120 public:
121 ZipFileWriter() = default;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700122
Adam Lesinski06460ef2017-03-14 18:52:13 -0700123 bool Open(const StringPiece& path) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700124 file_ = {fopen(path.data(), "w+b"), fclose};
125 if (!file_) {
Adam Lesinski06460ef2017-03-14 18:52:13 -0700126 error_ = android::base::SystemErrorCodeToString(errno);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700127 return false;
128 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 writer_ = util::make_unique<ZipWriter>(file_.get());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700130 return true;
131 }
132
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133 bool StartEntry(const StringPiece& path, uint32_t flags) override {
134 if (!writer_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700135 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700136 }
137
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700138 size_t zip_flags = 0;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700139 if (flags & ArchiveEntry::kCompress) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700140 zip_flags |= ZipWriter::kCompress;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800141 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 if (flags & ArchiveEntry::kAlign) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700144 zip_flags |= ZipWriter::kAlign32;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800145 }
146
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700147 int32_t result = writer_->StartEntry(path.data(), zip_flags);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 if (result != 0) {
Adam Lesinski06460ef2017-03-14 18:52:13 -0700149 error_ = ZipWriter::ErrorCodeString(result);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700152 return true;
153 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700154
Adam Lesinski06460ef2017-03-14 18:52:13 -0700155 bool Write(const void* data, int len) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700156 int32_t result = writer_->WriteBytes(data, len);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 if (result != 0) {
Adam Lesinski06460ef2017-03-14 18:52:13 -0700158 error_ = ZipWriter::ErrorCodeString(result);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700160 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 return true;
162 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700163
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700164 bool FinishEntry() override {
165 int32_t result = writer_->FinishEntry();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 if (result != 0) {
Adam Lesinski06460ef2017-03-14 18:52:13 -0700167 error_ = ZipWriter::ErrorCodeString(result);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700168 return false;
169 }
170 return true;
171 }
172
Adam Lesinski06460ef2017-03-14 18:52:13 -0700173 bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
174 while (true) {
175 if (!StartEntry(path, flags)) {
176 return false;
177 }
178
179 const void* data = nullptr;
180 size_t len = 0;
181 while (in->Next(&data, &len)) {
182 if (!Write(data, static_cast<int>(len))) {
183 return false;
184 }
185 }
186
187 if (in->HadError()) {
188 return false;
189 }
190
191 if (!FinishEntry()) {
192 return false;
193 }
194
195 // Check to see if the file was compressed enough. This is preserving behavior of AAPT.
196 if ((flags & ArchiveEntry::kCompress) != 0 && in->CanRewind()) {
197 ZipWriter::FileEntry last_entry;
198 int32_t result = writer_->GetLastEntry(&last_entry);
199 CHECK(result == 0);
200 if (last_entry.compressed_size + (last_entry.compressed_size / 10) >
201 last_entry.uncompressed_size) {
202 // The file was not compressed enough, rewind and store it uncompressed.
203 if (!in->Rewind()) {
204 // Well we tried, may as well keep what we had.
205 return true;
206 }
207
208 int32_t result = writer_->DiscardLastEntry();
209 if (result != 0) {
210 error_ = ZipWriter::ErrorCodeString(result);
211 return false;
212 }
213 flags &= ~ArchiveEntry::kCompress;
214
215 continue;
216 }
217 }
218 return true;
219 }
220 }
221
222 bool HadError() const override { return !error_.empty(); }
223
224 std::string GetError() const override { return error_; }
225
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700226 virtual ~ZipFileWriter() {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700227 if (writer_) {
228 writer_->Finish();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700229 }
230 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700231
232 private:
233 DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
234
235 std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
236 std::unique_ptr<ZipWriter> writer_;
Adam Lesinski06460ef2017-03-14 18:52:13 -0700237 std::string error_;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700238};
239
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700240} // namespace
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700241
Adam Lesinski06460ef2017-03-14 18:52:13 -0700242std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
243 const StringPiece& path) {
244 std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
245 if (!writer->Open(path)) {
246 diag->Error(DiagMessage(path) << writer->GetError());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700247 return {};
248 }
249 return std::move(writer);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700250}
251
Adam Lesinski06460ef2017-03-14 18:52:13 -0700252std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
253 const StringPiece& path) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700254 std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
Adam Lesinski06460ef2017-03-14 18:52:13 -0700255 if (!writer->Open(path)) {
256 diag->Error(DiagMessage(path) << writer->GetError());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700257 return {};
258 }
259 return std::move(writer);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700260}
261
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700262} // namespace aapt