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     return !in->HadError();
107   }
108 
HadError() const109   bool HadError() const override {
110     return !error_.empty();
111   }
112 
GetError() const113   std::string GetError() const override {
114     return error_;
115   }
116 
117  private:
118   DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
119 
120   std::string dir_;
121   std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
122   std::string error_;
123 };
124 
125 class ZipFileWriter : public IArchiveWriter {
126  public:
127   ZipFileWriter() = default;
128 
Open(const StringPiece & path)129   bool Open(const StringPiece& path) {
130     file_ = {::android::base::utf8::fopen(path.to_string().c_str(), "w+b"), fclose};
131     if (!file_) {
132       error_ = SystemErrorCodeToString(errno);
133       return false;
134     }
135     writer_ = util::make_unique<ZipWriter>(file_.get());
136     return true;
137   }
138 
StartEntry(const StringPiece & path,uint32_t flags)139   bool StartEntry(const StringPiece& path, uint32_t flags) override {
140     if (!writer_) {
141       return false;
142     }
143 
144     size_t zip_flags = 0;
145     if (flags & ArchiveEntry::kCompress) {
146       zip_flags |= ZipWriter::kCompress;
147     }
148 
149     if (flags & ArchiveEntry::kAlign) {
150       zip_flags |= ZipWriter::kAlign32;
151     }
152 
153     int32_t result = writer_->StartEntry(path.data(), zip_flags);
154     if (result != 0) {
155       error_ = ZipWriter::ErrorCodeString(result);
156       return false;
157     }
158     return true;
159   }
160 
Write(const void * data,int len)161   bool Write(const void* data, int len) override {
162     int32_t result = writer_->WriteBytes(data, len);
163     if (result != 0) {
164       error_ = ZipWriter::ErrorCodeString(result);
165       return false;
166     }
167     return true;
168   }
169 
FinishEntry()170   bool FinishEntry() override {
171     int32_t result = writer_->FinishEntry();
172     if (result != 0) {
173       error_ = ZipWriter::ErrorCodeString(result);
174       return false;
175     }
176     return true;
177   }
178 
WriteFile(const StringPiece & path,uint32_t flags,io::InputStream * in)179   bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
180     while (true) {
181       if (!StartEntry(path, flags)) {
182         return false;
183       }
184 
185       const void* data = nullptr;
186       size_t len = 0;
187       while (in->Next(&data, &len)) {
188         if (!Write(data, static_cast<int>(len))) {
189           return false;
190         }
191       }
192 
193       if (in->HadError()) {
194         return false;
195       }
196 
197       if (!FinishEntry()) {
198         return false;
199       }
200 
201       // Check to see if the file was compressed enough. This is preserving behavior of AAPT.
202       if ((flags & ArchiveEntry::kCompress) != 0 && in->CanRewind()) {
203         ZipWriter::FileEntry last_entry;
204         int32_t result = writer_->GetLastEntry(&last_entry);
205         CHECK(result == 0);
206         if (last_entry.compressed_size + (last_entry.compressed_size / 10) >
207             last_entry.uncompressed_size) {
208           // The file was not compressed enough, rewind and store it uncompressed.
209           if (!in->Rewind()) {
210             // Well we tried, may as well keep what we had.
211             return true;
212           }
213 
214           int32_t result = writer_->DiscardLastEntry();
215           if (result != 0) {
216             error_ = ZipWriter::ErrorCodeString(result);
217             return false;
218           }
219           flags &= ~ArchiveEntry::kCompress;
220 
221           continue;
222         }
223       }
224       return true;
225     }
226   }
227 
HadError() const228   bool HadError() const override {
229     return !error_.empty();
230   }
231 
GetError() const232   std::string GetError() const override {
233     return error_;
234   }
235 
~ZipFileWriter()236   virtual ~ZipFileWriter() {
237     if (writer_) {
238       writer_->Finish();
239     }
240   }
241 
242  private:
243   DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
244 
245   std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
246   std::unique_ptr<ZipWriter> writer_;
247   std::string error_;
248 };
249 
250 }  // namespace
251 
CreateDirectoryArchiveWriter(IDiagnostics * diag,const StringPiece & path)252 std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
253                                                              const StringPiece& path) {
254   std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
255   if (!writer->Open(path)) {
256     diag->Error(DiagMessage(path) << writer->GetError());
257     return {};
258   }
259   return std::move(writer);
260 }
261 
CreateZipFileArchiveWriter(IDiagnostics * diag,const StringPiece & path)262 std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
263                                                            const StringPiece& path) {
264   std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
265   if (!writer->Open(path)) {
266     diag->Error(DiagMessage(path) << writer->GetError());
267     return {};
268   }
269   return std::move(writer);
270 }
271 
272 }  // namespace aapt
273