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 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 { 110 return !error_.empty(); 111 } 112 113 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 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 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 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 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 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 228 bool HadError() const override { 229 return !error_.empty(); 230 } 231 232 std::string GetError() const override { 233 return error_; 234 } 235 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 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 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