Home | History | Annotate | Download | only in vold
      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