1 /*
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"
18
19 #include <cstdio>
20 #include <memory>
21 #include <string>
22 #include <vector>
23
24 #include "android-base/errors.h"
25 #include "android-base/macros.h"
26 #include "androidfw/StringPiece.h"
27 #include "ziparchive/zip_writer.h"
28
29 #include "util/Files.h"
30
31 using android::StringPiece;
32
33 namespace aapt {
34
35 namespace {
36
37 class DirectoryWriter : public IArchiveWriter {
38 public:
39 DirectoryWriter() = default;
40
Open(const StringPiece & out_dir)41 bool Open(const StringPiece& out_dir) {
42 dir_ = out_dir.to_string();
43 file::FileType type = file::GetFileType(dir_);
44 if (type == file::FileType::kNonexistant) {
45 error_ = "directory does not exist";
46 return false;
47 } else if (type != file::FileType::kDirectory) {
48 error_ = "not a directory";
49 return false;
50 }
51 return true;
52 }
53
StartEntry(const StringPiece & path,uint32_t flags)54 bool StartEntry(const StringPiece& path, uint32_t flags) override {
55 if (file_) {
56 return false;
57 }
58
59 std::string full_path = dir_;
60 file::AppendPath(&full_path, path);
61 file::mkdirs(file::GetStem(full_path));
62
63 file_ = {fopen(full_path.data(), "wb"), fclose};
64 if (!file_) {
65 error_ = android::base::SystemErrorCodeToString(errno);
66 return false;
67 }
68 return true;
69 }
70
Write(const void * data,int len)71 bool Write(const void* data, int len) override {
72 if (!file_) {
73 return false;
74 }
75
76 if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
77 error_ = android::base::SystemErrorCodeToString(errno);
78 file_.reset(nullptr);
79 return false;
80 }
81 return true;
82 }
83
FinishEntry()84 bool FinishEntry() override {
85 if (!file_) {
86 return false;
87 }
88 file_.reset(nullptr);
89 return true;
90 }
91
WriteFile(const StringPiece & path,uint32_t flags,io::InputStream * in)92 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
HadError() const107 bool HadError() const override { return !error_.empty(); }
108
GetError() const109 std::string GetError() const override { return error_; }
110
111 private:
112 DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
113
114 std::string dir_;
115 std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
116 std::string error_;
117 };
118
119 class ZipFileWriter : public IArchiveWriter {
120 public:
121 ZipFileWriter() = default;
122
Open(const StringPiece & path)123 bool Open(const StringPiece& path) {
124 file_ = {fopen(path.data(), "w+b"), fclose};
125 if (!file_) {
126 error_ = android::base::SystemErrorCodeToString(errno);
127 return false;
128 }
129 writer_ = util::make_unique<ZipWriter>(file_.get());
130 return true;
131 }
132
StartEntry(const StringPiece & path,uint32_t flags)133 bool StartEntry(const StringPiece& path, uint32_t flags) override {
134 if (!writer_) {
135 return false;
136 }
137
138 size_t zip_flags = 0;
139 if (flags & ArchiveEntry::kCompress) {
140 zip_flags |= ZipWriter::kCompress;
141 }
142
143 if (flags & ArchiveEntry::kAlign) {
144 zip_flags |= ZipWriter::kAlign32;
145 }
146
147 int32_t result = writer_->StartEntry(path.data(), zip_flags);
148 if (result != 0) {
149 error_ = ZipWriter::ErrorCodeString(result);
150 return false;
151 }
152 return true;
153 }
154
Write(const void * data,int len)155 bool Write(const void* data, int len) override {
156 int32_t result = writer_->WriteBytes(data, len);
157 if (result != 0) {
158 error_ = ZipWriter::ErrorCodeString(result);
159 return false;
160 }
161 return true;
162 }
163
FinishEntry()164 bool FinishEntry() override {
165 int32_t result = writer_->FinishEntry();
166 if (result != 0) {
167 error_ = ZipWriter::ErrorCodeString(result);
168 return false;
169 }
170 return true;
171 }
172
WriteFile(const StringPiece & path,uint32_t flags,io::InputStream * in)173 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
HadError() const222 bool HadError() const override { return !error_.empty(); }
223
GetError() const224 std::string GetError() const override { return error_; }
225
~ZipFileWriter()226 virtual ~ZipFileWriter() {
227 if (writer_) {
228 writer_->Finish();
229 }
230 }
231
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_;
237 std::string error_;
238 };
239
240 } // namespace
241
CreateDirectoryArchiveWriter(IDiagnostics * diag,const StringPiece & path)242 std::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());
247 return {};
248 }
249 return std::move(writer);
250 }
251
CreateZipFileArchiveWriter(IDiagnostics * diag,const StringPiece & path)252 std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
253 const StringPiece& path) {
254 std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
255 if (!writer->Open(path)) {
256 diag->Error(DiagMessage(path) << writer->GetError());
257 return {};
258 }
259 return std::move(writer);
260 }
261
262 } // namespace aapt
263