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 "format/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 "android-base/utf8.h"
27 #include "androidfw/StringPiece.h"
28 #include "ziparchive/zip_writer.h"
29 
30 #include "util/Files.h"
31 
32 using ::android::StringPiece;
33 using ::android::base::SystemErrorCodeToString;
34 
35 namespace aapt {
36 
37 namespace {
38 
39 class DirectoryWriter : public IArchiveWriter {
40  public:
41   DirectoryWriter() = default;
42 
Open(const StringPiece & out_dir)43   bool Open(const StringPiece& out_dir) {
44     dir_ = out_dir.to_string();
45     file::FileType type = file::GetFileType(dir_);
46     if (type == file::FileType::kNonexistant) {
47       error_ = "directory does not exist";
48       return false;
49     } else if (type != file::FileType::kDirectory) {
50       error_ = "not a directory";
51       return false;
52     }
53     return true;
54   }
55 
StartEntry(const StringPiece & path,uint32_t flags)56   bool StartEntry(const StringPiece& path, uint32_t flags) override {
57     if (file_) {
58       return false;
59     }
60 
61     std::string full_path = dir_;
62     file::AppendPath(&full_path, path);
63     file::mkdirs(file::GetStem(full_path).to_string());
64 
65     file_ = {::android::base::utf8::fopen(full_path.c_str(), "wb"), fclose};
66     if (!file_) {
67       error_ = SystemErrorCodeToString(errno);
68       return false;
69     }
70     return true;
71   }
72 
Write(const void * data,int len)73   bool Write(const void* data, int len) override {
74     if (!file_) {
75       return false;
76     }
77 
78     if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
79       error_ = SystemErrorCodeToString(errno);
80       file_.reset(nullptr);
81       return false;
82     }
83     return true;
84   }
85 
FinishEntry()86   bool FinishEntry() override {
87     if (!file_) {
88       return false;
89     }
90     file_.reset(nullptr);
91     return true;
92   }
93 
WriteFile(const StringPiece & path,uint32_t flags,io::InputStream * in)94   bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
95     if (!StartEntry(path, flags)) {
96       return false;
97     }
98 
99     const void* data = nullptr;
100     size_t len = 0;
101     while (in->Next(&data, &len)) {
102       if (!Write(data, static_cast<int>(len))) {
103         return false;
104       }
105     }
106 
107     if (in->HadError()) {
108       error_ = in->GetError();
109       return false;
110     }
111 
112     return FinishEntry();
113   }
114 
HadError() const115   bool HadError() const override {
116     return !error_.empty();
117   }
118 
GetError() const119   std::string GetError() const override {
120     return error_;
121   }
122 
123  private:
124   DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
125 
126   std::string dir_;
127   std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
128   std::string error_;
129 };
130 
131 class ZipFileWriter : public IArchiveWriter {
132  public:
133   ZipFileWriter() = default;
134 
Open(const StringPiece & path)135   bool Open(const StringPiece& path) {
136     file_ = {::android::base::utf8::fopen(path.to_string().c_str(), "w+b"), fclose};
137     if (!file_) {
138       error_ = SystemErrorCodeToString(errno);
139       return false;
140     }
141     writer_ = util::make_unique<ZipWriter>(file_.get());
142     return true;
143   }
144 
StartEntry(const StringPiece & path,uint32_t flags)145   bool StartEntry(const StringPiece& path, uint32_t flags) override {
146     if (!writer_) {
147       return false;
148     }
149 
150     size_t zip_flags = 0;
151     if (flags & ArchiveEntry::kCompress) {
152       zip_flags |= ZipWriter::kCompress;
153     }
154 
155     if (flags & ArchiveEntry::kAlign) {
156       zip_flags |= ZipWriter::kAlign32;
157     }
158 
159     int32_t result = writer_->StartEntry(path.data(), zip_flags);
160     if (result != 0) {
161       error_ = ZipWriter::ErrorCodeString(result);
162       return false;
163     }
164     return true;
165   }
166 
Write(const void * data,int len)167   bool Write(const void* data, int len) override {
168     int32_t result = writer_->WriteBytes(data, len);
169     if (result != 0) {
170       error_ = ZipWriter::ErrorCodeString(result);
171       return false;
172     }
173     return true;
174   }
175 
FinishEntry()176   bool FinishEntry() override {
177     int32_t result = writer_->FinishEntry();
178     if (result != 0) {
179       error_ = ZipWriter::ErrorCodeString(result);
180       return false;
181     }
182     return true;
183   }
184 
WriteFile(const StringPiece & path,uint32_t flags,io::InputStream * in)185   bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
186     while (true) {
187       if (!StartEntry(path, flags)) {
188         return false;
189       }
190 
191       const void* data = nullptr;
192       size_t len = 0;
193       while (in->Next(&data, &len)) {
194         if (!Write(data, static_cast<int>(len))) {
195           return false;
196         }
197       }
198 
199       if (in->HadError()) {
200         error_ = in->GetError();
201         return false;
202       }
203 
204       if (!FinishEntry()) {
205         return false;
206       }
207 
208       // Check to see if the file was compressed enough. This is preserving behavior of AAPT.
209       if ((flags & ArchiveEntry::kCompress) != 0 && in->CanRewind()) {
210         ZipWriter::FileEntry last_entry;
211         int32_t result = writer_->GetLastEntry(&last_entry);
212         CHECK(result == 0);
213         if (last_entry.compressed_size + (last_entry.compressed_size / 10) >
214             last_entry.uncompressed_size) {
215           // The file was not compressed enough, rewind and store it uncompressed.
216           if (!in->Rewind()) {
217             // Well we tried, may as well keep what we had.
218             return true;
219           }
220 
221           int32_t result = writer_->DiscardLastEntry();
222           if (result != 0) {
223             error_ = ZipWriter::ErrorCodeString(result);
224             return false;
225           }
226           flags &= ~ArchiveEntry::kCompress;
227 
228           continue;
229         }
230       }
231       return true;
232     }
233   }
234 
HadError() const235   bool HadError() const override {
236     return !error_.empty();
237   }
238 
GetError() const239   std::string GetError() const override {
240     return error_;
241   }
242 
~ZipFileWriter()243   virtual ~ZipFileWriter() {
244     if (writer_) {
245       writer_->Finish();
246     }
247   }
248 
249  private:
250   DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
251 
252   std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
253   std::unique_ptr<ZipWriter> writer_;
254   std::string error_;
255 };
256 
257 }  // namespace
258 
CreateDirectoryArchiveWriter(IDiagnostics * diag,const StringPiece & path)259 std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
260                                                              const StringPiece& path) {
261   std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
262   if (!writer->Open(path)) {
263     diag->Error(DiagMessage(path) << writer->GetError());
264     return {};
265   }
266   return std::move(writer);
267 }
268 
CreateZipFileArchiveWriter(IDiagnostics * diag,const StringPiece & path)269 std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
270                                                            const StringPiece& path) {
271   std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
272   if (!writer->Open(path)) {
273     diag->Error(DiagMessage(path) << writer->GetError());
274     return {};
275   }
276   return std::move(writer);
277 }
278 
279 }  // namespace aapt
280