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 "MoveTask.h" 18#include "Utils.h" 19#include "VolumeManager.h" 20#include "ResponseCode.h" 21 22#include <android-base/stringprintf.h> 23#include <android-base/logging.h> 24#include <private/android_filesystem_config.h> 25#include <hardware_legacy/power.h> 26 27#include <dirent.h> 28#include <sys/wait.h> 29 30#define CONSTRAIN(amount, low, high) (amount < low ? low : (amount > high ? high : amount)) 31 32using android::base::StringPrintf; 33 34namespace android { 35namespace vold { 36 37// TODO: keep in sync with PackageManager 38static const int kMoveSucceeded = -100; 39static const int kMoveFailedInternalError = -6; 40 41static const char* kCpPath = "/system/bin/cp"; 42static const char* kRmPath = "/system/bin/rm"; 43 44static const char* kWakeLock = "MoveTask"; 45 46MoveTask::MoveTask(const std::shared_ptr<VolumeBase>& from, 47 const std::shared_ptr<VolumeBase>& to) : 48 mFrom(from), mTo(to) { 49} 50 51MoveTask::~MoveTask() { 52} 53 54void MoveTask::start() { 55 mThread = std::thread(&MoveTask::run, this); 56} 57 58static void notifyProgress(int progress) { 59 VolumeManager::Instance()->getBroadcaster()->sendBroadcast(ResponseCode::MoveStatus, 60 StringPrintf("%d", progress).c_str(), false); 61} 62 63static status_t pushBackContents(const std::string& path, std::vector<std::string>& cmd) { 64 DIR* dir = opendir(path.c_str()); 65 if (dir == NULL) { 66 return -1; 67 } 68 bool found = false; 69 struct dirent* ent; 70 while ((ent = readdir(dir)) != NULL) { 71 if ((!strcmp(ent->d_name, ".")) || (!strcmp(ent->d_name, ".."))) { 72 continue; 73 } 74 cmd.push_back(StringPrintf("%s/%s", path.c_str(), ent->d_name)); 75 found = true; 76 } 77 closedir(dir); 78 return found ? OK : -1; 79} 80 81static status_t execRm(const std::string& path, int startProgress, int stepProgress) { 82 notifyProgress(startProgress); 83 84 uint64_t expectedBytes = GetTreeBytes(path); 85 uint64_t startFreeBytes = GetFreeBytes(path); 86 87 std::vector<std::string> cmd; 88 cmd.push_back(kRmPath); 89 cmd.push_back("-f"); /* force: remove without confirmation, no error if it doesn't exist */ 90 cmd.push_back("-R"); /* recursive: remove directory contents */ 91 if (pushBackContents(path, cmd) != OK) { 92 LOG(WARNING) << "No contents in " << path; 93 return OK; 94 } 95 96 pid_t pid = ForkExecvpAsync(cmd); 97 if (pid == -1) return -1; 98 99 int status; 100 while (true) { 101 if (waitpid(pid, &status, WNOHANG) == pid) { 102 if (WIFEXITED(status)) { 103 LOG(DEBUG) << "Finished rm with status " << WEXITSTATUS(status); 104 return (WEXITSTATUS(status) == 0) ? OK : -1; 105 } else { 106 break; 107 } 108 } 109 110 sleep(1); 111 uint64_t deltaFreeBytes = GetFreeBytes(path) - startFreeBytes; 112 notifyProgress(startProgress + CONSTRAIN((int) 113 ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress)); 114 } 115 return -1; 116} 117 118static status_t execCp(const std::string& fromPath, const std::string& toPath, 119 int startProgress, int stepProgress) { 120 notifyProgress(startProgress); 121 122 uint64_t expectedBytes = GetTreeBytes(fromPath); 123 uint64_t startFreeBytes = GetFreeBytes(toPath); 124 125 std::vector<std::string> cmd; 126 cmd.push_back(kCpPath); 127 cmd.push_back("-p"); /* preserve timestamps, ownership, and permissions */ 128 cmd.push_back("-R"); /* recurse into subdirectories (DEST must be a directory) */ 129 cmd.push_back("-P"); /* Do not follow symlinks [default] */ 130 cmd.push_back("-d"); /* don't dereference symlinks */ 131 if (pushBackContents(fromPath, cmd) != OK) { 132 LOG(WARNING) << "No contents in " << fromPath; 133 return OK; 134 } 135 cmd.push_back(toPath.c_str()); 136 137 pid_t pid = ForkExecvpAsync(cmd); 138 if (pid == -1) return -1; 139 140 int status; 141 while (true) { 142 if (waitpid(pid, &status, WNOHANG) == pid) { 143 if (WIFEXITED(status)) { 144 LOG(DEBUG) << "Finished cp with status " << WEXITSTATUS(status); 145 return (WEXITSTATUS(status) == 0) ? OK : -1; 146 } else { 147 break; 148 } 149 } 150 151 sleep(1); 152 uint64_t deltaFreeBytes = startFreeBytes - GetFreeBytes(toPath); 153 notifyProgress(startProgress + CONSTRAIN((int) 154 ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress)); 155 } 156 return -1; 157} 158 159static void bringOffline(const std::shared_ptr<VolumeBase>& vol) { 160 vol->destroy(); 161 vol->setSilent(true); 162 vol->create(); 163 vol->setMountFlags(0); 164 vol->mount(); 165} 166 167static void bringOnline(const std::shared_ptr<VolumeBase>& vol) { 168 vol->destroy(); 169 vol->setSilent(false); 170 vol->create(); 171} 172 173void MoveTask::run() { 174 acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock); 175 176 std::string fromPath; 177 std::string toPath; 178 179 // TODO: add support for public volumes 180 if (mFrom->getType() != VolumeBase::Type::kEmulated) goto fail; 181 if (mTo->getType() != VolumeBase::Type::kEmulated) goto fail; 182 183 // Step 1: tear down volumes and mount silently without making 184 // visible to userspace apps 185 { 186 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); 187 bringOffline(mFrom); 188 bringOffline(mTo); 189 } 190 191 fromPath = mFrom->getInternalPath(); 192 toPath = mTo->getInternalPath(); 193 194 // Step 2: clean up any stale data 195 if (execRm(toPath, 10, 10) != OK) { 196 goto fail; 197 } 198 199 // Step 3: perform actual copy 200 if (execCp(fromPath, toPath, 20, 60) != OK) { 201 goto copy_fail; 202 } 203 204 // NOTE: MountService watches for this magic value to know 205 // that move was successful 206 notifyProgress(82); 207 { 208 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); 209 bringOnline(mFrom); 210 bringOnline(mTo); 211 } 212 213 // Step 4: clean up old data 214 if (execRm(fromPath, 85, 15) != OK) { 215 goto fail; 216 } 217 218 notifyProgress(kMoveSucceeded); 219 release_wake_lock(kWakeLock); 220 return; 221 222copy_fail: 223 // if we failed to copy the data we should not leave it laying around 224 // in target location. Do not check return value, we can not do any 225 // useful anyway. 226 execRm(toPath, 80, 1); 227fail: 228 { 229 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); 230 bringOnline(mFrom); 231 bringOnline(mTo); 232 } 233 notifyProgress(kMoveFailedInternalError); 234 release_wake_lock(kWakeLock); 235 return; 236} 237 238} // namespace vold 239} // namespace android 240