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 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 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 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 84 bool FinishEntry() override { 85 if (!file_) { 86 return false; 87 } 88 file_.reset(nullptr); 89 return true; 90 } 91 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 107 bool HadError() const override { return !error_.empty(); } 108 109 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 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 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 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 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 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 222 bool HadError() const override { return !error_.empty(); } 223 224 std::string GetError() const override { return error_; } 225 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 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 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