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