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