1 /* 2 * Copyright (C) 2016 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 "ZipUtil.h" 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <utime.h> 22 23 #include <string> 24 25 #include <android-base/logging.h> 26 #include <android-base/unique_fd.h> 27 #include <selinux/label.h> 28 #include <selinux/selinux.h> 29 #include <ziparchive/zip_archive.h> 30 31 #include "DirUtil.h" 32 33 static constexpr mode_t UNZIP_DIRMODE = 0755; 34 static constexpr mode_t UNZIP_FILEMODE = 0644; 35 36 bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path, 37 const std::string& dest_path, const struct utimbuf* timestamp, 38 struct selabel_handle* sehnd) { 39 if (!zip_path.empty() && zip_path[0] == '/') { 40 LOG(ERROR) << "ExtractPackageRecursive(): zip_path must be a relative path " << zip_path; 41 return false; 42 } 43 if (dest_path.empty() || dest_path[0] != '/') { 44 LOG(ERROR) << "ExtractPackageRecursive(): dest_path must be an absolute path " << dest_path; 45 return false; 46 } 47 48 void* cookie; 49 std::string target_dir(dest_path); 50 if (dest_path.back() != '/') { 51 target_dir += '/'; 52 } 53 std::string prefix_path(zip_path); 54 if (!zip_path.empty() && zip_path.back() != '/') { 55 prefix_path += '/'; 56 } 57 const ZipString zip_prefix(prefix_path.c_str()); 58 59 int ret = StartIteration(zip, &cookie, &zip_prefix, nullptr); 60 if (ret != 0) { 61 LOG(ERROR) << "failed to start iterating zip entries."; 62 return false; 63 } 64 65 std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration); 66 ZipEntry entry; 67 ZipString name; 68 int extractCount = 0; 69 while (Next(cookie, &entry, &name) == 0) { 70 std::string entry_name(name.name, name.name + name.name_length); 71 CHECK_LE(prefix_path.size(), entry_name.size()); 72 std::string path = target_dir + entry_name.substr(prefix_path.size()); 73 // Skip dir. 74 if (path.back() == '/') { 75 continue; 76 } 77 //TODO(b/31917448) handle the symlink. 78 79 if (dirCreateHierarchy(path.c_str(), UNZIP_DIRMODE, timestamp, true, sehnd) != 0) { 80 LOG(ERROR) << "failed to create dir for " << path; 81 return false; 82 } 83 84 char *secontext = NULL; 85 if (sehnd) { 86 selabel_lookup(sehnd, &secontext, path.c_str(), UNZIP_FILEMODE); 87 setfscreatecon(secontext); 88 } 89 android::base::unique_fd fd(open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, UNZIP_FILEMODE)); 90 if (fd == -1) { 91 PLOG(ERROR) << "Can't create target file \"" << path << "\""; 92 return false; 93 } 94 if (secontext) { 95 freecon(secontext); 96 setfscreatecon(NULL); 97 } 98 99 int err = ExtractEntryToFile(zip, &entry, fd); 100 if (err != 0) { 101 LOG(ERROR) << "Error extracting \"" << path << "\" : " << ErrorCodeString(err); 102 return false; 103 } 104 105 if (fsync(fd) != 0) { 106 PLOG(ERROR) << "Error syncing file descriptor when extracting \"" << path << "\""; 107 return false; 108 } 109 110 if (timestamp != nullptr && utime(path.c_str(), timestamp)) { 111 PLOG(ERROR) << "Error touching \"" << path << "\""; 112 return false; 113 } 114 115 LOG(INFO) << "Extracted file \"" << path << "\""; 116 ++extractCount; 117 } 118 119 LOG(INFO) << "Extracted " << extractCount << " file(s)"; 120 return true; 121 } 122