CommandListener.cpp revision 10d34887b3e00e603604e0301487f9b8222c66f4
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/mount.h>
19#include <sys/socket.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <sys/wait.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <dirent.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <fs_mgr.h>
29#include <stdio.h>
30#include <string.h>
31#include <stdint.h>
32#include <inttypes.h>
33#include <ctype.h>
34
35#define LOG_TAG "VoldCmdListener"
36
37#include <android-base/logging.h>
38#include <android-base/stringprintf.h>
39#include <cutils/fs.h>
40
41#include <sysutils/SocketClient.h>
42#include <private/android_filesystem_config.h>
43
44#include "CommandListener.h"
45#include "VolumeManager.h"
46#include "VolumeBase.h"
47#include "ResponseCode.h"
48#include "Process.h"
49#include "Loop.h"
50#include "Devmapper.h"
51#include "MoveTask.h"
52#include "TrimTask.h"
53
54#define DUMP_ARGS 0
55
56CommandListener::CommandListener() :
57                 FrameworkListener("vold", true) {
58    registerCmd(new DumpCmd());
59    registerCmd(new VolumeCmd());
60    registerCmd(new AsecCmd());
61    registerCmd(new ObbCmd());
62    registerCmd(new StorageCmd());
63    registerCmd(new FstrimCmd());
64    registerCmd(new AppFuseCmd());
65}
66
67#if DUMP_ARGS
68void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
69    char buffer[4096];
70    char *p = buffer;
71
72    memset(buffer, 0, sizeof(buffer));
73    int i;
74    for (i = 0; i < argc; i++) {
75        unsigned int len = strlen(argv[i]) + 1; // Account for space
76        if (i == argObscure) {
77            len += 2; // Account for {}
78        }
79        if (((p - buffer) + len) < (sizeof(buffer)-1)) {
80            if (i == argObscure) {
81                *p++ = '{';
82                *p++ = '}';
83                *p++ = ' ';
84                continue;
85            }
86            strcpy(p, argv[i]);
87            p+= strlen(argv[i]);
88            if (i != (argc -1)) {
89                *p++ = ' ';
90            }
91        }
92    }
93    SLOGD("%s", buffer);
94}
95#else
96void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
97#endif
98
99int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
100    if (!cond) {
101        return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
102    } else {
103        return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
104    }
105}
106
107CommandListener::DumpCmd::DumpCmd() :
108                 VoldCommand("dump") {
109}
110
111int CommandListener::DumpCmd::runCommand(SocketClient *cli,
112                                         int /*argc*/, char ** /*argv*/) {
113    cli->sendMsg(0, "Dumping loop status", false);
114    if (Loop::dumpState(cli)) {
115        cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
116    }
117    cli->sendMsg(0, "Dumping DM status", false);
118    if (Devmapper::dumpState(cli)) {
119        cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
120    }
121    cli->sendMsg(0, "Dumping mounted filesystems", false);
122    FILE *fp = fopen("/proc/mounts", "r");
123    if (fp) {
124        char line[1024];
125        while (fgets(line, sizeof(line), fp)) {
126            line[strlen(line)-1] = '\0';
127            cli->sendMsg(0, line, false);;
128        }
129        fclose(fp);
130    }
131
132    cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
133    return 0;
134}
135
136CommandListener::VolumeCmd::VolumeCmd() :
137                 VoldCommand("volume") {
138}
139
140int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
141                                           int argc, char **argv) {
142    dumpArgs(argc, argv, -1);
143
144    if (argc < 2) {
145        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
146        return 0;
147    }
148
149    VolumeManager *vm = VolumeManager::Instance();
150    std::lock_guard<std::mutex> lock(vm->getLock());
151
152    // TODO: tease out methods not directly related to volumes
153
154    std::string cmd(argv[1]);
155    if (cmd == "reset") {
156        return sendGenericOkFail(cli, vm->reset());
157
158    } else if (cmd == "shutdown") {
159        return sendGenericOkFail(cli, vm->shutdown());
160
161    } else if (cmd == "debug") {
162        return sendGenericOkFail(cli, vm->setDebug(true));
163
164    } else if (cmd == "partition" && argc > 3) {
165        // partition [diskId] [public|private|mixed] [ratio]
166        std::string id(argv[2]);
167        auto disk = vm->findDisk(id);
168        if (disk == nullptr) {
169            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
170        }
171
172        std::string type(argv[3]);
173        if (type == "public") {
174            return sendGenericOkFail(cli, disk->partitionPublic());
175        } else if (type == "private") {
176            return sendGenericOkFail(cli, disk->partitionPrivate());
177        } else if (type == "mixed") {
178            if (argc < 4) {
179                return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
180            }
181            int frac = atoi(argv[4]);
182            return sendGenericOkFail(cli, disk->partitionMixed(frac));
183        } else {
184            return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
185        }
186
187    } else if (cmd == "mkdirs" && argc > 2) {
188        // mkdirs [path]
189        return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
190
191    } else if (cmd == "user_added" && argc > 3) {
192        // user_added [user] [serial]
193        return sendGenericOkFail(cli, vm->onUserAdded(atoi(argv[2]), atoi(argv[3])));
194
195    } else if (cmd == "user_removed" && argc > 2) {
196        // user_removed [user]
197        return sendGenericOkFail(cli, vm->onUserRemoved(atoi(argv[2])));
198
199    } else if (cmd == "user_started" && argc > 2) {
200        // user_started [user]
201        return sendGenericOkFail(cli, vm->onUserStarted(atoi(argv[2])));
202
203    } else if (cmd == "user_stopped" && argc > 2) {
204        // user_stopped [user]
205        return sendGenericOkFail(cli, vm->onUserStopped(atoi(argv[2])));
206
207    } else if (cmd == "mount" && argc > 2) {
208        // mount [volId] [flags] [user]
209        std::string id(argv[2]);
210        auto vol = vm->findVolume(id);
211        if (vol == nullptr) {
212            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
213        }
214
215        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
216        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
217
218        vol->setMountFlags(mountFlags);
219        vol->setMountUserId(mountUserId);
220
221        int res = vol->mount();
222        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
223            vm->setPrimary(vol);
224        }
225        return sendGenericOkFail(cli, res);
226
227    } else if (cmd == "unmount" && argc > 2) {
228        // unmount [volId]
229        std::string id(argv[2]);
230        auto vol = vm->findVolume(id);
231        if (vol == nullptr) {
232            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
233        }
234
235        return sendGenericOkFail(cli, vol->unmount());
236
237    } else if (cmd == "format" && argc > 3) {
238        // format [volId] [fsType|auto]
239        std::string id(argv[2]);
240        std::string fsType(argv[3]);
241        auto vol = vm->findVolume(id);
242        if (vol == nullptr) {
243            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
244        }
245
246        return sendGenericOkFail(cli, vol->format(fsType));
247
248    } else if (cmd == "move_storage" && argc > 3) {
249        // move_storage [fromVolId] [toVolId]
250        auto fromVol = vm->findVolume(std::string(argv[2]));
251        auto toVol = vm->findVolume(std::string(argv[3]));
252        if (fromVol == nullptr || toVol == nullptr) {
253            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
254        }
255
256        (new android::vold::MoveTask(fromVol, toVol))->start();
257        return sendGenericOkFail(cli, 0);
258
259    } else if (cmd == "benchmark" && argc > 2) {
260        // benchmark [volId]
261        std::string id(argv[2]);
262        nsecs_t res = vm->benchmarkPrivate(id);
263        return cli->sendMsg(ResponseCode::CommandOkay,
264                android::base::StringPrintf("%" PRId64, res).c_str(), false);
265
266    } else if (cmd == "forget_partition" && argc > 2) {
267        // forget_partition [partGuid]
268        std::string partGuid(argv[2]);
269        return sendGenericOkFail(cli, vm->forgetPartition(partGuid));
270
271    } else if (cmd == "remount_uid" && argc > 3) {
272        // remount_uid [uid] [none|default|read|write]
273        uid_t uid = atoi(argv[2]);
274        std::string mode(argv[3]);
275        return sendGenericOkFail(cli, vm->remountUid(uid, mode));
276    }
277
278    return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
279}
280
281CommandListener::StorageCmd::StorageCmd() :
282                 VoldCommand("storage") {
283}
284
285int CommandListener::StorageCmd::runCommand(SocketClient *cli,
286                                                      int argc, char **argv) {
287    /* Guarantied to be initialized by vold's main() before the CommandListener is active */
288    extern struct fstab *fstab;
289
290    dumpArgs(argc, argv, -1);
291
292    if (argc < 2) {
293        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
294        return 0;
295    }
296
297    if (!strcmp(argv[1], "mountall")) {
298        if (argc != 2) {
299            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
300            return 0;
301        }
302        fs_mgr_mount_all(fstab);
303        cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
304        return 0;
305    }
306    if (!strcmp(argv[1], "users")) {
307        DIR *dir;
308        struct dirent *de;
309
310        if (argc < 3) {
311            cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument: user <mountpoint>", false);
312            return 0;
313        }
314        if (!(dir = opendir("/proc"))) {
315            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
316            return 0;
317        }
318
319        while ((de = readdir(dir))) {
320            int pid = Process::getPid(de->d_name);
321
322            if (pid < 0) {
323                continue;
324            }
325
326            char processName[255];
327            Process::getProcessName(pid, processName, sizeof(processName));
328
329            if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
330                Process::checkFileMaps(pid, argv[2]) ||
331                Process::checkSymLink(pid, argv[2], "cwd") ||
332                Process::checkSymLink(pid, argv[2], "root") ||
333                Process::checkSymLink(pid, argv[2], "exe")) {
334
335                char msg[1024];
336                snprintf(msg, sizeof(msg), "%d %s", pid, processName);
337                cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
338            }
339        }
340        closedir(dir);
341        cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
342    } else {
343        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
344    }
345    return 0;
346}
347
348CommandListener::AsecCmd::AsecCmd() :
349                 VoldCommand("asec") {
350}
351
352void CommandListener::AsecCmd::listAsecsInDirectory(SocketClient *cli, const char *directory) {
353    DIR *d = opendir(directory);
354
355    if (!d) {
356        cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
357        return;
358    }
359
360    size_t dirent_len = offsetof(struct dirent, d_name) +
361            fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
362
363    struct dirent *dent = (struct dirent *) malloc(dirent_len);
364    if (dent == NULL) {
365        cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", true);
366        return;
367    }
368
369    struct dirent *result;
370
371    while (!readdir_r(d, dent, &result) && result != NULL) {
372        if (dent->d_name[0] == '.')
373            continue;
374        if (dent->d_type != DT_REG)
375            continue;
376        size_t name_len = strlen(dent->d_name);
377        if (name_len > 5 && name_len < 260 &&
378                !strcmp(&dent->d_name[name_len - 5], ".asec")) {
379            char id[255];
380            memset(id, 0, sizeof(id));
381            strlcpy(id, dent->d_name, name_len - 4);
382            cli->sendMsg(ResponseCode::AsecListResult, id, false);
383        }
384    }
385    closedir(d);
386
387    free(dent);
388}
389
390int CommandListener::AsecCmd::runCommand(SocketClient *cli,
391                                                      int argc, char **argv) {
392    if (argc < 2) {
393        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
394        return 0;
395    }
396
397    VolumeManager *vm = VolumeManager::Instance();
398    int rc = 0;
399
400    if (!strcmp(argv[1], "list")) {
401        dumpArgs(argc, argv, -1);
402
403        listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_EXT);
404        listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_INT);
405    } else if (!strcmp(argv[1], "create")) {
406        dumpArgs(argc, argv, 5);
407        if (argc != 8) {
408            cli->sendMsg(ResponseCode::CommandSyntaxError,
409                    "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid> "
410                    "<isExternal>", false);
411            return 0;
412        }
413
414        unsigned long numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
415        const bool isExternal = (atoi(argv[7]) == 1);
416        rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
417    } else if (!strcmp(argv[1], "resize")) {
418        dumpArgs(argc, argv, -1);
419        if (argc != 5) {
420            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec resize <container-id> <size_mb> <key>", false);
421            return 0;
422        }
423        unsigned long numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
424        rc = vm->resizeAsec(argv[2], numSectors, argv[4]);
425    } else if (!strcmp(argv[1], "finalize")) {
426        dumpArgs(argc, argv, -1);
427        if (argc != 3) {
428            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
429            return 0;
430        }
431        rc = vm->finalizeAsec(argv[2]);
432    } else if (!strcmp(argv[1], "fixperms")) {
433        dumpArgs(argc, argv, -1);
434        if  (argc != 5) {
435            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
436            return 0;
437        }
438
439        char *endptr;
440        gid_t gid = (gid_t) strtoul(argv[3], &endptr, 10);
441        if (*endptr != '\0') {
442            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
443            return 0;
444        }
445
446        rc = vm->fixupAsecPermissions(argv[2], gid, argv[4]);
447    } else if (!strcmp(argv[1], "destroy")) {
448        dumpArgs(argc, argv, -1);
449        if (argc < 3) {
450            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
451            return 0;
452        }
453        bool force = false;
454        if (argc > 3 && !strcmp(argv[3], "force")) {
455            force = true;
456        }
457        rc = vm->destroyAsec(argv[2], force);
458    } else if (!strcmp(argv[1], "mount")) {
459        dumpArgs(argc, argv, 3);
460        if (argc != 6) {
461            cli->sendMsg(ResponseCode::CommandSyntaxError,
462                    "Usage: asec mount <namespace-id> <key> <ownerUid> <ro|rw>", false);
463            return 0;
464        }
465        bool readOnly = true;
466        if (!strcmp(argv[5], "rw")) {
467            readOnly = false;
468        }
469        rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]), readOnly);
470    } else if (!strcmp(argv[1], "unmount")) {
471        dumpArgs(argc, argv, -1);
472        if (argc < 3) {
473            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
474            return 0;
475        }
476        bool force = false;
477        if (argc > 3 && !strcmp(argv[3], "force")) {
478            force = true;
479        }
480        rc = vm->unmountAsec(argv[2], force);
481    } else if (!strcmp(argv[1], "rename")) {
482        dumpArgs(argc, argv, -1);
483        if (argc != 4) {
484            cli->sendMsg(ResponseCode::CommandSyntaxError,
485                    "Usage: asec rename <old_id> <new_id>", false);
486            return 0;
487        }
488        rc = vm->renameAsec(argv[2], argv[3]);
489    } else if (!strcmp(argv[1], "path")) {
490        dumpArgs(argc, argv, -1);
491        if (argc != 3) {
492            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
493            return 0;
494        }
495        char path[255];
496
497        if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
498            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
499            return 0;
500        }
501    } else if (!strcmp(argv[1], "fspath")) {
502        dumpArgs(argc, argv, -1);
503        if (argc != 3) {
504            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
505            return 0;
506        }
507        char path[255];
508
509        if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
510            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
511            return 0;
512        }
513    } else {
514        dumpArgs(argc, argv, -1);
515        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
516    }
517
518    if (!rc) {
519        cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
520    } else {
521        rc = ResponseCode::convertFromErrno();
522        cli->sendMsg(rc, "asec operation failed", true);
523    }
524
525    return 0;
526}
527
528CommandListener::ObbCmd::ObbCmd() :
529                 VoldCommand("obb") {
530}
531
532int CommandListener::ObbCmd::runCommand(SocketClient *cli,
533                                                      int argc, char **argv) {
534    if (argc < 2) {
535        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
536        return 0;
537    }
538
539    VolumeManager *vm = VolumeManager::Instance();
540    int rc = 0;
541
542    if (!strcmp(argv[1], "list")) {
543        dumpArgs(argc, argv, -1);
544
545        rc = vm->listMountedObbs(cli);
546    } else if (!strcmp(argv[1], "mount")) {
547            dumpArgs(argc, argv, 3);
548            if (argc != 5) {
549                cli->sendMsg(ResponseCode::CommandSyntaxError,
550                        "Usage: obb mount <filename> <key> <ownerGid>", false);
551                return 0;
552            }
553            rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
554    } else if (!strcmp(argv[1], "unmount")) {
555        dumpArgs(argc, argv, -1);
556        if (argc < 3) {
557            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
558            return 0;
559        }
560        bool force = false;
561        if (argc > 3 && !strcmp(argv[3], "force")) {
562            force = true;
563        }
564        rc = vm->unmountObb(argv[2], force);
565    } else if (!strcmp(argv[1], "path")) {
566        dumpArgs(argc, argv, -1);
567        if (argc != 3) {
568            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
569            return 0;
570        }
571        char path[255];
572
573        if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
574            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
575            return 0;
576        }
577    } else {
578        dumpArgs(argc, argv, -1);
579        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
580    }
581
582    if (!rc) {
583        cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
584    } else {
585        rc = ResponseCode::convertFromErrno();
586        cli->sendMsg(rc, "obb operation failed", true);
587    }
588
589    return 0;
590}
591
592CommandListener::FstrimCmd::FstrimCmd() :
593                 VoldCommand("fstrim") {
594}
595int CommandListener::FstrimCmd::runCommand(SocketClient *cli,
596                                                      int argc, char **argv) {
597    if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
598        cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run fstrim commands", false);
599        return 0;
600    }
601
602    if (argc < 2) {
603        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
604        return 0;
605    }
606
607    VolumeManager *vm = VolumeManager::Instance();
608    std::lock_guard<std::mutex> lock(vm->getLock());
609
610    int flags = 0;
611
612    std::string cmd(argv[1]);
613    if (cmd == "dotrim") {
614        flags = 0;
615    } else if (cmd == "dotrimbench") {
616        flags = android::vold::TrimTask::Flags::kBenchmarkAfter;
617    } else if (cmd == "dodtrim") {
618        flags = android::vold::TrimTask::Flags::kDeepTrim;
619    } else if (cmd == "dodtrimbench") {
620        flags = android::vold::TrimTask::Flags::kDeepTrim
621                | android::vold::TrimTask::Flags::kBenchmarkAfter;
622    }
623
624    (new android::vold::TrimTask(flags))->start();
625    return sendGenericOkFail(cli, 0);
626}
627
628static size_t kAppFuseMaxMountPointName = 32;
629
630static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
631    if (name.size() > kAppFuseMaxMountPointName) {
632        LOG(ERROR) << "AppFuse mount name is too long.";
633        return -EINVAL;
634    }
635    for (size_t i = 0; i < name.size(); i++) {
636        if (!isalnum(name[i])) {
637            LOG(ERROR) << "AppFuse mount name contains invalid character.";
638            return -EINVAL;
639        }
640    }
641    *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
642    return android::OK;
643}
644
645static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
646    // Remove existing mount.
647    android::vold::ForceUnmount(path);
648
649    const auto opts = android::base::StringPrintf(
650            "fd=%i,"
651            "rootmode=40000,"
652            "default_permissions,"
653            "allow_other,"
654            "user_id=%d,group_id=%d",
655            device_fd,
656            uid,
657            uid);
658
659    const int result = TEMP_FAILURE_RETRY(mount(
660            "/dev/fuse", path.c_str(), "fuse",
661            MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
662    if (result != 0) {
663        PLOG(ERROR) << "Failed to mount " << path;
664        return -errno;
665    }
666
667    return android::OK;
668}
669
670static android::status_t runCommandInNamespace(const std::string& command,
671                                               uid_t uid,
672                                               pid_t pid,
673                                               const std::string& path,
674                                               int device_fd) {
675    LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path << " in namespace "
676               << uid;
677
678    const android::vold::ScopedDir dir(opendir("/proc"));
679    if (dir.get() == nullptr) {
680        PLOG(ERROR) << "Failed to open /proc";
681        return -errno;
682    }
683
684    // Obtains process file descriptor.
685    const std::string pid_str = android::base::StringPrintf("%d", pid);
686    const android::vold::ScopedFd pid_fd(
687            openat(dirfd(dir.get()), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
688    if (pid_fd.get() == -1) {
689        PLOG(ERROR) << "Failed to open /proc/" << pid;
690        return -errno;
691    }
692
693    // Check UID of process.
694    {
695        struct stat sb;
696        const int result = fstat(pid_fd.get(), &sb);
697        if (result == -1) {
698            PLOG(ERROR) << "Failed to stat /proc/" << pid;
699            return -errno;
700        }
701        if (sb.st_uid != uid) {
702            LOG(ERROR) << "Mismatch UID expected=" << uid << ", actual=" << sb.st_uid;
703            return -EPERM;
704        }
705    }
706
707    // Matches so far, but refuse to touch if in root namespace
708    {
709        char rootName[PATH_MAX];
710        char pidName[PATH_MAX];
711        const int root_result =
712                android::vold::SaneReadLinkAt(dirfd(dir.get()), "1/ns/mnt", rootName, PATH_MAX);
713        const int pid_result =
714                android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
715        if (root_result == -1) {
716            LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
717            return -EPERM;
718        }
719        if (pid_result == -1) {
720            LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
721            return -EPERM;
722        }
723        if (!strcmp(rootName, pidName)) {
724            LOG(ERROR) << "Don't mount appfuse in root namespace";
725            return -EPERM;
726        }
727    }
728
729    // We purposefully leave the namespace open across the fork
730    android::vold::ScopedFd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY));
731    if (ns_fd.get() < 0) {
732        PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
733        return -errno;
734    }
735
736    int child = fork();
737    if (child == 0) {
738        if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
739            PLOG(ERROR) << "Failed to setns";
740            _exit(-errno);
741        }
742
743        if (command == "mount") {
744            _exit(mountInNamespace(uid, device_fd, path));
745        } else if (command == "unmount") {
746            android::vold::ForceUnmount(path);
747            _exit(android::OK);
748        } else {
749            LOG(ERROR) << "Unknown appfuse command " << command;
750            _exit(-EPERM);
751        }
752    }
753
754    if (child == -1) {
755        PLOG(ERROR) << "Failed to folk child process";
756        return -errno;
757    }
758
759    android::status_t status;
760    TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
761
762    return status;
763}
764
765CommandListener::AppFuseCmd::AppFuseCmd() : VoldCommand("appfuse") {}
766
767int CommandListener::AppFuseCmd::runCommand(SocketClient *cli, int argc, char **argv) {
768    if (argc < 2) {
769        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
770        return 0;
771    }
772
773    const std::string command(argv[1]);
774
775    if (command == "mount" && argc == 5) {
776        const uid_t uid = atoi(argv[2]);
777        const pid_t pid = atoi(argv[3]);
778        const std::string name(argv[4]);
779
780        // Check mount point name.
781        std::string path;
782        if (getMountPath(uid, name, &path) != android::OK) {
783            return cli->sendMsg(ResponseCode::CommandParameterError,
784                                "Invalid mount point name.",
785                                false);
786        }
787
788        // Create directories.
789        {
790            const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
791            if (result != android::OK) {
792                PLOG(ERROR) << "Failed to prepare directory " << path;
793                return sendGenericOkFail(cli, result);
794            }
795        }
796
797        // Open device FD.
798        android::vold::ScopedFd device_fd(open("/dev/fuse", O_RDWR));
799        if (device_fd.get() == -1) {
800            PLOG(ERROR) << "Failed to open /dev/fuse";
801            return sendGenericOkFail(cli, -errno);
802        }
803
804        // Mount.
805        {
806            const android::status_t result =
807                    runCommandInNamespace(command, uid, pid, path, device_fd.get());
808            if (result != android::OK) {
809                return sendGenericOkFail(cli, result);
810            }
811        }
812
813        return sendFd(cli, device_fd.get());
814    } else if (command == "unmount" && argc == 5) {
815        const uid_t uid = atoi(argv[2]);
816        const uid_t pid = atoi(argv[3]);
817        const std::string name(argv[4]);
818
819        // Check mount point name.
820        std::string path;
821        if (getMountPath(uid, name, &path) != android::OK) {
822            return cli->sendMsg(ResponseCode::CommandParameterError,
823                                "Invalid mount point name.",
824                                false);
825        }
826
827        const android::status_t result =
828                runCommandInNamespace(command, uid, pid, path, -1 /* device_fd */);
829        return sendGenericOkFail(cli, result);
830    }
831
832    return cli->sendMsg(ResponseCode::CommandSyntaxError,  "Unknown appfuse cmd", false);
833}
834
835android::status_t CommandListener::AppFuseCmd::sendFd(SocketClient *cli, int fd) {
836    struct iovec data;
837    char dataBuffer[128];
838    char controlBuffer[CMSG_SPACE(sizeof(int))];
839    struct msghdr message;
840
841    // Message.
842    memset(&message, 0, sizeof(struct msghdr));
843    message.msg_iov = &data;
844    message.msg_iovlen = 1;
845    message.msg_control = controlBuffer;
846    message.msg_controllen = CMSG_SPACE(sizeof(int));
847
848    // Data.
849    data.iov_base = dataBuffer;
850    data.iov_len = snprintf(dataBuffer,
851                            sizeof(dataBuffer),
852                            "200 %d AppFuse command succeeded",
853                            cli->getCmdNum()) + 1;
854
855    // Control.
856    struct cmsghdr* const controlMessage = CMSG_FIRSTHDR(&message);
857    memset(controlBuffer, 0, CMSG_SPACE(sizeof(int)));
858    controlMessage->cmsg_level = SOL_SOCKET;
859    controlMessage->cmsg_type = SCM_RIGHTS;
860    controlMessage->cmsg_len = CMSG_LEN(sizeof(int));
861    *((int *) CMSG_DATA(controlMessage)) = fd;
862
863    const int result = TEMP_FAILURE_RETRY(sendmsg(cli->getSocket(), &message, 0));
864    if (result == -1) {
865        PLOG(ERROR) << "Failed to send FD from vold";
866        return -errno;
867    }
868
869    return android::OK;
870}
871