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