CommandListener.cpp revision 1bfb375f77c093a8e16bef4ddeab2681ca126d56
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 <fs_mgr.h>
26#include <stdio.h>
27#include <string.h>
28
29#define LOG_TAG "VoldCmdListener"
30#include <cutils/log.h>
31
32#include <sysutils/SocketClient.h>
33#include <private/android_filesystem_config.h>
34
35#include "CommandListener.h"
36#include "VolumeManager.h"
37#include "VolumeBase.h"
38#include "ResponseCode.h"
39#include "Process.h"
40#include "Loop.h"
41#include "Devmapper.h"
42#include "cryptfs.h"
43#include "fstrim.h"
44#include "MoveTask.h"
45
46#define DUMP_ARGS 0
47
48CommandListener::CommandListener() :
49                 FrameworkListener("vold", true) {
50    registerCmd(new DumpCmd());
51    registerCmd(new VolumeCmd());
52    registerCmd(new AsecCmd());
53    registerCmd(new ObbCmd());
54    registerCmd(new StorageCmd());
55    registerCmd(new CryptfsCmd());
56    registerCmd(new FstrimCmd());
57}
58
59#if DUMP_ARGS
60void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
61    char buffer[4096];
62    char *p = buffer;
63
64    memset(buffer, 0, sizeof(buffer));
65    int i;
66    for (i = 0; i < argc; i++) {
67        unsigned int len = strlen(argv[i]) + 1; // Account for space
68        if (i == argObscure) {
69            len += 2; // Account for {}
70        }
71        if (((p - buffer) + len) < (sizeof(buffer)-1)) {
72            if (i == argObscure) {
73                *p++ = '{';
74                *p++ = '}';
75                *p++ = ' ';
76                continue;
77            }
78            strcpy(p, argv[i]);
79            p+= strlen(argv[i]);
80            if (i != (argc -1)) {
81                *p++ = ' ';
82            }
83        }
84    }
85    SLOGD("%s", buffer);
86}
87#else
88void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
89#endif
90
91int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
92    if (!cond) {
93        return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
94    } else {
95        return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
96    }
97}
98
99CommandListener::DumpCmd::DumpCmd() :
100                 VoldCommand("dump") {
101}
102
103int CommandListener::DumpCmd::runCommand(SocketClient *cli,
104                                         int /*argc*/, char ** /*argv*/) {
105    cli->sendMsg(0, "Dumping loop status", false);
106    if (Loop::dumpState(cli)) {
107        cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
108    }
109    cli->sendMsg(0, "Dumping DM status", false);
110    if (Devmapper::dumpState(cli)) {
111        cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
112    }
113    cli->sendMsg(0, "Dumping mounted filesystems", false);
114    FILE *fp = fopen("/proc/mounts", "r");
115    if (fp) {
116        char line[1024];
117        while (fgets(line, sizeof(line), fp)) {
118            line[strlen(line)-1] = '\0';
119            cli->sendMsg(0, line, false);;
120        }
121        fclose(fp);
122    }
123
124    cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
125    return 0;
126}
127
128CommandListener::VolumeCmd::VolumeCmd() :
129                 VoldCommand("volume") {
130}
131
132int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
133                                           int argc, char **argv) {
134    dumpArgs(argc, argv, -1);
135
136    if (argc < 2) {
137        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
138        return 0;
139    }
140
141    VolumeManager *vm = VolumeManager::Instance();
142    std::lock_guard<std::mutex> lock(vm->getLock());
143
144    // TODO: tease out methods not directly related to volumes
145
146    std::string cmd(argv[1]);
147    if (cmd == "reset") {
148        return sendGenericOkFail(cli, vm->reset());
149
150    } else if (cmd == "shutdown") {
151        return sendGenericOkFail(cli, vm->shutdown());
152
153    } else if (cmd == "debug") {
154        return sendGenericOkFail(cli, vm->setDebug(true));
155
156    } else if (cmd == "partition" && argc > 3) {
157        // partition [diskId] [public|private|mixed] [ratio]
158        std::string id(argv[2]);
159        auto disk = vm->findDisk(id);
160        if (disk == nullptr) {
161            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
162        }
163
164        std::string type(argv[3]);
165        if (type == "public") {
166            return sendGenericOkFail(cli, disk->partitionPublic());
167        } else if (type == "private") {
168            return sendGenericOkFail(cli, disk->partitionPrivate());
169        } else if (type == "mixed") {
170            if (argc < 4) {
171                return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
172            }
173            int frac = atoi(argv[4]);
174            return sendGenericOkFail(cli, disk->partitionMixed(frac));
175        } else {
176            return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
177        }
178
179    } else if (cmd == "mkdirs" && argc > 2) {
180        // mkdirs [path]
181        return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
182
183    } else if (cmd == "start_user" && argc > 2) {
184        // start_user [user]
185        return sendGenericOkFail(cli, vm->startUser(atoi(argv[2])));
186
187    } else if (cmd == "cleanup_user" && argc > 2) {
188        // cleanup_user [user]
189        return sendGenericOkFail(cli, vm->cleanupUser(atoi(argv[2])));
190
191    } else if (cmd == "mount" && argc > 2) {
192        // mount [volId] [flags] [user]
193        std::string id(argv[2]);
194        auto vol = vm->findVolume(id);
195        if (vol == nullptr) {
196            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
197        }
198
199        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
200        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
201
202        vol->setMountFlags(mountFlags);
203        vol->setMountUserId(mountUserId);
204
205        int res = vol->mount();
206        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
207            vm->setPrimary(vol);
208        }
209        return sendGenericOkFail(cli, res);
210
211    } else if (cmd == "unmount" && argc > 2) {
212        // unmount [volId]
213        std::string id(argv[2]);
214        auto vol = vm->findVolume(id);
215        if (vol == nullptr) {
216            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
217        }
218
219        return sendGenericOkFail(cli, vol->unmount());
220
221    } else if (cmd == "format" && argc > 2) {
222        // format [volId]
223        std::string id(argv[2]);
224        auto vol = vm->findVolume(id);
225        if (vol == nullptr) {
226            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
227        }
228
229        return sendGenericOkFail(cli, vol->format());
230
231    } else if (cmd == "move_storage" && argc > 3) {
232        // move_storage [fromVolId] [toVolId]
233        auto fromVol = vm->findVolume(std::string(argv[2]));
234        auto toVol = vm->findVolume(std::string(argv[3]));
235        if (fromVol == nullptr || toVol == nullptr) {
236            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
237        }
238
239        (new android::vold::MoveTask(fromVol, toVol))->start();
240        return sendGenericOkFail(cli, 0);
241    }
242
243    return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
244}
245
246CommandListener::StorageCmd::StorageCmd() :
247                 VoldCommand("storage") {
248}
249
250int CommandListener::StorageCmd::runCommand(SocketClient *cli,
251                                                      int argc, char **argv) {
252    /* Guarantied to be initialized by vold's main() before the CommandListener is active */
253    extern struct fstab *fstab;
254
255    dumpArgs(argc, argv, -1);
256
257    if (argc < 2) {
258        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
259        return 0;
260    }
261
262    if (!strcmp(argv[1], "mountall")) {
263        if (argc != 2) {
264            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
265            return 0;
266        }
267        fs_mgr_mount_all(fstab);
268        cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
269        return 0;
270    }
271    if (!strcmp(argv[1], "users")) {
272        DIR *dir;
273        struct dirent *de;
274
275        if (argc < 3) {
276            cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument: user <mountpoint>", false);
277            return 0;
278        }
279        if (!(dir = opendir("/proc"))) {
280            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
281            return 0;
282        }
283
284        while ((de = readdir(dir))) {
285            int pid = Process::getPid(de->d_name);
286
287            if (pid < 0) {
288                continue;
289            }
290
291            char processName[255];
292            Process::getProcessName(pid, processName, sizeof(processName));
293
294            if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
295                Process::checkFileMaps(pid, argv[2]) ||
296                Process::checkSymLink(pid, argv[2], "cwd") ||
297                Process::checkSymLink(pid, argv[2], "root") ||
298                Process::checkSymLink(pid, argv[2], "exe")) {
299
300                char msg[1024];
301                snprintf(msg, sizeof(msg), "%d %s", pid, processName);
302                cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
303            }
304        }
305        closedir(dir);
306        cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
307    } else {
308        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
309    }
310    return 0;
311}
312
313CommandListener::AsecCmd::AsecCmd() :
314                 VoldCommand("asec") {
315}
316
317void CommandListener::AsecCmd::listAsecsInDirectory(SocketClient *cli, const char *directory) {
318    DIR *d = opendir(directory);
319
320    if (!d) {
321        cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
322        return;
323    }
324
325    size_t dirent_len = offsetof(struct dirent, d_name) +
326            fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
327
328    struct dirent *dent = (struct dirent *) malloc(dirent_len);
329    if (dent == NULL) {
330        cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", true);
331        return;
332    }
333
334    struct dirent *result;
335
336    while (!readdir_r(d, dent, &result) && result != NULL) {
337        if (dent->d_name[0] == '.')
338            continue;
339        if (dent->d_type != DT_REG)
340            continue;
341        size_t name_len = strlen(dent->d_name);
342        if (name_len > 5 && name_len < 260 &&
343                !strcmp(&dent->d_name[name_len - 5], ".asec")) {
344            char id[255];
345            memset(id, 0, sizeof(id));
346            strlcpy(id, dent->d_name, name_len - 4);
347            cli->sendMsg(ResponseCode::AsecListResult, id, false);
348        }
349    }
350    closedir(d);
351
352    free(dent);
353}
354
355int CommandListener::AsecCmd::runCommand(SocketClient *cli,
356                                                      int argc, char **argv) {
357    if (argc < 2) {
358        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
359        return 0;
360    }
361
362    VolumeManager *vm = VolumeManager::Instance();
363    int rc = 0;
364
365    if (!strcmp(argv[1], "list")) {
366        dumpArgs(argc, argv, -1);
367
368        listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_EXT);
369        listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_INT);
370    } else if (!strcmp(argv[1], "create")) {
371        dumpArgs(argc, argv, 5);
372        if (argc != 8) {
373            cli->sendMsg(ResponseCode::CommandSyntaxError,
374                    "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid> "
375                    "<isExternal>", false);
376            return 0;
377        }
378
379        unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
380        const bool isExternal = (atoi(argv[7]) == 1);
381        rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
382    } else if (!strcmp(argv[1], "resize")) {
383        dumpArgs(argc, argv, -1);
384        if (argc != 5) {
385            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec resize <container-id> <size_mb> <key>", false);
386            return 0;
387        }
388        unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
389        rc = vm->resizeAsec(argv[2], numSectors, argv[4]);
390    } else if (!strcmp(argv[1], "finalize")) {
391        dumpArgs(argc, argv, -1);
392        if (argc != 3) {
393            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
394            return 0;
395        }
396        rc = vm->finalizeAsec(argv[2]);
397    } else if (!strcmp(argv[1], "fixperms")) {
398        dumpArgs(argc, argv, -1);
399        if  (argc != 5) {
400            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
401            return 0;
402        }
403
404        char *endptr;
405        gid_t gid = (gid_t) strtoul(argv[3], &endptr, 10);
406        if (*endptr != '\0') {
407            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
408            return 0;
409        }
410
411        rc = vm->fixupAsecPermissions(argv[2], gid, argv[4]);
412    } else if (!strcmp(argv[1], "destroy")) {
413        dumpArgs(argc, argv, -1);
414        if (argc < 3) {
415            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
416            return 0;
417        }
418        bool force = false;
419        if (argc > 3 && !strcmp(argv[3], "force")) {
420            force = true;
421        }
422        rc = vm->destroyAsec(argv[2], force);
423    } else if (!strcmp(argv[1], "mount")) {
424        dumpArgs(argc, argv, 3);
425        if (argc != 6) {
426            cli->sendMsg(ResponseCode::CommandSyntaxError,
427                    "Usage: asec mount <namespace-id> <key> <ownerUid> <ro|rw>", false);
428            return 0;
429        }
430        bool readOnly = true;
431        if (!strcmp(argv[5], "rw")) {
432            readOnly = false;
433        }
434        rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]), readOnly);
435    } else if (!strcmp(argv[1], "unmount")) {
436        dumpArgs(argc, argv, -1);
437        if (argc < 3) {
438            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
439            return 0;
440        }
441        bool force = false;
442        if (argc > 3 && !strcmp(argv[3], "force")) {
443            force = true;
444        }
445        rc = vm->unmountAsec(argv[2], force);
446    } else if (!strcmp(argv[1], "rename")) {
447        dumpArgs(argc, argv, -1);
448        if (argc != 4) {
449            cli->sendMsg(ResponseCode::CommandSyntaxError,
450                    "Usage: asec rename <old_id> <new_id>", false);
451            return 0;
452        }
453        rc = vm->renameAsec(argv[2], argv[3]);
454    } else if (!strcmp(argv[1], "path")) {
455        dumpArgs(argc, argv, -1);
456        if (argc != 3) {
457            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
458            return 0;
459        }
460        char path[255];
461
462        if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
463            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
464            return 0;
465        }
466    } else if (!strcmp(argv[1], "fspath")) {
467        dumpArgs(argc, argv, -1);
468        if (argc != 3) {
469            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
470            return 0;
471        }
472        char path[255];
473
474        if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
475            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
476            return 0;
477        }
478    } else {
479        dumpArgs(argc, argv, -1);
480        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
481    }
482
483    if (!rc) {
484        cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
485    } else {
486        rc = ResponseCode::convertFromErrno();
487        cli->sendMsg(rc, "asec operation failed", true);
488    }
489
490    return 0;
491}
492
493CommandListener::ObbCmd::ObbCmd() :
494                 VoldCommand("obb") {
495}
496
497int CommandListener::ObbCmd::runCommand(SocketClient *cli,
498                                                      int argc, char **argv) {
499    if (argc < 2) {
500        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
501        return 0;
502    }
503
504    VolumeManager *vm = VolumeManager::Instance();
505    int rc = 0;
506
507    if (!strcmp(argv[1], "list")) {
508        dumpArgs(argc, argv, -1);
509
510        rc = vm->listMountedObbs(cli);
511    } else if (!strcmp(argv[1], "mount")) {
512            dumpArgs(argc, argv, 3);
513            if (argc != 5) {
514                cli->sendMsg(ResponseCode::CommandSyntaxError,
515                        "Usage: obb mount <filename> <key> <ownerGid>", false);
516                return 0;
517            }
518            rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
519    } else if (!strcmp(argv[1], "unmount")) {
520        dumpArgs(argc, argv, -1);
521        if (argc < 3) {
522            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
523            return 0;
524        }
525        bool force = false;
526        if (argc > 3 && !strcmp(argv[3], "force")) {
527            force = true;
528        }
529        rc = vm->unmountObb(argv[2], force);
530    } else if (!strcmp(argv[1], "path")) {
531        dumpArgs(argc, argv, -1);
532        if (argc != 3) {
533            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
534            return 0;
535        }
536        char path[255];
537
538        if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
539            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
540            return 0;
541        }
542    } else {
543        dumpArgs(argc, argv, -1);
544        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
545    }
546
547    if (!rc) {
548        cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
549    } else {
550        rc = ResponseCode::convertFromErrno();
551        cli->sendMsg(rc, "obb operation failed", true);
552    }
553
554    return 0;
555}
556
557CommandListener::CryptfsCmd::CryptfsCmd() :
558                 VoldCommand("cryptfs") {
559}
560
561static int getType(const char* type)
562{
563    if (!strcmp(type, "default")) {
564        return CRYPT_TYPE_DEFAULT;
565    } else if (!strcmp(type, "password")) {
566        return CRYPT_TYPE_PASSWORD;
567    } else if (!strcmp(type, "pin")) {
568        return CRYPT_TYPE_PIN;
569    } else if (!strcmp(type, "pattern")) {
570        return CRYPT_TYPE_PATTERN;
571    } else {
572        return -1;
573    }
574}
575
576int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
577                                                      int argc, char **argv) {
578    if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
579        cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
580        return 0;
581    }
582
583    if (argc < 2) {
584        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
585        return 0;
586    }
587
588    int rc = 0;
589
590    if (!strcmp(argv[1], "checkpw")) {
591        if (argc != 3) {
592            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
593            return 0;
594        }
595        dumpArgs(argc, argv, 2);
596        rc = cryptfs_check_passwd(argv[2]);
597    } else if (!strcmp(argv[1], "restart")) {
598        if (argc != 2) {
599            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false);
600            return 0;
601        }
602        dumpArgs(argc, argv, -1);
603        rc = cryptfs_restart();
604    } else if (!strcmp(argv[1], "cryptocomplete")) {
605        if (argc != 2) {
606            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs cryptocomplete", false);
607            return 0;
608        }
609        dumpArgs(argc, argv, -1);
610        rc = cryptfs_crypto_complete();
611    } else if (!strcmp(argv[1], "enablecrypto")) {
612        const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
613                             "default|password|pin|pattern [passwd]";
614        if ( (argc != 4 && argc != 5)
615             || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
616            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
617            return 0;
618        }
619        dumpArgs(argc, argv, 4);
620
621        int tries;
622        for (tries = 0; tries < 2; ++tries) {
623            int type = getType(argv[3]);
624            if (type == -1) {
625                cli->sendMsg(ResponseCode::CommandSyntaxError, syntax,
626                             false);
627                return 0;
628            } else if (type == CRYPT_TYPE_DEFAULT) {
629              rc = cryptfs_enable_default(argv[2], /*allow_reboot*/false);
630            } else {
631                rc = cryptfs_enable(argv[2], type, argv[4],
632                                    /*allow_reboot*/false);
633            }
634
635            if (rc == 0) {
636                break;
637            } else if (tries == 0) {
638                Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
639            }
640        }
641    } else if (!strcmp(argv[1], "changepw")) {
642        const char* syntax = "Usage: cryptfs changepw "
643                             "default|password|pin|pattern [newpasswd]";
644        const char* password;
645        if (argc == 3) {
646            password = "";
647        } else if (argc == 4) {
648            password = argv[3];
649        } else {
650            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
651            return 0;
652        }
653        int type = getType(argv[2]);
654        if (type == -1) {
655            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
656            return 0;
657        }
658        SLOGD("cryptfs changepw %s {}", argv[2]);
659        rc = cryptfs_changepw(type, password);
660    } else if (!strcmp(argv[1], "verifypw")) {
661        if (argc != 3) {
662            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
663            return 0;
664        }
665        SLOGD("cryptfs verifypw {}");
666        rc = cryptfs_verify_passwd(argv[2]);
667    } else if (!strcmp(argv[1], "getfield")) {
668        char *valbuf;
669        int valbuf_len = PROPERTY_VALUE_MAX;
670
671        if (argc != 3) {
672            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs getfield <fieldname>", false);
673            return 0;
674        }
675        dumpArgs(argc, argv, -1);
676
677        // Increase the buffer size until it is big enough for the field value stored.
678        while (1) {
679            valbuf = (char*)malloc(valbuf_len);
680            if (valbuf == NULL) {
681                cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", false);
682                return 0;
683            }
684            rc = cryptfs_getfield(argv[2], valbuf, valbuf_len);
685            if (rc != CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL) {
686                break;
687            }
688            free(valbuf);
689            valbuf_len *= 2;
690        }
691        if (rc == CRYPTO_GETFIELD_OK) {
692            cli->sendMsg(ResponseCode::CryptfsGetfieldResult, valbuf, false);
693        }
694        free(valbuf);
695    } else if (!strcmp(argv[1], "setfield")) {
696        if (argc != 4) {
697            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs setfield <fieldname> <value>", false);
698            return 0;
699        }
700        dumpArgs(argc, argv, -1);
701        rc = cryptfs_setfield(argv[2], argv[3]);
702    } else if (!strcmp(argv[1], "mountdefaultencrypted")) {
703        SLOGD("cryptfs mountdefaultencrypted");
704        dumpArgs(argc, argv, -1);
705        rc = cryptfs_mount_default_encrypted();
706    } else if (!strcmp(argv[1], "getpwtype")) {
707        SLOGD("cryptfs getpwtype");
708        dumpArgs(argc, argv, -1);
709        switch(cryptfs_get_password_type()) {
710        case CRYPT_TYPE_PASSWORD:
711            cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
712            return 0;
713        case CRYPT_TYPE_PATTERN:
714            cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
715            return 0;
716        case CRYPT_TYPE_PIN:
717            cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
718            return 0;
719        case CRYPT_TYPE_DEFAULT:
720            cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
721            return 0;
722        default:
723          /** @TODO better error and make sure handled by callers */
724            cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
725            return 0;
726        }
727    } else if (!strcmp(argv[1], "getpw")) {
728        SLOGD("cryptfs getpw");
729        dumpArgs(argc, argv, -1);
730        const char* password = cryptfs_get_password();
731        if (password) {
732            char* message = 0;
733            int size = asprintf(&message, "{{sensitive}} %s", password);
734            if (size != -1) {
735                cli->sendMsg(ResponseCode::CommandOkay, message, false);
736                memset(message, 0, size);
737                free (message);
738                return 0;
739            }
740        }
741        rc = -1;
742    } else if (!strcmp(argv[1], "clearpw")) {
743        SLOGD("cryptfs clearpw");
744        dumpArgs(argc, argv, -1);
745        cryptfs_clear_password();
746        rc = 0;
747    } else {
748        dumpArgs(argc, argv, -1);
749        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
750        return 0;
751    }
752
753    // Always report that the command succeeded and return the error code.
754    // The caller will check the return value to see what the error was.
755    char msg[255];
756    snprintf(msg, sizeof(msg), "%d", rc);
757    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
758
759    return 0;
760}
761
762CommandListener::FstrimCmd::FstrimCmd() :
763                 VoldCommand("fstrim") {
764}
765int CommandListener::FstrimCmd::runCommand(SocketClient *cli,
766                                                      int argc, char **argv) {
767    if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
768        cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run fstrim commands", false);
769        return 0;
770    }
771
772    if (argc < 2) {
773        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
774        return 0;
775    }
776
777    int rc = 0;
778
779    if (!strcmp(argv[1], "dotrim")) {
780        if (argc != 2) {
781            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: fstrim dotrim", false);
782            return 0;
783        }
784        dumpArgs(argc, argv, -1);
785        rc = fstrim_filesystems(0);
786    } else if (!strcmp(argv[1], "dodtrim")) {
787        if (argc != 2) {
788            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: fstrim dodtrim", false);
789            return 0;
790        }
791        dumpArgs(argc, argv, -1);
792        rc = fstrim_filesystems(1);   /* Do Deep Discard trim */
793    } else {
794        dumpArgs(argc, argv, -1);
795        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown fstrim cmd", false);
796    }
797
798    // Always report that the command succeeded and return the error code.
799    // The caller will check the return value to see what the error was.
800    char msg[255];
801    snprintf(msg, sizeof(msg), "%d", rc);
802    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
803
804    return 0;
805}
806