Home | History | Annotate | Download | only in vold
      1 /*
      2  * Copyright (C) 2015 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 "sehandle.h"
     18 #include "Utils.h"
     19 #include "Process.h"
     20 #include "VolumeManager.h"
     21 
     22 #include <android-base/file.h>
     23 #include <android-base/logging.h>
     24 #include <android-base/properties.h>
     25 #include <android-base/stringprintf.h>
     26 #include <cutils/fs.h>
     27 #include <logwrap/logwrap.h>
     28 #include <private/android_filesystem_config.h>
     29 
     30 #include <mutex>
     31 #include <dirent.h>
     32 #include <fcntl.h>
     33 #include <linux/fs.h>
     34 #include <stdlib.h>
     35 #include <sys/mount.h>
     36 #include <sys/types.h>
     37 #include <sys/stat.h>
     38 #include <sys/sysmacros.h>
     39 #include <sys/wait.h>
     40 #include <sys/statvfs.h>
     41 
     42 #ifndef UMOUNT_NOFOLLOW
     43 #define UMOUNT_NOFOLLOW    0x00000008  /* Don't follow symlink on umount */
     44 #endif
     45 
     46 using android::base::ReadFileToString;
     47 using android::base::StringPrintf;
     48 
     49 namespace android {
     50 namespace vold {
     51 
     52 security_context_t sBlkidContext = nullptr;
     53 security_context_t sBlkidUntrustedContext = nullptr;
     54 security_context_t sFsckContext = nullptr;
     55 security_context_t sFsckUntrustedContext = nullptr;
     56 
     57 static const char* kBlkidPath = "/system/bin/blkid";
     58 static const char* kKeyPath = "/data/misc/vold";
     59 
     60 static const char* kProcFilesystems = "/proc/filesystems";
     61 
     62 status_t CreateDeviceNode(const std::string& path, dev_t dev) {
     63     const char* cpath = path.c_str();
     64     status_t res = 0;
     65 
     66     char* secontext = nullptr;
     67     if (sehandle) {
     68         if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
     69             setfscreatecon(secontext);
     70         }
     71     }
     72 
     73     mode_t mode = 0660 | S_IFBLK;
     74     if (mknod(cpath, mode, dev) < 0) {
     75         if (errno != EEXIST) {
     76             PLOG(ERROR) << "Failed to create device node for " << major(dev)
     77                     << ":" << minor(dev) << " at " << path;
     78             res = -errno;
     79         }
     80     }
     81 
     82     if (secontext) {
     83         setfscreatecon(nullptr);
     84         freecon(secontext);
     85     }
     86 
     87     return res;
     88 }
     89 
     90 status_t DestroyDeviceNode(const std::string& path) {
     91     const char* cpath = path.c_str();
     92     if (TEMP_FAILURE_RETRY(unlink(cpath))) {
     93         return -errno;
     94     } else {
     95         return OK;
     96     }
     97 }
     98 
     99 status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
    100     const char* cpath = path.c_str();
    101 
    102     char* secontext = nullptr;
    103     if (sehandle) {
    104         if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) {
    105             setfscreatecon(secontext);
    106         }
    107     }
    108 
    109     int res = fs_prepare_dir(cpath, mode, uid, gid);
    110 
    111     if (secontext) {
    112         setfscreatecon(nullptr);
    113         freecon(secontext);
    114     }
    115 
    116     if (res == 0) {
    117         return OK;
    118     } else {
    119         return -errno;
    120     }
    121 }
    122 
    123 status_t ForceUnmount(const std::string& path) {
    124     const char* cpath = path.c_str();
    125     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
    126         return OK;
    127     }
    128     // Apps might still be handling eject request, so wait before
    129     // we start sending signals
    130     if (!VolumeManager::shutting_down) sleep(5);
    131 
    132     Process::killProcessesWithOpenFiles(cpath, SIGINT);
    133     if (!VolumeManager::shutting_down) sleep(5);
    134     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
    135         return OK;
    136     }
    137 
    138     Process::killProcessesWithOpenFiles(cpath, SIGTERM);
    139     if (!VolumeManager::shutting_down) sleep(5);
    140     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
    141         return OK;
    142     }
    143 
    144     Process::killProcessesWithOpenFiles(cpath, SIGKILL);
    145     if (!VolumeManager::shutting_down) sleep(5);
    146     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
    147         return OK;
    148     }
    149 
    150     return -errno;
    151 }
    152 
    153 status_t KillProcessesUsingPath(const std::string& path) {
    154     const char* cpath = path.c_str();
    155     if (Process::killProcessesWithOpenFiles(cpath, SIGINT) == 0) {
    156         return OK;
    157     }
    158     if (!VolumeManager::shutting_down) sleep(5);
    159 
    160     if (Process::killProcessesWithOpenFiles(cpath, SIGTERM) == 0) {
    161         return OK;
    162     }
    163     if (!VolumeManager::shutting_down) sleep(5);
    164 
    165     if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
    166         return OK;
    167     }
    168     if (!VolumeManager::shutting_down) sleep(5);
    169 
    170     // Send SIGKILL a second time to determine if we've
    171     // actually killed everyone with open files
    172     if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
    173         return OK;
    174     }
    175     PLOG(ERROR) << "Failed to kill processes using " << path;
    176     return -EBUSY;
    177 }
    178 
    179 status_t BindMount(const std::string& source, const std::string& target) {
    180     if (::mount(source.c_str(), target.c_str(), "", MS_BIND, NULL)) {
    181         PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
    182         return -errno;
    183     }
    184     return OK;
    185 }
    186 
    187 static status_t readMetadata(const std::string& path, std::string& fsType,
    188         std::string& fsUuid, std::string& fsLabel, bool untrusted) {
    189     fsType.clear();
    190     fsUuid.clear();
    191     fsLabel.clear();
    192 
    193     std::vector<std::string> cmd;
    194     cmd.push_back(kBlkidPath);
    195     cmd.push_back("-c");
    196     cmd.push_back("/dev/null");
    197     cmd.push_back("-s");
    198     cmd.push_back("TYPE");
    199     cmd.push_back("-s");
    200     cmd.push_back("UUID");
    201     cmd.push_back("-s");
    202     cmd.push_back("LABEL");
    203     cmd.push_back(path);
    204 
    205     std::vector<std::string> output;
    206     status_t res = ForkExecvp(cmd, output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
    207     if (res != OK) {
    208         LOG(WARNING) << "blkid failed to identify " << path;
    209         return res;
    210     }
    211 
    212     char value[128];
    213     for (const auto& line : output) {
    214         // Extract values from blkid output, if defined
    215         const char* cline = line.c_str();
    216         const char* start = strstr(cline, "TYPE=");
    217         if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
    218             fsType = value;
    219         }
    220 
    221         start = strstr(cline, "UUID=");
    222         if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
    223             fsUuid = value;
    224         }
    225 
    226         start = strstr(cline, "LABEL=");
    227         if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
    228             fsLabel = value;
    229         }
    230     }
    231 
    232     return OK;
    233 }
    234 
    235 status_t ReadMetadata(const std::string& path, std::string& fsType,
    236         std::string& fsUuid, std::string& fsLabel) {
    237     return readMetadata(path, fsType, fsUuid, fsLabel, false);
    238 }
    239 
    240 status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
    241         std::string& fsUuid, std::string& fsLabel) {
    242     return readMetadata(path, fsType, fsUuid, fsLabel, true);
    243 }
    244 
    245 status_t ForkExecvp(const std::vector<std::string>& args) {
    246     return ForkExecvp(args, nullptr);
    247 }
    248 
    249 status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
    250     size_t argc = args.size();
    251     char** argv = (char**) calloc(argc, sizeof(char*));
    252     for (size_t i = 0; i < argc; i++) {
    253         argv[i] = (char*) args[i].c_str();
    254         if (i == 0) {
    255             LOG(VERBOSE) << args[i];
    256         } else {
    257             LOG(VERBOSE) << "    " << args[i];
    258         }
    259     }
    260 
    261     if (setexeccon(context)) {
    262         LOG(ERROR) << "Failed to setexeccon";
    263         abort();
    264     }
    265     status_t res = android_fork_execvp(argc, argv, NULL, false, true);
    266     if (setexeccon(nullptr)) {
    267         LOG(ERROR) << "Failed to setexeccon";
    268         abort();
    269     }
    270 
    271     free(argv);
    272     return res;
    273 }
    274 
    275 status_t ForkExecvp(const std::vector<std::string>& args,
    276         std::vector<std::string>& output) {
    277     return ForkExecvp(args, output, nullptr);
    278 }
    279 
    280 status_t ForkExecvp(const std::vector<std::string>& args,
    281         std::vector<std::string>& output, security_context_t context) {
    282     std::string cmd;
    283     for (size_t i = 0; i < args.size(); i++) {
    284         cmd += args[i] + " ";
    285         if (i == 0) {
    286             LOG(VERBOSE) << args[i];
    287         } else {
    288             LOG(VERBOSE) << "    " << args[i];
    289         }
    290     }
    291     output.clear();
    292 
    293     if (setexeccon(context)) {
    294         LOG(ERROR) << "Failed to setexeccon";
    295         abort();
    296     }
    297     FILE* fp = popen(cmd.c_str(), "r"); // NOLINT
    298     if (setexeccon(nullptr)) {
    299         LOG(ERROR) << "Failed to setexeccon";
    300         abort();
    301     }
    302 
    303     if (!fp) {
    304         PLOG(ERROR) << "Failed to popen " << cmd;
    305         return -errno;
    306     }
    307     char line[1024];
    308     while (fgets(line, sizeof(line), fp) != nullptr) {
    309         LOG(VERBOSE) << line;
    310         output.push_back(std::string(line));
    311     }
    312     if (pclose(fp) != 0) {
    313         PLOG(ERROR) << "Failed to pclose " << cmd;
    314         return -errno;
    315     }
    316 
    317     return OK;
    318 }
    319 
    320 pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
    321     size_t argc = args.size();
    322     char** argv = (char**) calloc(argc + 1, sizeof(char*));
    323     for (size_t i = 0; i < argc; i++) {
    324         argv[i] = (char*) args[i].c_str();
    325         if (i == 0) {
    326             LOG(VERBOSE) << args[i];
    327         } else {
    328             LOG(VERBOSE) << "    " << args[i];
    329         }
    330     }
    331 
    332     pid_t pid = fork();
    333     if (pid == 0) {
    334         close(STDIN_FILENO);
    335         close(STDOUT_FILENO);
    336         close(STDERR_FILENO);
    337 
    338         if (execvp(argv[0], argv)) {
    339             PLOG(ERROR) << "Failed to exec";
    340         }
    341 
    342         _exit(1);
    343     }
    344 
    345     if (pid == -1) {
    346         PLOG(ERROR) << "Failed to exec";
    347     }
    348 
    349     free(argv);
    350     return pid;
    351 }
    352 
    353 status_t ReadRandomBytes(size_t bytes, std::string& out) {
    354     out.resize(bytes);
    355     return ReadRandomBytes(bytes, &out[0]);
    356 }
    357 
    358 status_t ReadRandomBytes(size_t bytes, char* buf) {
    359     int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
    360     if (fd == -1) {
    361         return -errno;
    362     }
    363 
    364     size_t n;
    365     while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) {
    366         bytes -= n;
    367         buf += n;
    368     }
    369     close(fd);
    370 
    371     if (bytes == 0) {
    372         return OK;
    373     } else {
    374         return -EIO;
    375     }
    376 }
    377 
    378 status_t GenerateRandomUuid(std::string& out) {
    379     status_t res = ReadRandomBytes(16, out);
    380     if (res == OK) {
    381         out[6] &= 0x0f;  /* clear version        */
    382         out[6] |= 0x40;  /* set to version 4     */
    383         out[8] &= 0x3f;  /* clear variant        */
    384         out[8] |= 0x80;  /* set to IETF variant  */
    385     }
    386     return res;
    387 }
    388 
    389 status_t HexToStr(const std::string& hex, std::string& str) {
    390     str.clear();
    391     bool even = true;
    392     char cur = 0;
    393     for (size_t i = 0; i < hex.size(); i++) {
    394         int val = 0;
    395         switch (hex[i]) {
    396         case ' ': case '-': case ':': continue;
    397         case 'f': case 'F': val = 15; break;
    398         case 'e': case 'E': val = 14; break;
    399         case 'd': case 'D': val = 13; break;
    400         case 'c': case 'C': val = 12; break;
    401         case 'b': case 'B': val = 11; break;
    402         case 'a': case 'A': val = 10; break;
    403         case '9': val = 9; break;
    404         case '8': val = 8; break;
    405         case '7': val = 7; break;
    406         case '6': val = 6; break;
    407         case '5': val = 5; break;
    408         case '4': val = 4; break;
    409         case '3': val = 3; break;
    410         case '2': val = 2; break;
    411         case '1': val = 1; break;
    412         case '0': val = 0; break;
    413         default: return -EINVAL;
    414         }
    415 
    416         if (even) {
    417             cur = val << 4;
    418         } else {
    419             cur += val;
    420             str.push_back(cur);
    421             cur = 0;
    422         }
    423         even = !even;
    424     }
    425     return even ? OK : -EINVAL;
    426 }
    427 
    428 static const char* kLookup = "0123456789abcdef";
    429 
    430 status_t StrToHex(const std::string& str, std::string& hex) {
    431     hex.clear();
    432     for (size_t i = 0; i < str.size(); i++) {
    433         hex.push_back(kLookup[(str[i] & 0xF0) >> 4]);
    434         hex.push_back(kLookup[str[i] & 0x0F]);
    435     }
    436     return OK;
    437 }
    438 
    439 status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex) {
    440     hex.clear();
    441     for (size_t i = 0; i < str.size(); i++) {
    442         hex.push_back(kLookup[(str.data()[i] & 0xF0) >> 4]);
    443         hex.push_back(kLookup[str.data()[i] & 0x0F]);
    444     }
    445     return OK;
    446 }
    447 
    448 status_t NormalizeHex(const std::string& in, std::string& out) {
    449     std::string tmp;
    450     if (HexToStr(in, tmp)) {
    451         return -EINVAL;
    452     }
    453     return StrToHex(tmp, out);
    454 }
    455 
    456 uint64_t GetFreeBytes(const std::string& path) {
    457     struct statvfs sb;
    458     if (statvfs(path.c_str(), &sb) == 0) {
    459         return (uint64_t) sb.f_bavail * sb.f_frsize;
    460     } else {
    461         return -1;
    462     }
    463 }
    464 
    465 // TODO: borrowed from frameworks/native/libs/diskusage/ which should
    466 // eventually be migrated into system/
    467 static int64_t stat_size(struct stat *s) {
    468     int64_t blksize = s->st_blksize;
    469     // count actual blocks used instead of nominal file size
    470     int64_t size = s->st_blocks * 512;
    471 
    472     if (blksize) {
    473         /* round up to filesystem block size */
    474         size = (size + blksize - 1) & (~(blksize - 1));
    475     }
    476 
    477     return size;
    478 }
    479 
    480 // TODO: borrowed from frameworks/native/libs/diskusage/ which should
    481 // eventually be migrated into system/
    482 int64_t calculate_dir_size(int dfd) {
    483     int64_t size = 0;
    484     struct stat s;
    485     DIR *d;
    486     struct dirent *de;
    487 
    488     d = fdopendir(dfd);
    489     if (d == NULL) {
    490         close(dfd);
    491         return 0;
    492     }
    493 
    494     while ((de = readdir(d))) {
    495         const char *name = de->d_name;
    496         if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
    497             size += stat_size(&s);
    498         }
    499         if (de->d_type == DT_DIR) {
    500             int subfd;
    501 
    502             /* always skip "." and ".." */
    503             if (name[0] == '.') {
    504                 if (name[1] == 0)
    505                     continue;
    506                 if ((name[1] == '.') && (name[2] == 0))
    507                     continue;
    508             }
    509 
    510             subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
    511             if (subfd >= 0) {
    512                 size += calculate_dir_size(subfd);
    513             }
    514         }
    515     }
    516     closedir(d);
    517     return size;
    518 }
    519 
    520 uint64_t GetTreeBytes(const std::string& path) {
    521     int dirfd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC);
    522     if (dirfd < 0) {
    523         PLOG(WARNING) << "Failed to open " << path;
    524         return -1;
    525     } else {
    526         uint64_t res = calculate_dir_size(dirfd);
    527         close(dirfd);
    528         return res;
    529     }
    530 }
    531 
    532 bool IsFilesystemSupported(const std::string& fsType) {
    533     std::string supported;
    534     if (!ReadFileToString(kProcFilesystems, &supported)) {
    535         PLOG(ERROR) << "Failed to read supported filesystems";
    536         return false;
    537     }
    538     return supported.find(fsType + "\n") != std::string::npos;
    539 }
    540 
    541 status_t WipeBlockDevice(const std::string& path) {
    542     status_t res = -1;
    543     const char* c_path = path.c_str();
    544     unsigned long nr_sec = 0;
    545     unsigned long long range[2];
    546 
    547     int fd = TEMP_FAILURE_RETRY(open(c_path, O_RDWR | O_CLOEXEC));
    548     if (fd == -1) {
    549         PLOG(ERROR) << "Failed to open " << path;
    550         goto done;
    551     }
    552 
    553     if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
    554         PLOG(ERROR) << "Failed to determine size of " << path;
    555         goto done;
    556     }
    557 
    558     range[0] = 0;
    559     range[1] = (unsigned long long) nr_sec * 512;
    560 
    561     LOG(INFO) << "About to discard " << range[1] << " on " << path;
    562     if (ioctl(fd, BLKDISCARD, &range) == 0) {
    563         LOG(INFO) << "Discard success on " << path;
    564         res = 0;
    565     } else {
    566         PLOG(ERROR) << "Discard failure on " << path;
    567     }
    568 
    569 done:
    570     close(fd);
    571     return res;
    572 }
    573 
    574 static bool isValidFilename(const std::string& name) {
    575     if (name.empty() || (name == ".") || (name == "..")
    576             || (name.find('/') != std::string::npos)) {
    577         return false;
    578     } else {
    579         return true;
    580     }
    581 }
    582 
    583 std::string BuildKeyPath(const std::string& partGuid) {
    584     return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
    585 }
    586 
    587 std::string BuildDataSystemLegacyPath(userid_t userId) {
    588     return StringPrintf("%s/system/users/%u", BuildDataPath(nullptr).c_str(), userId);
    589 }
    590 
    591 std::string BuildDataSystemCePath(userid_t userId) {
    592     return StringPrintf("%s/system_ce/%u", BuildDataPath(nullptr).c_str(), userId);
    593 }
    594 
    595 std::string BuildDataSystemDePath(userid_t userId) {
    596     return StringPrintf("%s/system_de/%u", BuildDataPath(nullptr).c_str(), userId);
    597 }
    598 
    599 std::string BuildDataMiscLegacyPath(userid_t userId) {
    600     return StringPrintf("%s/misc/user/%u", BuildDataPath(nullptr).c_str(), userId);
    601 }
    602 
    603 std::string BuildDataMiscCePath(userid_t userId) {
    604     return StringPrintf("%s/misc_ce/%u", BuildDataPath(nullptr).c_str(), userId);
    605 }
    606 
    607 std::string BuildDataMiscDePath(userid_t userId) {
    608     return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId);
    609 }
    610 
    611 // Keep in sync with installd (frameworks/native/cmds/installd/utils.h)
    612 std::string BuildDataProfilesDePath(userid_t userId) {
    613     return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId);
    614 }
    615 
    616 std::string BuildDataPath(const char* volumeUuid) {
    617     // TODO: unify with installd path generation logic
    618     if (volumeUuid == nullptr) {
    619         return "/data";
    620     } else {
    621         CHECK(isValidFilename(volumeUuid));
    622         return StringPrintf("/mnt/expand/%s", volumeUuid);
    623     }
    624 }
    625 
    626 std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userId) {
    627     // TODO: unify with installd path generation logic
    628     std::string data(BuildDataPath(volumeUuid));
    629     return StringPrintf("%s/media/%u", data.c_str(), userId);
    630 }
    631 
    632 std::string BuildDataUserCePath(const char* volumeUuid, userid_t userId) {
    633     // TODO: unify with installd path generation logic
    634     std::string data(BuildDataPath(volumeUuid));
    635     if (volumeUuid == nullptr && userId == 0) {
    636         std::string legacy = StringPrintf("%s/data", data.c_str());
    637         struct stat sb;
    638         if (lstat(legacy.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
    639             /* /data/data is dir, return /data/data for legacy system */
    640             return legacy;
    641         }
    642     }
    643     return StringPrintf("%s/user/%u", data.c_str(), userId);
    644 }
    645 
    646 std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
    647     // TODO: unify with installd path generation logic
    648     std::string data(BuildDataPath(volumeUuid));
    649     return StringPrintf("%s/user_de/%u", data.c_str(), userId);
    650 }
    651 
    652 dev_t GetDevice(const std::string& path) {
    653     struct stat sb;
    654     if (stat(path.c_str(), &sb)) {
    655         PLOG(WARNING) << "Failed to stat " << path;
    656         return 0;
    657     } else {
    658         return sb.st_dev;
    659     }
    660 }
    661 
    662 status_t RestoreconRecursive(const std::string& path) {
    663     LOG(VERBOSE) << "Starting restorecon of " << path;
    664 
    665     static constexpr const char* kRestoreconString = "selinux.restorecon_recursive";
    666 
    667     android::base::SetProperty(kRestoreconString, "");
    668     android::base::SetProperty(kRestoreconString, path);
    669 
    670     android::base::WaitForProperty(kRestoreconString, path);
    671 
    672     LOG(VERBOSE) << "Finished restorecon of " << path;
    673     return OK;
    674 }
    675 
    676 status_t SaneReadLinkAt(int dirfd, const char* path, char* buf, size_t bufsiz) {
    677     ssize_t len = readlinkat(dirfd, path, buf, bufsiz);
    678     if (len < 0) {
    679         return -1;
    680     } else if (len == (ssize_t) bufsiz) {
    681         return -1;
    682     } else {
    683         buf[len] = '\0';
    684         return 0;
    685     }
    686 }
    687 
    688 bool IsRunningInEmulator() {
    689     return android::base::GetBoolProperty("ro.kernel.qemu", false);
    690 }
    691 
    692 }  // namespace vold
    693 }  // namespace android
    694