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 "MoveStorage.h" 18 #include "Utils.h" 19 #include "VolumeManager.h" 20 21 #include <android-base/logging.h> 22 #include <android-base/properties.h> 23 #include <android-base/stringprintf.h> 24 #include <hardware_legacy/power.h> 25 #include <private/android_filesystem_config.h> 26 27 #include <thread> 28 29 #include <dirent.h> 30 #include <sys/wait.h> 31 32 #define CONSTRAIN(amount, low, high) ((amount) < (low) ? (low) : ((amount) > (high) ? (high) : (amount))) 33 34 static const char* kPropBlockingExec = "persist.sys.blocking_exec"; 35 36 using android::base::StringPrintf; 37 38 namespace android { 39 namespace vold { 40 41 // TODO: keep in sync with PackageManager 42 static const int kMoveSucceeded = -100; 43 static const int kMoveFailedInternalError = -6; 44 45 static const char* kCpPath = "/system/bin/cp"; 46 static const char* kRmPath = "/system/bin/rm"; 47 48 static const char* kWakeLock = "MoveTask"; 49 50 static void notifyProgress(int progress, 51 const android::sp<android::os::IVoldTaskListener>& listener) { 52 if (listener) { 53 android::os::PersistableBundle extras; 54 listener->onStatus(progress, extras); 55 } 56 } 57 58 static status_t pushBackContents(const std::string& path, std::vector<std::string>& cmd, 59 bool addWildcard) { 60 DIR* dir = opendir(path.c_str()); 61 if (dir == NULL) { 62 return -1; 63 } 64 bool found = false; 65 struct dirent* ent; 66 while ((ent = readdir(dir)) != NULL) { 67 if ((!strcmp(ent->d_name, ".")) || (!strcmp(ent->d_name, ".."))) { 68 continue; 69 } 70 if (addWildcard) { 71 cmd.push_back(StringPrintf("%s/%s/*", path.c_str(), ent->d_name)); 72 } else { 73 cmd.push_back(StringPrintf("%s/%s", path.c_str(), ent->d_name)); 74 } 75 found = true; 76 } 77 closedir(dir); 78 return found ? OK : -1; 79 } 80 81 static status_t execRm(const std::string& path, int startProgress, int stepProgress, 82 const android::sp<android::os::IVoldTaskListener>& listener) { 83 notifyProgress(startProgress, listener); 84 85 uint64_t expectedBytes = GetTreeBytes(path); 86 uint64_t startFreeBytes = GetFreeBytes(path); 87 88 std::vector<std::string> cmd; 89 cmd.push_back(kRmPath); 90 cmd.push_back("-f"); /* force: remove without confirmation, no error if it doesn't exist */ 91 cmd.push_back("-R"); /* recursive: remove directory contents */ 92 if (pushBackContents(path, cmd, true) != OK) { 93 LOG(WARNING) << "No contents in " << path; 94 return OK; 95 } 96 97 if (android::base::GetBoolProperty(kPropBlockingExec, false)) { 98 return ForkExecvp(cmd); 99 } 100 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), listener); 119 } 120 return -1; 121 } 122 123 static status_t execCp(const std::string& fromPath, const std::string& toPath, int startProgress, 124 int stepProgress, const android::sp<android::os::IVoldTaskListener>& listener) { 125 notifyProgress(startProgress, listener); 126 127 uint64_t expectedBytes = GetTreeBytes(fromPath); 128 uint64_t startFreeBytes = GetFreeBytes(toPath); 129 130 if (expectedBytes > startFreeBytes) { 131 LOG(ERROR) << "Data size " << expectedBytes << " is too large to fit in free space " 132 << startFreeBytes; 133 return -1; 134 } 135 136 std::vector<std::string> cmd; 137 cmd.push_back(kCpPath); 138 cmd.push_back("-p"); /* preserve timestamps, ownership, and permissions */ 139 cmd.push_back("-R"); /* recurse into subdirectories (DEST must be a directory) */ 140 cmd.push_back("-P"); /* Do not follow symlinks [default] */ 141 cmd.push_back("-d"); /* don't dereference symlinks */ 142 if (pushBackContents(fromPath, cmd, false) != OK) { 143 LOG(WARNING) << "No contents in " << fromPath; 144 return OK; 145 } 146 cmd.push_back(toPath.c_str()); 147 148 if (android::base::GetBoolProperty(kPropBlockingExec, false)) { 149 return ForkExecvp(cmd); 150 } 151 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), listener); 170 } 171 return -1; 172 } 173 174 static void bringOffline(const std::shared_ptr<VolumeBase>& vol) { 175 vol->destroy(); 176 vol->setSilent(true); 177 vol->create(); 178 vol->setMountFlags(0); 179 vol->mount(); 180 } 181 182 static void bringOnline(const std::shared_ptr<VolumeBase>& vol) { 183 vol->destroy(); 184 vol->setSilent(false); 185 vol->create(); 186 } 187 188 static status_t moveStorageInternal(const std::shared_ptr<VolumeBase>& from, 189 const std::shared_ptr<VolumeBase>& to, 190 const android::sp<android::os::IVoldTaskListener>& listener) { 191 std::string fromPath; 192 std::string toPath; 193 194 // TODO: add support for public volumes 195 if (from->getType() != VolumeBase::Type::kEmulated) goto fail; 196 if (to->getType() != VolumeBase::Type::kEmulated) goto fail; 197 198 // Step 1: tear down volumes and mount silently without making 199 // visible to userspace apps 200 { 201 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); 202 bringOffline(from); 203 bringOffline(to); 204 } 205 206 fromPath = from->getInternalPath(); 207 toPath = to->getInternalPath(); 208 209 // Step 2: clean up any stale data 210 if (execRm(toPath, 10, 10, listener) != OK) { 211 goto fail; 212 } 213 214 // Step 3: perform actual copy 215 if (execCp(fromPath, toPath, 20, 60, listener) != OK) { 216 goto copy_fail; 217 } 218 219 // NOTE: MountService watches for this magic value to know 220 // that move was successful 221 notifyProgress(82, listener); 222 { 223 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); 224 bringOnline(from); 225 bringOnline(to); 226 } 227 228 // Step 4: clean up old data 229 if (execRm(fromPath, 85, 15, listener) != OK) { 230 goto fail; 231 } 232 233 notifyProgress(kMoveSucceeded, listener); 234 return OK; 235 236 copy_fail: 237 // if we failed to copy the data we should not leave it laying around 238 // in target location. Do not check return value, we can not do any 239 // useful anyway. 240 execRm(toPath, 80, 1, listener); 241 fail: 242 { 243 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); 244 bringOnline(from); 245 bringOnline(to); 246 } 247 notifyProgress(kMoveFailedInternalError, listener); 248 return -1; 249 } 250 251 void MoveStorage(const std::shared_ptr<VolumeBase>& from, const std::shared_ptr<VolumeBase>& to, 252 const android::sp<android::os::IVoldTaskListener>& listener) { 253 acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock); 254 255 android::os::PersistableBundle extras; 256 status_t res = moveStorageInternal(from, to, listener); 257 if (listener) { 258 listener->onFinished(res, extras); 259 } 260 261 release_wake_lock(kWakeLock); 262 } 263 264 } // namespace vold 265 } // namespace android 266