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