OSDN Git Service

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