19c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey/*
29c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * Copyright (C) 2015 The Android Open Source Project
39c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey *
49c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
59c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * you may not use this file except in compliance with the License.
69c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * You may obtain a copy of the License at
79c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey *
89c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
99c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey *
109c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
119c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
129c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * See the License for the specific language governing permissions and
149c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey * limitations under the License.
159c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey */
169c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
17d0640f6358041f7e2657167560b357078db73526Jeff Sharkey#include "fs/Ext4.h"
18d0640f6358041f7e2657167560b357078db73526Jeff Sharkey#include "fs/F2fs.h"
199c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include "PrivateVolume.h"
203161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey#include "EmulatedVolume.h"
219c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include "Utils.h"
229c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include "VolumeManager.h"
239c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include "ResponseCode.h"
249c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include "cryptfs.h"
259c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
269c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <base/stringprintf.h>
279c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <base/logging.h>
289c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <cutils/fs.h>
299c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <private/android_filesystem_config.h>
309c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
319c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <fcntl.h>
329c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <stdlib.h>
339c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <sys/mount.h>
349c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <sys/stat.h>
359c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <sys/types.h>
369c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <sys/wait.h>
379c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey#include <sys/param.h>
389c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
399c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeyusing android::base::StringPrintf;
409c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
419c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeynamespace android {
429c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeynamespace vold {
439c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
44d0640f6358041f7e2657167560b357078db73526Jeff Sharkeystatic const unsigned int kMajorBlockMmc = 179;
45d0640f6358041f7e2657167560b357078db73526Jeff Sharkey
469c48498f4529f623650c56d03e63324c8d813032Jeff SharkeyPrivateVolume::PrivateVolume(dev_t device, const std::string& keyRaw) :
479c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        VolumeBase(Type::kPrivate), mRawDevice(device), mKeyRaw(keyRaw) {
489c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    setId(StringPrintf("private:%u,%u", major(device), minor(device)));
499c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    mRawDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
509c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
519c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
529c48498f4529f623650c56d03e63324c8d813032Jeff SharkeyPrivateVolume::~PrivateVolume() {
539c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
549c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
559c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeystatus_t PrivateVolume::readMetadata() {
569c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    status_t res = ReadMetadata(mDmDevPath, mFsType, mFsUuid, mFsLabel);
57ce6a913aeac7db94a41362c63bab74092767bb3eJeff Sharkey    notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
58ce6a913aeac7db94a41362c63bab74092767bb3eJeff Sharkey    notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
59ce6a913aeac7db94a41362c63bab74092767bb3eJeff Sharkey    notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
609c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    return res;
619c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
629c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
639c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeystatus_t PrivateVolume::doCreate() {
649c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    if (CreateDeviceNode(mRawDevPath, mRawDevice)) {
659c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        return -EIO;
669c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
679c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
689c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    // Recover from stale vold by tearing down any old mappings
699c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    cryptfs_revert_ext_volume(getId().c_str());
709c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
719c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    // TODO: figure out better SELinux labels for private volumes
729c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
739c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    unsigned char* key = (unsigned char*) mKeyRaw.data();
749c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    char crypto_blkdev[MAXPATHLEN];
759c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    int res = cryptfs_setup_ext_volume(getId().c_str(), mRawDevPath.c_str(),
769c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey            key, mKeyRaw.size(), crypto_blkdev);
779c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    mDmDevPath = crypto_blkdev;
789c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    if (res != 0) {
799c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        PLOG(ERROR) << getId() << " failed to setup cryptfs";
809c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        return -EIO;
819c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
829c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
839c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    return OK;
849c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
859c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
869c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeystatus_t PrivateVolume::doDestroy() {
879c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    if (cryptfs_revert_ext_volume(getId().c_str())) {
889c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        LOG(ERROR) << getId() << " failed to revert cryptfs";
899c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
909c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    return DestroyDeviceNode(mRawDevPath);
919c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
929c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
939c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeystatus_t PrivateVolume::doMount() {
949c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    if (readMetadata()) {
959c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        LOG(ERROR) << getId() << " failed to read metadata";
969c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        return -EIO;
979c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
989c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
99f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey    mPath = StringPrintf("/mnt/expand/%s", mFsUuid.c_str());
1009c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    setPath(mPath);
1019c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
102f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey    if (PrepareDir(mPath, 0700, AID_ROOT, AID_ROOT)) {
1039c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        PLOG(ERROR) << getId() << " failed to create mount point " << mPath;
104f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey        return -EIO;
105f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey    }
106f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey
107d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    if (mFsType == "ext4") {
108d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        int res = ext4::Check(mDmDevPath, mPath);
109d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if (res == 0 || res == 1) {
110d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            LOG(DEBUG) << getId() << " passed filesystem check";
111d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        } else {
112d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            PLOG(ERROR) << getId() << " failed filesystem check";
113d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            return -EIO;
114d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
115d0640f6358041f7e2657167560b357078db73526Jeff Sharkey
116d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if (ext4::Mount(mDmDevPath, mPath, false, false, true)) {
117d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            PLOG(ERROR) << getId() << " failed to mount";
118d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            return -EIO;
119d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
120d0640f6358041f7e2657167560b357078db73526Jeff Sharkey
121d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    } else if (mFsType == "f2fs") {
122d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        int res = f2fs::Check(mDmDevPath);
123d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if (res == 0) {
124d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            LOG(DEBUG) << getId() << " passed filesystem check";
125d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        } else {
126d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            PLOG(ERROR) << getId() << " failed filesystem check";
127d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            return -EIO;
128d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
129d0640f6358041f7e2657167560b357078db73526Jeff Sharkey
130d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if (f2fs::Mount(mDmDevPath, mPath)) {
131d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            PLOG(ERROR) << getId() << " failed to mount";
132d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            return -EIO;
133d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
1349c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
135d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    } else {
136d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
1379c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        return -EIO;
1389c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
1399c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
14034824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    LOG(VERBOSE) << "Starting restorecon of " << mPath;
14134824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey
14234824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    // TODO: find a cleaner way of waiting for restorecon to finish
14334824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    property_set("selinux.restorecon_recursive", "");
14434824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    property_set("selinux.restorecon_recursive", mPath.c_str());
14534824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey
14634824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    char value[PROPERTY_VALUE_MAX];
14734824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    while (true) {
14834824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey        property_get("selinux.restorecon_recursive", value, "");
14934824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey        if (strcmp(mPath.c_str(), value) == 0) {
15034824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey            break;
15134824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey        }
15234824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey        sleep(1);
15334824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey        LOG(VERBOSE) << "Waiting for restorecon...";
15434824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    }
15534824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey
15634824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey    LOG(VERBOSE) << "Finished restorecon of " << mPath;
15734824129de2c4a8bb0d1cb9011beff2c186a87d0Jeff Sharkey
158f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey    // Verify that common directories are ready to roll
159f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey    if (PrepareDir(mPath + "/app", 0771, AID_SYSTEM, AID_SYSTEM) ||
160f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey            PrepareDir(mPath + "/user", 0711, AID_SYSTEM, AID_SYSTEM) ||
161f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey            PrepareDir(mPath + "/media", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||
16281f55c6dc1a14ed68e404fa3a2c244dd343e4990Jeff Sharkey            PrepareDir(mPath + "/media/0", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||
163f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey            PrepareDir(mPath + "/local", 0751, AID_ROOT, AID_ROOT) ||
164f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey            PrepareDir(mPath + "/local/tmp", 0771, AID_SHELL, AID_SHELL)) {
165f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey        PLOG(ERROR) << getId() << " failed to prepare";
166f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey        return -EIO;
167f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey    }
168f0121c574ede0898e36eeba0a0e0affd0f8d81a7Jeff Sharkey
1693161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey    // Create a new emulated volume stacked above us, it will automatically
1703161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey    // be destroyed during unmount
1713161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey    std::string mediaPath(mPath + "/media");
1723161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey    auto vol = std::shared_ptr<VolumeBase>(
1733161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey            new EmulatedVolume(mediaPath, mRawDevice, mFsUuid));
1743161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey    addVolume(vol);
1753161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey    vol->create();
1763161fb3702830b586b2e36fa9ca4519f59f951b4Jeff Sharkey
1779c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    return OK;
1789c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
1799c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
1809c48498f4529f623650c56d03e63324c8d813032Jeff Sharkeystatus_t PrivateVolume::doUnmount() {
1819c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    ForceUnmount(mPath);
1829c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
1839c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    if (TEMP_FAILURE_RETRY(rmdir(mPath.c_str()))) {
1849c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey        PLOG(ERROR) << getId() << " failed to rmdir mount point " << mPath;
1859c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
1869c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
1879c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    return OK;
1889c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
1899c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
190d0640f6358041f7e2657167560b357078db73526Jeff Sharkeystatus_t PrivateVolume::doFormat(const std::string& fsType) {
191d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    std::string resolvedFsType = fsType;
192d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    if (fsType == "auto") {
193d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        // For now, assume that all MMC devices are flash-based SD cards, and
194d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        // give everyone else ext4 because sysfs rotational isn't reliable.
195d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if ((major(mRawDevice) == kMajorBlockMmc) && f2fs::IsSupported()) {
196d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            resolvedFsType = "f2fs";
197d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        } else {
198d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            resolvedFsType = "ext4";
199d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
200d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        LOG(DEBUG) << "Resolved auto to " << resolvedFsType;
201d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    }
202d0640f6358041f7e2657167560b357078db73526Jeff Sharkey
203d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    if (resolvedFsType == "ext4") {
204d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        // TODO: change reported mountpoint once we have better selinux support
205d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if (ext4::Format(mDmDevPath, 0, "/data")) {
206d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            PLOG(ERROR) << getId() << " failed to format";
207d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            return -EIO;
208d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
209d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    } else if (resolvedFsType == "f2fs") {
210d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        if (f2fs::Format(mDmDevPath)) {
211d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            PLOG(ERROR) << getId() << " failed to format";
212d0640f6358041f7e2657167560b357078db73526Jeff Sharkey            return -EIO;
213d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        }
214d0640f6358041f7e2657167560b357078db73526Jeff Sharkey    } else {
215d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        LOG(ERROR) << getId() << " unsupported filesystem " << fsType;
216d0640f6358041f7e2657167560b357078db73526Jeff Sharkey        return -EINVAL;
2179c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    }
2189c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
2199c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey    return OK;
2209c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}
2219c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey
2229c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}  // namespace vold
2239c48498f4529f623650c56d03e63324c8d813032Jeff Sharkey}  // namespace android
224