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