Home | History | Annotate | Download | only in vold
      1 /*
      2  * Copyright (C) 2008 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 <stdlib.h>
     18 #include <sys/mount.h>
     19 #include <sys/socket.h>
     20 #include <sys/stat.h>
     21 #include <sys/types.h>
     22 #include <sys/wait.h>
     23 #include <netinet/in.h>
     24 #include <arpa/inet.h>
     25 #include <dirent.h>
     26 #include <errno.h>
     27 #include <fcntl.h>
     28 #include <fs_mgr.h>
     29 #include <stdio.h>
     30 #include <string.h>
     31 #include <stdint.h>
     32 #include <inttypes.h>
     33 #include <ctype.h>
     34 
     35 #define LOG_TAG "VoldCmdListener"
     36 
     37 #include <android-base/logging.h>
     38 #include <android-base/stringprintf.h>
     39 #include <cutils/fs.h>
     40 
     41 #include <sysutils/SocketClient.h>
     42 #include <private/android_filesystem_config.h>
     43 
     44 #include "CommandListener.h"
     45 #include "VolumeManager.h"
     46 #include "VolumeBase.h"
     47 #include "ResponseCode.h"
     48 #include "Process.h"
     49 #include "Loop.h"
     50 #include "Devmapper.h"
     51 #include "MoveTask.h"
     52 #include "TrimTask.h"
     53 
     54 #define DUMP_ARGS 0
     55 #define DEBUG_APPFUSE 0
     56 
     57 CommandListener::CommandListener() :
     58                  FrameworkListener("vold", true) {
     59     registerCmd(new DumpCmd());
     60     registerCmd(new VolumeCmd());
     61     registerCmd(new AsecCmd());
     62     registerCmd(new ObbCmd());
     63     registerCmd(new StorageCmd());
     64     registerCmd(new FstrimCmd());
     65     registerCmd(new AppFuseCmd());
     66 }
     67 
     68 #if DUMP_ARGS
     69 void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
     70     char buffer[4096];
     71     char *p = buffer;
     72 
     73     memset(buffer, 0, sizeof(buffer));
     74     int i;
     75     for (i = 0; i < argc; i++) {
     76         unsigned int len = strlen(argv[i]) + 1; // Account for space
     77         if (i == argObscure) {
     78             len += 2; // Account for {}
     79         }
     80         if (((p - buffer) + len) < (sizeof(buffer)-1)) {
     81             if (i == argObscure) {
     82                 *p++ = '{';
     83                 *p++ = '}';
     84                 *p++ = ' ';
     85                 continue;
     86             }
     87             strcpy(p, argv[i]);
     88             p+= strlen(argv[i]);
     89             if (i != (argc -1)) {
     90                 *p++ = ' ';
     91             }
     92         }
     93     }
     94     SLOGD("%s", buffer);
     95 }
     96 #else
     97 void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
     98 #endif
     99 
    100 int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
    101     if (!cond) {
    102         return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
    103     } else {
    104         return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
    105     }
    106 }
    107 
    108 CommandListener::DumpCmd::DumpCmd() :
    109                  VoldCommand("dump") {
    110 }
    111 
    112 int CommandListener::DumpCmd::runCommand(SocketClient *cli,
    113                                          int /*argc*/, char ** /*argv*/) {
    114     cli->sendMsg(0, "Dumping loop status", false);
    115     if (Loop::dumpState(cli)) {
    116         cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
    117     }
    118     cli->sendMsg(0, "Dumping DM status", false);
    119     if (Devmapper::dumpState(cli)) {
    120         cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
    121     }
    122     cli->sendMsg(0, "Dumping mounted filesystems", false);
    123     FILE *fp = fopen("/proc/mounts", "r");
    124     if (fp) {
    125         char line[1024];
    126         while (fgets(line, sizeof(line), fp)) {
    127             line[strlen(line)-1] = '\0';
    128             cli->sendMsg(0, line, false);;
    129         }
    130         fclose(fp);
    131     }
    132 
    133     cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
    134     return 0;
    135 }
    136 
    137 CommandListener::VolumeCmd::VolumeCmd() :
    138                  VoldCommand("volume") {
    139 }
    140 
    141 int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
    142                                            int argc, char **argv) {
    143     dumpArgs(argc, argv, -1);
    144 
    145     if (argc < 2) {
    146         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    147         return 0;
    148     }
    149 
    150     VolumeManager *vm = VolumeManager::Instance();
    151     std::lock_guard<std::mutex> lock(vm->getLock());
    152 
    153     // TODO: tease out methods not directly related to volumes
    154 
    155     std::string cmd(argv[1]);
    156     if (cmd == "reset") {
    157         return sendGenericOkFail(cli, vm->reset());
    158 
    159     } else if (cmd == "shutdown") {
    160         return sendGenericOkFail(cli, vm->shutdown());
    161 
    162     } else if (cmd == "debug") {
    163         return sendGenericOkFail(cli, vm->setDebug(true));
    164 
    165     } else if (cmd == "partition" && argc > 3) {
    166         // partition [diskId] [public|private|mixed] [ratio]
    167         std::string id(argv[2]);
    168         auto disk = vm->findDisk(id);
    169         if (disk == nullptr) {
    170             return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
    171         }
    172 
    173         std::string type(argv[3]);
    174         if (type == "public") {
    175             return sendGenericOkFail(cli, disk->partitionPublic());
    176         } else if (type == "private") {
    177             return sendGenericOkFail(cli, disk->partitionPrivate());
    178         } else if (type == "mixed") {
    179             if (argc < 4) {
    180                 return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
    181             }
    182             int frac = atoi(argv[4]);
    183             return sendGenericOkFail(cli, disk->partitionMixed(frac));
    184         } else {
    185             return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
    186         }
    187 
    188     } else if (cmd == "mkdirs" && argc > 2) {
    189         // mkdirs [path]
    190         return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
    191 
    192     } else if (cmd == "user_added" && argc > 3) {
    193         // user_added [user] [serial]
    194         return sendGenericOkFail(cli, vm->onUserAdded(atoi(argv[2]), atoi(argv[3])));
    195 
    196     } else if (cmd == "user_removed" && argc > 2) {
    197         // user_removed [user]
    198         return sendGenericOkFail(cli, vm->onUserRemoved(atoi(argv[2])));
    199 
    200     } else if (cmd == "user_started" && argc > 2) {
    201         // user_started [user]
    202         return sendGenericOkFail(cli, vm->onUserStarted(atoi(argv[2])));
    203 
    204     } else if (cmd == "user_stopped" && argc > 2) {
    205         // user_stopped [user]
    206         return sendGenericOkFail(cli, vm->onUserStopped(atoi(argv[2])));
    207 
    208     } else if (cmd == "mount" && argc > 2) {
    209         // mount [volId] [flags] [user]
    210         std::string id(argv[2]);
    211         auto vol = vm->findVolume(id);
    212         if (vol == nullptr) {
    213             return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
    214         }
    215 
    216         int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
    217         userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
    218 
    219         vol->setMountFlags(mountFlags);
    220         vol->setMountUserId(mountUserId);
    221 
    222         int res = vol->mount();
    223         if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
    224             vm->setPrimary(vol);
    225         }
    226         return sendGenericOkFail(cli, res);
    227 
    228     } else if (cmd == "unmount" && argc > 2) {
    229         // unmount [volId]
    230         std::string id(argv[2]);
    231         auto vol = vm->findVolume(id);
    232         if (vol == nullptr) {
    233             return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
    234         }
    235 
    236         return sendGenericOkFail(cli, vol->unmount());
    237 
    238     } else if (cmd == "format" && argc > 3) {
    239         // format [volId] [fsType|auto]
    240         std::string id(argv[2]);
    241         std::string fsType(argv[3]);
    242         auto vol = vm->findVolume(id);
    243         if (vol == nullptr) {
    244             return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
    245         }
    246 
    247         return sendGenericOkFail(cli, vol->format(fsType));
    248 
    249     } else if (cmd == "move_storage" && argc > 3) {
    250         // move_storage [fromVolId] [toVolId]
    251         auto fromVol = vm->findVolume(std::string(argv[2]));
    252         auto toVol = vm->findVolume(std::string(argv[3]));
    253         if (fromVol == nullptr || toVol == nullptr) {
    254             return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
    255         }
    256 
    257         (new android::vold::MoveTask(fromVol, toVol))->start();
    258         return sendGenericOkFail(cli, 0);
    259 
    260     } else if (cmd == "benchmark" && argc > 2) {
    261         // benchmark [volId]
    262         std::string id(argv[2]);
    263         nsecs_t res = vm->benchmarkPrivate(id);
    264         return cli->sendMsg(ResponseCode::CommandOkay,
    265                 android::base::StringPrintf("%" PRId64, res).c_str(), false);
    266 
    267     } else if (cmd == "forget_partition" && argc > 2) {
    268         // forget_partition [partGuid]
    269         std::string partGuid(argv[2]);
    270         return sendGenericOkFail(cli, vm->forgetPartition(partGuid));
    271 
    272     } else if (cmd == "remount_uid" && argc > 3) {
    273         // remount_uid [uid] [none|default|read|write]
    274         uid_t uid = atoi(argv[2]);
    275         std::string mode(argv[3]);
    276         return sendGenericOkFail(cli, vm->remountUid(uid, mode));
    277     }
    278 
    279     return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
    280 }
    281 
    282 CommandListener::StorageCmd::StorageCmd() :
    283                  VoldCommand("storage") {
    284 }
    285 
    286 int CommandListener::StorageCmd::runCommand(SocketClient *cli,
    287                                                       int argc, char **argv) {
    288     /* Guarantied to be initialized by vold's main() before the CommandListener is active */
    289     extern struct fstab *fstab;
    290 
    291     dumpArgs(argc, argv, -1);
    292 
    293     if (argc < 2) {
    294         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    295         return 0;
    296     }
    297 
    298     if (!strcmp(argv[1], "mountall")) {
    299         if (argc != 2) {
    300             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
    301             return 0;
    302         }
    303         fs_mgr_mount_all(fstab);
    304         cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
    305         return 0;
    306     }
    307     if (!strcmp(argv[1], "users")) {
    308         DIR *dir;
    309         struct dirent *de;
    310 
    311         if (argc < 3) {
    312             cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument: user <mountpoint>", false);
    313             return 0;
    314         }
    315         if (!(dir = opendir("/proc"))) {
    316             cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
    317             return 0;
    318         }
    319 
    320         while ((de = readdir(dir))) {
    321             int pid = Process::getPid(de->d_name);
    322 
    323             if (pid < 0) {
    324                 continue;
    325             }
    326 
    327             char processName[255];
    328             Process::getProcessName(pid, processName, sizeof(processName));
    329 
    330             if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
    331                 Process::checkFileMaps(pid, argv[2]) ||
    332                 Process::checkSymLink(pid, argv[2], "cwd") ||
    333                 Process::checkSymLink(pid, argv[2], "root") ||
    334                 Process::checkSymLink(pid, argv[2], "exe")) {
    335 
    336                 char msg[1024];
    337                 snprintf(msg, sizeof(msg), "%d %s", pid, processName);
    338                 cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
    339             }
    340         }
    341         closedir(dir);
    342         cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
    343     } else {
    344         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
    345     }
    346     return 0;
    347 }
    348 
    349 CommandListener::AsecCmd::AsecCmd() :
    350                  VoldCommand("asec") {
    351 }
    352 
    353 void CommandListener::AsecCmd::listAsecsInDirectory(SocketClient *cli, const char *directory) {
    354     DIR *d = opendir(directory);
    355 
    356     if (!d) {
    357         cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
    358         return;
    359     }
    360 
    361     size_t dirent_len = offsetof(struct dirent, d_name) +
    362             fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
    363 
    364     struct dirent *dent = (struct dirent *) malloc(dirent_len);
    365     if (dent == NULL) {
    366         cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", true);
    367         return;
    368     }
    369 
    370     struct dirent *result;
    371 
    372     while (!readdir_r(d, dent, &result) && result != NULL) {
    373         if (dent->d_name[0] == '.')
    374             continue;
    375         if (dent->d_type != DT_REG)
    376             continue;
    377         size_t name_len = strlen(dent->d_name);
    378         if (name_len > 5 && name_len < 260 &&
    379                 !strcmp(&dent->d_name[name_len - 5], ".asec")) {
    380             char id[255];
    381             memset(id, 0, sizeof(id));
    382             strlcpy(id, dent->d_name, name_len - 4);
    383             cli->sendMsg(ResponseCode::AsecListResult, id, false);
    384         }
    385     }
    386     closedir(d);
    387 
    388     free(dent);
    389 }
    390 
    391 int CommandListener::AsecCmd::runCommand(SocketClient *cli,
    392                                                       int argc, char **argv) {
    393     if (argc < 2) {
    394         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    395         return 0;
    396     }
    397 
    398     VolumeManager *vm = VolumeManager::Instance();
    399     int rc = 0;
    400 
    401     if (!strcmp(argv[1], "list")) {
    402         dumpArgs(argc, argv, -1);
    403 
    404         listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_EXT);
    405         listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_INT);
    406     } else if (!strcmp(argv[1], "create")) {
    407         dumpArgs(argc, argv, 5);
    408         if (argc != 8) {
    409             cli->sendMsg(ResponseCode::CommandSyntaxError,
    410                     "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid> "
    411                     "<isExternal>", false);
    412             return 0;
    413         }
    414 
    415         unsigned long numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
    416         const bool isExternal = (atoi(argv[7]) == 1);
    417         rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
    418     } else if (!strcmp(argv[1], "resize")) {
    419         dumpArgs(argc, argv, -1);
    420         if (argc != 5) {
    421             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec resize <container-id> <size_mb> <key>", false);
    422             return 0;
    423         }
    424         unsigned long numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
    425         rc = vm->resizeAsec(argv[2], numSectors, argv[4]);
    426     } else if (!strcmp(argv[1], "finalize")) {
    427         dumpArgs(argc, argv, -1);
    428         if (argc != 3) {
    429             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
    430             return 0;
    431         }
    432         rc = vm->finalizeAsec(argv[2]);
    433     } else if (!strcmp(argv[1], "fixperms")) {
    434         dumpArgs(argc, argv, -1);
    435         if  (argc != 5) {
    436             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
    437             return 0;
    438         }
    439 
    440         char *endptr;
    441         gid_t gid = (gid_t) strtoul(argv[3], &endptr, 10);
    442         if (*endptr != '\0') {
    443             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
    444             return 0;
    445         }
    446 
    447         rc = vm->fixupAsecPermissions(argv[2], gid, argv[4]);
    448     } else if (!strcmp(argv[1], "destroy")) {
    449         dumpArgs(argc, argv, -1);
    450         if (argc < 3) {
    451             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
    452             return 0;
    453         }
    454         bool force = false;
    455         if (argc > 3 && !strcmp(argv[3], "force")) {
    456             force = true;
    457         }
    458         rc = vm->destroyAsec(argv[2], force);
    459     } else if (!strcmp(argv[1], "mount")) {
    460         dumpArgs(argc, argv, 3);
    461         if (argc != 6) {
    462             cli->sendMsg(ResponseCode::CommandSyntaxError,
    463                     "Usage: asec mount <namespace-id> <key> <ownerUid> <ro|rw>", false);
    464             return 0;
    465         }
    466         bool readOnly = true;
    467         if (!strcmp(argv[5], "rw")) {
    468             readOnly = false;
    469         }
    470         rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]), readOnly);
    471     } else if (!strcmp(argv[1], "unmount")) {
    472         dumpArgs(argc, argv, -1);
    473         if (argc < 3) {
    474             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
    475             return 0;
    476         }
    477         bool force = false;
    478         if (argc > 3 && !strcmp(argv[3], "force")) {
    479             force = true;
    480         }
    481         rc = vm->unmountAsec(argv[2], force);
    482     } else if (!strcmp(argv[1], "rename")) {
    483         dumpArgs(argc, argv, -1);
    484         if (argc != 4) {
    485             cli->sendMsg(ResponseCode::CommandSyntaxError,
    486                     "Usage: asec rename <old_id> <new_id>", false);
    487             return 0;
    488         }
    489         rc = vm->renameAsec(argv[2], argv[3]);
    490     } else if (!strcmp(argv[1], "path")) {
    491         dumpArgs(argc, argv, -1);
    492         if (argc != 3) {
    493             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
    494             return 0;
    495         }
    496         char path[255];
    497 
    498         if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
    499             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    500             return 0;
    501         }
    502     } else if (!strcmp(argv[1], "fspath")) {
    503         dumpArgs(argc, argv, -1);
    504         if (argc != 3) {
    505             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
    506             return 0;
    507         }
    508         char path[255];
    509 
    510         if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
    511             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    512             return 0;
    513         }
    514     } else {
    515         dumpArgs(argc, argv, -1);
    516         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
    517     }
    518 
    519     if (!rc) {
    520         cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
    521     } else {
    522         rc = ResponseCode::convertFromErrno();
    523         cli->sendMsg(rc, "asec operation failed", true);
    524     }
    525 
    526     return 0;
    527 }
    528 
    529 CommandListener::ObbCmd::ObbCmd() :
    530                  VoldCommand("obb") {
    531 }
    532 
    533 int CommandListener::ObbCmd::runCommand(SocketClient *cli,
    534                                                       int argc, char **argv) {
    535     if (argc < 2) {
    536         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    537         return 0;
    538     }
    539 
    540     VolumeManager *vm = VolumeManager::Instance();
    541     int rc = 0;
    542 
    543     if (!strcmp(argv[1], "list")) {
    544         dumpArgs(argc, argv, -1);
    545 
    546         rc = vm->listMountedObbs(cli);
    547     } else if (!strcmp(argv[1], "mount")) {
    548             dumpArgs(argc, argv, 3);
    549             if (argc != 5) {
    550                 cli->sendMsg(ResponseCode::CommandSyntaxError,
    551                         "Usage: obb mount <filename> <key> <ownerGid>", false);
    552                 return 0;
    553             }
    554             rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
    555     } else if (!strcmp(argv[1], "unmount")) {
    556         dumpArgs(argc, argv, -1);
    557         if (argc < 3) {
    558             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
    559             return 0;
    560         }
    561         bool force = false;
    562         if (argc > 3 && !strcmp(argv[3], "force")) {
    563             force = true;
    564         }
    565         rc = vm->unmountObb(argv[2], force);
    566     } else if (!strcmp(argv[1], "path")) {
    567         dumpArgs(argc, argv, -1);
    568         if (argc != 3) {
    569             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
    570             return 0;
    571         }
    572         char path[255];
    573 
    574         if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
    575             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    576             return 0;
    577         }
    578     } else {
    579         dumpArgs(argc, argv, -1);
    580         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
    581     }
    582 
    583     if (!rc) {
    584         cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
    585     } else {
    586         rc = ResponseCode::convertFromErrno();
    587         cli->sendMsg(rc, "obb operation failed", true);
    588     }
    589 
    590     return 0;
    591 }
    592 
    593 CommandListener::FstrimCmd::FstrimCmd() :
    594                  VoldCommand("fstrim") {
    595 }
    596 int CommandListener::FstrimCmd::runCommand(SocketClient *cli,
    597                                                       int argc, char **argv) {
    598     if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
    599         cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run fstrim commands", false);
    600         return 0;
    601     }
    602 
    603     if (argc < 2) {
    604         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    605         return 0;
    606     }
    607 
    608     VolumeManager *vm = VolumeManager::Instance();
    609     std::lock_guard<std::mutex> lock(vm->getLock());
    610 
    611     int flags = 0;
    612 
    613     std::string cmd(argv[1]);
    614     if (cmd == "dotrim") {
    615         flags = 0;
    616     } else if (cmd == "dotrimbench") {
    617         flags = android::vold::TrimTask::Flags::kBenchmarkAfter;
    618     } else if (cmd == "dodtrim") {
    619         flags = android::vold::TrimTask::Flags::kDeepTrim;
    620     } else if (cmd == "dodtrimbench") {
    621         flags = android::vold::TrimTask::Flags::kDeepTrim
    622                 | android::vold::TrimTask::Flags::kBenchmarkAfter;
    623     }
    624 
    625     (new android::vold::TrimTask(flags))->start();
    626     return sendGenericOkFail(cli, 0);
    627 }
    628 
    629 static size_t kAppFuseMaxMountPointName = 32;
    630 
    631 static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
    632     if (name.size() > kAppFuseMaxMountPointName) {
    633         LOG(ERROR) << "AppFuse mount name is too long.";
    634         return -EINVAL;
    635     }
    636     for (size_t i = 0; i < name.size(); i++) {
    637         if (!isalnum(name[i])) {
    638             LOG(ERROR) << "AppFuse mount name contains invalid character.";
    639             return -EINVAL;
    640         }
    641     }
    642     *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
    643     return android::OK;
    644 }
    645 
    646 static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
    647     // Remove existing mount.
    648     android::vold::ForceUnmount(path);
    649 
    650     const auto opts = android::base::StringPrintf(
    651             "fd=%i,"
    652             "rootmode=40000,"
    653             "default_permissions,"
    654             "allow_other,"
    655             "user_id=%d,group_id=%d,"
    656             "context=\"u:object_r:app_fuse_file:s0\","
    657             "fscontext=u:object_r:app_fusefs:s0",
    658             device_fd,
    659             uid,
    660             uid);
    661 
    662     const int result = TEMP_FAILURE_RETRY(mount(
    663             "/dev/fuse", path.c_str(), "fuse",
    664             MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
    665     if (result != 0) {
    666         PLOG(ERROR) << "Failed to mount " << path;
    667         return -errno;
    668     }
    669 
    670     return android::OK;
    671 }
    672 
    673 static android::status_t runCommandInNamespace(const std::string& command,
    674                                                uid_t uid,
    675                                                pid_t pid,
    676                                                const std::string& path,
    677                                                int device_fd) {
    678     if (DEBUG_APPFUSE) {
    679         LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path
    680                    << " in namespace " << uid;
    681     }
    682 
    683     const android::vold::ScopedDir dir(opendir("/proc"));
    684     if (dir.get() == nullptr) {
    685         PLOG(ERROR) << "Failed to open /proc";
    686         return -errno;
    687     }
    688 
    689     // Obtains process file descriptor.
    690     const std::string pid_str = android::base::StringPrintf("%d", pid);
    691     const android::vold::ScopedFd pid_fd(
    692             openat(dirfd(dir.get()), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
    693     if (pid_fd.get() == -1) {
    694         PLOG(ERROR) << "Failed to open /proc/" << pid;
    695         return -errno;
    696     }
    697 
    698     // Check UID of process.
    699     {
    700         struct stat sb;
    701         const int result = fstat(pid_fd.get(), &sb);
    702         if (result == -1) {
    703             PLOG(ERROR) << "Failed to stat /proc/" << pid;
    704             return -errno;
    705         }
    706         if (sb.st_uid != uid) {
    707             LOG(ERROR) << "Mismatch UID expected=" << uid << ", actual=" << sb.st_uid;
    708             return -EPERM;
    709         }
    710     }
    711 
    712     // Matches so far, but refuse to touch if in root namespace
    713     {
    714         char rootName[PATH_MAX];
    715         char pidName[PATH_MAX];
    716         const int root_result =
    717                 android::vold::SaneReadLinkAt(dirfd(dir.get()), "1/ns/mnt", rootName, PATH_MAX);
    718         const int pid_result =
    719                 android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
    720         if (root_result == -1) {
    721             LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
    722             return -EPERM;
    723         }
    724         if (pid_result == -1) {
    725             LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
    726             return -EPERM;
    727         }
    728         if (!strcmp(rootName, pidName)) {
    729             LOG(ERROR) << "Don't mount appfuse in root namespace";
    730             return -EPERM;
    731         }
    732     }
    733 
    734     // We purposefully leave the namespace open across the fork
    735     android::vold::ScopedFd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY));
    736     if (ns_fd.get() < 0) {
    737         PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
    738         return -errno;
    739     }
    740 
    741     int child = fork();
    742     if (child == 0) {
    743         if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
    744             PLOG(ERROR) << "Failed to setns";
    745             _exit(-errno);
    746         }
    747 
    748         if (command == "mount") {
    749             _exit(mountInNamespace(uid, device_fd, path));
    750         } else if (command == "unmount") {
    751             android::vold::ForceUnmount(path);
    752             _exit(android::OK);
    753         } else {
    754             LOG(ERROR) << "Unknown appfuse command " << command;
    755             _exit(-EPERM);
    756         }
    757     }
    758 
    759     if (child == -1) {
    760         PLOG(ERROR) << "Failed to folk child process";
    761         return -errno;
    762     }
    763 
    764     android::status_t status;
    765     TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
    766 
    767     return status;
    768 }
    769 
    770 CommandListener::AppFuseCmd::AppFuseCmd() : VoldCommand("appfuse") {}
    771 
    772 int CommandListener::AppFuseCmd::runCommand(SocketClient *cli, int argc, char **argv) {
    773     if (argc < 2) {
    774         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    775         return 0;
    776     }
    777 
    778     const std::string command(argv[1]);
    779 
    780     if (command == "mount" && argc == 5) {
    781         const uid_t uid = atoi(argv[2]);
    782         const pid_t pid = atoi(argv[3]);
    783         const std::string name(argv[4]);
    784 
    785         // Check mount point name.
    786         std::string path;
    787         if (getMountPath(uid, name, &path) != android::OK) {
    788             return cli->sendMsg(ResponseCode::CommandParameterError,
    789                                 "Invalid mount point name.",
    790                                 false);
    791         }
    792 
    793         // Create directories.
    794         {
    795             const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
    796             if (result != android::OK) {
    797                 PLOG(ERROR) << "Failed to prepare directory " << path;
    798                 return sendGenericOkFail(cli, result);
    799             }
    800         }
    801 
    802         // Open device FD.
    803         android::vold::ScopedFd device_fd(open("/dev/fuse", O_RDWR));
    804         if (device_fd.get() == -1) {
    805             PLOG(ERROR) << "Failed to open /dev/fuse";
    806             return sendGenericOkFail(cli, -errno);
    807         }
    808 
    809         // Mount.
    810         {
    811             const android::status_t result =
    812                     runCommandInNamespace(command, uid, pid, path, device_fd.get());
    813             if (result != android::OK) {
    814                 return sendGenericOkFail(cli, result);
    815             }
    816         }
    817 
    818         return sendFd(cli, device_fd.get());
    819     } else if (command == "unmount" && argc == 5) {
    820         const uid_t uid = atoi(argv[2]);
    821         const uid_t pid = atoi(argv[3]);
    822         const std::string name(argv[4]);
    823 
    824         // Check mount point name.
    825         std::string path;
    826         if (getMountPath(uid, name, &path) != android::OK) {
    827             return cli->sendMsg(ResponseCode::CommandParameterError,
    828                                 "Invalid mount point name.",
    829                                 false);
    830         }
    831 
    832         const android::status_t result =
    833                 runCommandInNamespace(command, uid, pid, path, -1 /* device_fd */);
    834         return sendGenericOkFail(cli, result);
    835     }
    836 
    837     return cli->sendMsg(ResponseCode::CommandSyntaxError,  "Unknown appfuse cmd", false);
    838 }
    839 
    840 android::status_t CommandListener::AppFuseCmd::sendFd(SocketClient *cli, int fd) {
    841     struct iovec data;
    842     char dataBuffer[128];
    843     char controlBuffer[CMSG_SPACE(sizeof(int))];
    844     struct msghdr message;
    845 
    846     // Message.
    847     memset(&message, 0, sizeof(struct msghdr));
    848     message.msg_iov = &data;
    849     message.msg_iovlen = 1;
    850     message.msg_control = controlBuffer;
    851     message.msg_controllen = CMSG_SPACE(sizeof(int));
    852 
    853     // Data.
    854     data.iov_base = dataBuffer;
    855     data.iov_len = snprintf(dataBuffer,
    856                             sizeof(dataBuffer),
    857                             "200 %d AppFuse command succeeded",
    858                             cli->getCmdNum()) + 1;
    859 
    860     // Control.
    861     struct cmsghdr* const controlMessage = CMSG_FIRSTHDR(&message);
    862     memset(controlBuffer, 0, CMSG_SPACE(sizeof(int)));
    863     controlMessage->cmsg_level = SOL_SOCKET;
    864     controlMessage->cmsg_type = SCM_RIGHTS;
    865     controlMessage->cmsg_len = CMSG_LEN(sizeof(int));
    866     *((int *) CMSG_DATA(controlMessage)) = fd;
    867 
    868     const int result = TEMP_FAILURE_RETRY(sendmsg(cli->getSocket(), &message, 0));
    869     if (result == -1) {
    870         PLOG(ERROR) << "Failed to send FD from vold";
    871         return -errno;
    872     }
    873 
    874     return android::OK;
    875 }
    876