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