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/socket.h>
     19 #include <sys/types.h>
     20 #include <netinet/in.h>
     21 #include <arpa/inet.h>
     22 #include <dirent.h>
     23 #include <errno.h>
     24 #include <fcntl.h>
     25 #include <string.h>
     26 
     27 #define LOG_TAG "VoldCmdListener"
     28 #include <cutils/log.h>
     29 
     30 #include <sysutils/SocketClient.h>
     31 #include <private/android_filesystem_config.h>
     32 
     33 #include "CommandListener.h"
     34 #include "VolumeManager.h"
     35 #include "ResponseCode.h"
     36 #include "Process.h"
     37 #include "Xwarp.h"
     38 #include "Loop.h"
     39 #include "Devmapper.h"
     40 #include "cryptfs.h"
     41 
     42 CommandListener::CommandListener() :
     43                  FrameworkListener("vold") {
     44     registerCmd(new DumpCmd());
     45     registerCmd(new VolumeCmd());
     46     registerCmd(new AsecCmd());
     47     registerCmd(new ObbCmd());
     48     registerCmd(new StorageCmd());
     49     registerCmd(new XwarpCmd());
     50     registerCmd(new CryptfsCmd());
     51 }
     52 
     53 void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
     54     char buffer[4096];
     55     char *p = buffer;
     56 
     57     memset(buffer, 0, sizeof(buffer));
     58     int i;
     59     for (i = 0; i < argc; i++) {
     60         unsigned int len = strlen(argv[i]) + 1; // Account for space
     61         if (i == argObscure) {
     62             len += 2; // Account for {}
     63         }
     64         if (((p - buffer) + len) < (sizeof(buffer)-1)) {
     65             if (i == argObscure) {
     66                 *p++ = '{';
     67                 *p++ = '}';
     68                 *p++ = ' ';
     69                 continue;
     70             }
     71             strcpy(p, argv[i]);
     72             p+= strlen(argv[i]);
     73             if (i != (argc -1)) {
     74                 *p++ = ' ';
     75             }
     76         }
     77     }
     78     SLOGD("%s", buffer);
     79 }
     80 
     81 CommandListener::DumpCmd::DumpCmd() :
     82                  VoldCommand("dump") {
     83 }
     84 
     85 int CommandListener::DumpCmd::runCommand(SocketClient *cli,
     86                                          int argc, char **argv) {
     87     cli->sendMsg(0, "Dumping loop status", false);
     88     if (Loop::dumpState(cli)) {
     89         cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
     90     }
     91     cli->sendMsg(0, "Dumping DM status", false);
     92     if (Devmapper::dumpState(cli)) {
     93         cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
     94     }
     95     cli->sendMsg(0, "Dumping mounted filesystems", false);
     96     FILE *fp = fopen("/proc/mounts", "r");
     97     if (fp) {
     98         char line[1024];
     99         while (fgets(line, sizeof(line), fp)) {
    100             line[strlen(line)-1] = '\0';
    101             cli->sendMsg(0, line, false);;
    102         }
    103         fclose(fp);
    104     }
    105 
    106     cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
    107     return 0;
    108 }
    109 
    110 
    111 CommandListener::VolumeCmd::VolumeCmd() :
    112                  VoldCommand("volume") {
    113 }
    114 
    115 int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
    116                                                       int argc, char **argv) {
    117     dumpArgs(argc, argv, -1);
    118 
    119     if (argc < 2) {
    120         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    121         return 0;
    122     }
    123 
    124     VolumeManager *vm = VolumeManager::Instance();
    125     int rc = 0;
    126 
    127     if (!strcmp(argv[1], "list")) {
    128         return vm->listVolumes(cli);
    129     } else if (!strcmp(argv[1], "debug")) {
    130         if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {
    131             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false);
    132             return 0;
    133         }
    134         vm->setDebug(!strcmp(argv[2], "on") ? true : false);
    135     } else if (!strcmp(argv[1], "mount")) {
    136         if (argc != 3) {
    137             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);
    138             return 0;
    139         }
    140         rc = vm->mountVolume(argv[2]);
    141     } else if (!strcmp(argv[1], "unmount")) {
    142         if (argc < 3 || argc > 4 ||
    143            ((argc == 4 && strcmp(argv[3], "force")) &&
    144             (argc == 4 && strcmp(argv[3], "force_and_revert")))) {
    145             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false);
    146             return 0;
    147         }
    148 
    149         bool force = false;
    150         bool revert = false;
    151         if (argc >= 4 && !strcmp(argv[3], "force")) {
    152             force = true;
    153         } else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) {
    154             force = true;
    155             revert = true;
    156         }
    157         rc = vm->unmountVolume(argv[2], force, revert);
    158     } else if (!strcmp(argv[1], "format")) {
    159         if (argc != 3) {
    160             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false);
    161             return 0;
    162         }
    163         rc = vm->formatVolume(argv[2]);
    164     } else if (!strcmp(argv[1], "share")) {
    165         if (argc != 4) {
    166             cli->sendMsg(ResponseCode::CommandSyntaxError,
    167                     "Usage: volume share <path> <method>", false);
    168             return 0;
    169         }
    170         rc = vm->shareVolume(argv[2], argv[3]);
    171     } else if (!strcmp(argv[1], "unshare")) {
    172         if (argc != 4) {
    173             cli->sendMsg(ResponseCode::CommandSyntaxError,
    174                     "Usage: volume unshare <path> <method>", false);
    175             return 0;
    176         }
    177         rc = vm->unshareVolume(argv[2], argv[3]);
    178     } else if (!strcmp(argv[1], "shared")) {
    179         bool enabled = false;
    180         if (argc != 4) {
    181             cli->sendMsg(ResponseCode::CommandSyntaxError,
    182                     "Usage: volume shared <path> <method>", false);
    183             return 0;
    184         }
    185 
    186         if (vm->shareEnabled(argv[2], argv[3], &enabled)) {
    187             cli->sendMsg(
    188                     ResponseCode::OperationFailed, "Failed to determine share enable state", true);
    189         } else {
    190             cli->sendMsg(ResponseCode::ShareEnabledResult,
    191                     (enabled ? "Share enabled" : "Share disabled"), false);
    192         }
    193         return 0;
    194     } else {
    195         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);
    196     }
    197 
    198     if (!rc) {
    199         cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);
    200     } else {
    201         int erno = errno;
    202         rc = ResponseCode::convertFromErrno();
    203         cli->sendMsg(rc, "volume operation failed", true);
    204     }
    205 
    206     return 0;
    207 }
    208 
    209 CommandListener::StorageCmd::StorageCmd() :
    210                  VoldCommand("storage") {
    211 }
    212 
    213 int CommandListener::StorageCmd::runCommand(SocketClient *cli,
    214                                                       int argc, char **argv) {
    215     dumpArgs(argc, argv, -1);
    216 
    217     if (argc < 2) {
    218         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    219         return 0;
    220     }
    221 
    222     if (!strcmp(argv[1], "users")) {
    223         DIR *dir;
    224         struct dirent *de;
    225 
    226         if (!(dir = opendir("/proc"))) {
    227             cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
    228             return 0;
    229         }
    230 
    231         while ((de = readdir(dir))) {
    232             int pid = Process::getPid(de->d_name);
    233 
    234             if (pid < 0) {
    235                 continue;
    236             }
    237 
    238             char processName[255];
    239             Process::getProcessName(pid, processName, sizeof(processName));
    240 
    241             if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
    242                 Process::checkFileMaps(pid, argv[2]) ||
    243                 Process::checkSymLink(pid, argv[2], "cwd") ||
    244                 Process::checkSymLink(pid, argv[2], "root") ||
    245                 Process::checkSymLink(pid, argv[2], "exe")) {
    246 
    247                 char msg[1024];
    248                 snprintf(msg, sizeof(msg), "%d %s", pid, processName);
    249                 cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
    250             }
    251         }
    252         closedir(dir);
    253         cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
    254     } else {
    255         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
    256     }
    257     return 0;
    258 }
    259 
    260 CommandListener::AsecCmd::AsecCmd() :
    261                  VoldCommand("asec") {
    262 }
    263 
    264 int CommandListener::AsecCmd::runCommand(SocketClient *cli,
    265                                                       int argc, char **argv) {
    266     if (argc < 2) {
    267         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    268         return 0;
    269     }
    270 
    271     VolumeManager *vm = VolumeManager::Instance();
    272     int rc = 0;
    273 
    274     if (!strcmp(argv[1], "list")) {
    275         dumpArgs(argc, argv, -1);
    276         DIR *d = opendir(Volume::SEC_ASECDIR);
    277 
    278         if (!d) {
    279             cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
    280             return 0;
    281         }
    282 
    283         struct dirent *dent;
    284         while ((dent = readdir(d))) {
    285             if (dent->d_name[0] == '.')
    286                 continue;
    287             if (!strcmp(&dent->d_name[strlen(dent->d_name)-5], ".asec")) {
    288                 char id[255];
    289                 memset(id, 0, sizeof(id));
    290                 strncpy(id, dent->d_name, strlen(dent->d_name) -5);
    291                 cli->sendMsg(ResponseCode::AsecListResult, id, false);
    292             }
    293         }
    294         closedir(d);
    295     } else if (!strcmp(argv[1], "create")) {
    296         dumpArgs(argc, argv, 5);
    297         if (argc != 7) {
    298             cli->sendMsg(ResponseCode::CommandSyntaxError,
    299                     "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid>", false);
    300             return 0;
    301         }
    302 
    303         unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
    304         rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]));
    305     } else if (!strcmp(argv[1], "finalize")) {
    306         dumpArgs(argc, argv, -1);
    307         if (argc != 3) {
    308             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
    309             return 0;
    310         }
    311         rc = vm->finalizeAsec(argv[2]);
    312     } else if (!strcmp(argv[1], "destroy")) {
    313         dumpArgs(argc, argv, -1);
    314         if (argc < 3) {
    315             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
    316             return 0;
    317         }
    318         bool force = false;
    319         if (argc > 3 && !strcmp(argv[3], "force")) {
    320             force = true;
    321         }
    322         rc = vm->destroyAsec(argv[2], force);
    323     } else if (!strcmp(argv[1], "mount")) {
    324         dumpArgs(argc, argv, 3);
    325         if (argc != 5) {
    326             cli->sendMsg(ResponseCode::CommandSyntaxError,
    327                     "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
    328             return 0;
    329         }
    330         rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
    331     } else if (!strcmp(argv[1], "unmount")) {
    332         dumpArgs(argc, argv, -1);
    333         if (argc < 3) {
    334             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
    335             return 0;
    336         }
    337         bool force = false;
    338         if (argc > 3 && !strcmp(argv[3], "force")) {
    339             force = true;
    340         }
    341         rc = vm->unmountAsec(argv[2], force);
    342     } else if (!strcmp(argv[1], "rename")) {
    343         dumpArgs(argc, argv, -1);
    344         if (argc != 4) {
    345             cli->sendMsg(ResponseCode::CommandSyntaxError,
    346                     "Usage: asec rename <old_id> <new_id>", false);
    347             return 0;
    348         }
    349         rc = vm->renameAsec(argv[2], argv[3]);
    350     } else if (!strcmp(argv[1], "path")) {
    351         dumpArgs(argc, argv, -1);
    352         if (argc != 3) {
    353             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
    354             return 0;
    355         }
    356         char path[255];
    357 
    358         if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
    359             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    360             return 0;
    361         }
    362     } else if (!strcmp(argv[1], "fspath")) {
    363         dumpArgs(argc, argv, -1);
    364         if (argc != 3) {
    365             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
    366             return 0;
    367         }
    368         char path[255];
    369 
    370         if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
    371             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    372             return 0;
    373         }
    374     } else {
    375         dumpArgs(argc, argv, -1);
    376         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
    377     }
    378 
    379     if (!rc) {
    380         cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
    381     } else {
    382         rc = ResponseCode::convertFromErrno();
    383         cli->sendMsg(rc, "asec operation failed", true);
    384     }
    385 
    386     return 0;
    387 }
    388 
    389 CommandListener::ObbCmd::ObbCmd() :
    390                  VoldCommand("obb") {
    391 }
    392 
    393 int CommandListener::ObbCmd::runCommand(SocketClient *cli,
    394                                                       int argc, char **argv) {
    395     if (argc < 2) {
    396         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    397         return 0;
    398     }
    399 
    400     VolumeManager *vm = VolumeManager::Instance();
    401     int rc = 0;
    402 
    403     if (!strcmp(argv[1], "list")) {
    404         dumpArgs(argc, argv, -1);
    405 
    406         rc = vm->listMountedObbs(cli);
    407     } else if (!strcmp(argv[1], "mount")) {
    408             dumpArgs(argc, argv, 3);
    409             if (argc != 5) {
    410                 cli->sendMsg(ResponseCode::CommandSyntaxError,
    411                         "Usage: obb mount <filename> <key> <ownerUid>", false);
    412                 return 0;
    413             }
    414             rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
    415     } else if (!strcmp(argv[1], "unmount")) {
    416         dumpArgs(argc, argv, -1);
    417         if (argc < 3) {
    418             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
    419             return 0;
    420         }
    421         bool force = false;
    422         if (argc > 3 && !strcmp(argv[3], "force")) {
    423             force = true;
    424         }
    425         rc = vm->unmountObb(argv[2], force);
    426     } else if (!strcmp(argv[1], "path")) {
    427         dumpArgs(argc, argv, -1);
    428         if (argc != 3) {
    429             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
    430             return 0;
    431         }
    432         char path[255];
    433 
    434         if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
    435             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    436             return 0;
    437         }
    438     } else {
    439         dumpArgs(argc, argv, -1);
    440         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
    441     }
    442 
    443     if (!rc) {
    444         cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
    445     } else {
    446         rc = ResponseCode::convertFromErrno();
    447         cli->sendMsg(rc, "obb operation failed", true);
    448     }
    449 
    450     return 0;
    451 }
    452 
    453 CommandListener::XwarpCmd::XwarpCmd() :
    454                  VoldCommand("xwarp") {
    455 }
    456 
    457 int CommandListener::XwarpCmd::runCommand(SocketClient *cli,
    458                                                       int argc, char **argv) {
    459     if (argc < 2) {
    460         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    461         return 0;
    462     }
    463 
    464     if (!strcmp(argv[1], "enable")) {
    465         if (Xwarp::enable()) {
    466             cli->sendMsg(ResponseCode::OperationFailed, "Failed to enable xwarp", true);
    467             return 0;
    468         }
    469 
    470         cli->sendMsg(ResponseCode::CommandOkay, "Xwarp mirroring started", false);
    471     } else if (!strcmp(argv[1], "disable")) {
    472         if (Xwarp::disable()) {
    473             cli->sendMsg(ResponseCode::OperationFailed, "Failed to disable xwarp", true);
    474             return 0;
    475         }
    476 
    477         cli->sendMsg(ResponseCode::CommandOkay, "Xwarp disabled", false);
    478     } else if (!strcmp(argv[1], "status")) {
    479         char msg[255];
    480         bool r;
    481         unsigned mirrorPos, maxSize;
    482 
    483         if (Xwarp::status(&r, &mirrorPos, &maxSize)) {
    484             cli->sendMsg(ResponseCode::OperationFailed, "Failed to get xwarp status", true);
    485             return 0;
    486         }
    487         snprintf(msg, sizeof(msg), "%s %u %u", (r ? "ready" : "not-ready"), mirrorPos, maxSize);
    488         cli->sendMsg(ResponseCode::XwarpStatusResult, msg, false);
    489     } else {
    490         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
    491     }
    492 
    493     return 0;
    494 }
    495 
    496 CommandListener::CryptfsCmd::CryptfsCmd() :
    497                  VoldCommand("cryptfs") {
    498 }
    499 
    500 int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
    501                                                       int argc, char **argv) {
    502     if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
    503         cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
    504         return 0;
    505     }
    506 
    507     if (argc < 2) {
    508         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    509         return 0;
    510     }
    511 
    512     int rc = 0;
    513 
    514     if (!strcmp(argv[1], "checkpw")) {
    515         if (argc != 3) {
    516             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
    517             return 0;
    518         }
    519         dumpArgs(argc, argv, 2);
    520         rc = cryptfs_check_passwd(argv[2]);
    521     } else if (!strcmp(argv[1], "restart")) {
    522         if (argc != 2) {
    523             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false);
    524             return 0;
    525         }
    526         dumpArgs(argc, argv, -1);
    527         rc = cryptfs_restart();
    528     } else if (!strcmp(argv[1], "cryptocomplete")) {
    529         if (argc != 2) {
    530             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs cryptocomplete", false);
    531             return 0;
    532         }
    533         dumpArgs(argc, argv, -1);
    534         rc = cryptfs_crypto_complete();
    535     } else if (!strcmp(argv[1], "enablecrypto")) {
    536         if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
    537             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false);
    538             return 0;
    539         }
    540         dumpArgs(argc, argv, 3);
    541         rc = cryptfs_enable(argv[2], argv[3]);
    542     } else if (!strcmp(argv[1], "changepw")) {
    543         if (argc != 3) {
    544             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw <newpasswd>", false);
    545             return 0;
    546         }
    547         SLOGD("cryptfs changepw {}");
    548         rc = cryptfs_changepw(argv[2]);
    549     } else if (!strcmp(argv[1], "verifypw")) {
    550         if (argc != 3) {
    551             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
    552             return 0;
    553         }
    554         SLOGD("cryptfs verifypw {}");
    555         rc = cryptfs_verify_passwd(argv[2]);
    556     } else {
    557         dumpArgs(argc, argv, -1);
    558         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
    559     }
    560 
    561     // Always report that the command succeeded and return the error code.
    562     // The caller will check the return value to see what the error was.
    563     char msg[255];
    564     snprintf(msg, sizeof(msg), "%d", rc);
    565     cli->sendMsg(ResponseCode::CommandOkay, msg, false);
    566 
    567     return 0;
    568 }
    569 
    570