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", true) {
     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 void CommandListener::AsecCmd::listAsecsInDirectory(SocketClient *cli, const char *directory) {
    265     DIR *d = opendir(directory);
    266 
    267     if (!d) {
    268         cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
    269         return;
    270     }
    271 
    272     size_t dirent_len = offsetof(struct dirent, d_name) +
    273             pathconf(directory, _PC_NAME_MAX) + 1;
    274 
    275     struct dirent *dent = (struct dirent *) malloc(dirent_len);
    276     if (dent == NULL) {
    277         cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", true);
    278         return;
    279     }
    280 
    281     struct dirent *result;
    282 
    283     while (!readdir_r(d, dent, &result) && result != NULL) {
    284         if (dent->d_name[0] == '.')
    285             continue;
    286         if (dent->d_type != DT_REG)
    287             continue;
    288         size_t name_len = strlen(dent->d_name);
    289         if (name_len > 5 && name_len < 260 &&
    290                 !strcmp(&dent->d_name[name_len - 5], ".asec")) {
    291             char id[255];
    292             memset(id, 0, sizeof(id));
    293             strlcpy(id, dent->d_name, name_len - 4);
    294             cli->sendMsg(ResponseCode::AsecListResult, id, false);
    295         }
    296     }
    297     closedir(d);
    298 
    299     free(dent);
    300 }
    301 
    302 int CommandListener::AsecCmd::runCommand(SocketClient *cli,
    303                                                       int argc, char **argv) {
    304     if (argc < 2) {
    305         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    306         return 0;
    307     }
    308 
    309     VolumeManager *vm = VolumeManager::Instance();
    310     int rc = 0;
    311 
    312     if (!strcmp(argv[1], "list")) {
    313         dumpArgs(argc, argv, -1);
    314 
    315         listAsecsInDirectory(cli, Volume::SEC_ASECDIR_EXT);
    316         listAsecsInDirectory(cli, Volume::SEC_ASECDIR_INT);
    317     } else if (!strcmp(argv[1], "create")) {
    318         dumpArgs(argc, argv, 5);
    319         if (argc != 8) {
    320             cli->sendMsg(ResponseCode::CommandSyntaxError,
    321                     "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid> "
    322                     "<isExternal>", false);
    323             return 0;
    324         }
    325 
    326         unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
    327         const bool isExternal = (atoi(argv[7]) == 1);
    328         rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
    329     } else if (!strcmp(argv[1], "finalize")) {
    330         dumpArgs(argc, argv, -1);
    331         if (argc != 3) {
    332             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
    333             return 0;
    334         }
    335         rc = vm->finalizeAsec(argv[2]);
    336     } else if (!strcmp(argv[1], "fixperms")) {
    337         dumpArgs(argc, argv, -1);
    338         if  (argc != 5) {
    339             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
    340             return 0;
    341         }
    342 
    343         char *endptr;
    344         gid_t gid = (gid_t) strtoul(argv[3], &endptr, 10);
    345         if (*endptr != '\0') {
    346             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
    347             return 0;
    348         }
    349 
    350         rc = vm->fixupAsecPermissions(argv[2], gid, argv[4]);
    351     } else if (!strcmp(argv[1], "destroy")) {
    352         dumpArgs(argc, argv, -1);
    353         if (argc < 3) {
    354             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
    355             return 0;
    356         }
    357         bool force = false;
    358         if (argc > 3 && !strcmp(argv[3], "force")) {
    359             force = true;
    360         }
    361         rc = vm->destroyAsec(argv[2], force);
    362     } else if (!strcmp(argv[1], "mount")) {
    363         dumpArgs(argc, argv, 3);
    364         if (argc != 5) {
    365             cli->sendMsg(ResponseCode::CommandSyntaxError,
    366                     "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
    367             return 0;
    368         }
    369         rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
    370     } else if (!strcmp(argv[1], "unmount")) {
    371         dumpArgs(argc, argv, -1);
    372         if (argc < 3) {
    373             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
    374             return 0;
    375         }
    376         bool force = false;
    377         if (argc > 3 && !strcmp(argv[3], "force")) {
    378             force = true;
    379         }
    380         rc = vm->unmountAsec(argv[2], force);
    381     } else if (!strcmp(argv[1], "rename")) {
    382         dumpArgs(argc, argv, -1);
    383         if (argc != 4) {
    384             cli->sendMsg(ResponseCode::CommandSyntaxError,
    385                     "Usage: asec rename <old_id> <new_id>", false);
    386             return 0;
    387         }
    388         rc = vm->renameAsec(argv[2], argv[3]);
    389     } else if (!strcmp(argv[1], "path")) {
    390         dumpArgs(argc, argv, -1);
    391         if (argc != 3) {
    392             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
    393             return 0;
    394         }
    395         char path[255];
    396 
    397         if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
    398             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    399             return 0;
    400         }
    401     } else if (!strcmp(argv[1], "fspath")) {
    402         dumpArgs(argc, argv, -1);
    403         if (argc != 3) {
    404             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
    405             return 0;
    406         }
    407         char path[255];
    408 
    409         if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
    410             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    411             return 0;
    412         }
    413     } else {
    414         dumpArgs(argc, argv, -1);
    415         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
    416     }
    417 
    418     if (!rc) {
    419         cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
    420     } else {
    421         rc = ResponseCode::convertFromErrno();
    422         cli->sendMsg(rc, "asec operation failed", true);
    423     }
    424 
    425     return 0;
    426 }
    427 
    428 CommandListener::ObbCmd::ObbCmd() :
    429                  VoldCommand("obb") {
    430 }
    431 
    432 int CommandListener::ObbCmd::runCommand(SocketClient *cli,
    433                                                       int argc, char **argv) {
    434     if (argc < 2) {
    435         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    436         return 0;
    437     }
    438 
    439     VolumeManager *vm = VolumeManager::Instance();
    440     int rc = 0;
    441 
    442     if (!strcmp(argv[1], "list")) {
    443         dumpArgs(argc, argv, -1);
    444 
    445         rc = vm->listMountedObbs(cli);
    446     } else if (!strcmp(argv[1], "mount")) {
    447             dumpArgs(argc, argv, 3);
    448             if (argc != 5) {
    449                 cli->sendMsg(ResponseCode::CommandSyntaxError,
    450                         "Usage: obb mount <filename> <key> <ownerUid>", false);
    451                 return 0;
    452             }
    453             rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
    454     } else if (!strcmp(argv[1], "unmount")) {
    455         dumpArgs(argc, argv, -1);
    456         if (argc < 3) {
    457             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
    458             return 0;
    459         }
    460         bool force = false;
    461         if (argc > 3 && !strcmp(argv[3], "force")) {
    462             force = true;
    463         }
    464         rc = vm->unmountObb(argv[2], force);
    465     } else if (!strcmp(argv[1], "path")) {
    466         dumpArgs(argc, argv, -1);
    467         if (argc != 3) {
    468             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
    469             return 0;
    470         }
    471         char path[255];
    472 
    473         if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
    474             cli->sendMsg(ResponseCode::AsecPathResult, path, false);
    475             return 0;
    476         }
    477     } else {
    478         dumpArgs(argc, argv, -1);
    479         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
    480     }
    481 
    482     if (!rc) {
    483         cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
    484     } else {
    485         rc = ResponseCode::convertFromErrno();
    486         cli->sendMsg(rc, "obb operation failed", true);
    487     }
    488 
    489     return 0;
    490 }
    491 
    492 CommandListener::XwarpCmd::XwarpCmd() :
    493                  VoldCommand("xwarp") {
    494 }
    495 
    496 int CommandListener::XwarpCmd::runCommand(SocketClient *cli,
    497                                                       int argc, char **argv) {
    498     if (argc < 2) {
    499         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    500         return 0;
    501     }
    502 
    503     if (!strcmp(argv[1], "enable")) {
    504         if (Xwarp::enable()) {
    505             cli->sendMsg(ResponseCode::OperationFailed, "Failed to enable xwarp", true);
    506             return 0;
    507         }
    508 
    509         cli->sendMsg(ResponseCode::CommandOkay, "Xwarp mirroring started", false);
    510     } else if (!strcmp(argv[1], "disable")) {
    511         if (Xwarp::disable()) {
    512             cli->sendMsg(ResponseCode::OperationFailed, "Failed to disable xwarp", true);
    513             return 0;
    514         }
    515 
    516         cli->sendMsg(ResponseCode::CommandOkay, "Xwarp disabled", false);
    517     } else if (!strcmp(argv[1], "status")) {
    518         char msg[255];
    519         bool r;
    520         unsigned mirrorPos, maxSize;
    521 
    522         if (Xwarp::status(&r, &mirrorPos, &maxSize)) {
    523             cli->sendMsg(ResponseCode::OperationFailed, "Failed to get xwarp status", true);
    524             return 0;
    525         }
    526         snprintf(msg, sizeof(msg), "%s %u %u", (r ? "ready" : "not-ready"), mirrorPos, maxSize);
    527         cli->sendMsg(ResponseCode::XwarpStatusResult, msg, false);
    528     } else {
    529         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
    530     }
    531 
    532     return 0;
    533 }
    534 
    535 CommandListener::CryptfsCmd::CryptfsCmd() :
    536                  VoldCommand("cryptfs") {
    537 }
    538 
    539 int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
    540                                                       int argc, char **argv) {
    541     if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
    542         cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
    543         return 0;
    544     }
    545 
    546     if (argc < 2) {
    547         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
    548         return 0;
    549     }
    550 
    551     int rc = 0;
    552 
    553     if (!strcmp(argv[1], "checkpw")) {
    554         if (argc != 3) {
    555             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
    556             return 0;
    557         }
    558         dumpArgs(argc, argv, 2);
    559         rc = cryptfs_check_passwd(argv[2]);
    560     } else if (!strcmp(argv[1], "restart")) {
    561         if (argc != 2) {
    562             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false);
    563             return 0;
    564         }
    565         dumpArgs(argc, argv, -1);
    566         rc = cryptfs_restart();
    567     } else if (!strcmp(argv[1], "cryptocomplete")) {
    568         if (argc != 2) {
    569             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs cryptocomplete", false);
    570             return 0;
    571         }
    572         dumpArgs(argc, argv, -1);
    573         rc = cryptfs_crypto_complete();
    574     } else if (!strcmp(argv[1], "enablecrypto")) {
    575         if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
    576             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false);
    577             return 0;
    578         }
    579         dumpArgs(argc, argv, 3);
    580         rc = cryptfs_enable(argv[2], argv[3]);
    581     } else if (!strcmp(argv[1], "changepw")) {
    582         if (argc != 3) {
    583             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw <newpasswd>", false);
    584             return 0;
    585         }
    586         SLOGD("cryptfs changepw {}");
    587         rc = cryptfs_changepw(argv[2]);
    588     } else if (!strcmp(argv[1], "verifypw")) {
    589         if (argc != 3) {
    590             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
    591             return 0;
    592         }
    593         SLOGD("cryptfs verifypw {}");
    594         rc = cryptfs_verify_passwd(argv[2]);
    595     } else {
    596         dumpArgs(argc, argv, -1);
    597         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
    598     }
    599 
    600     // Always report that the command succeeded and return the error code.
    601     // The caller will check the return value to see what the error was.
    602     char msg[255];
    603     snprintf(msg, sizeof(msg), "%d", rc);
    604     cli->sendMsg(ResponseCode::CommandOkay, msg, false);
    605 
    606     return 0;
    607 }
    608