/* * Copyright (C) 2008 The Android Open Source Project * Copyright (C) 2019 The LineageOS Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #define LOG_TAG "VolumeManager" #include #include #include #include #include #include "Disk.h" #include "DiskPartition.h" #include "EmulatedVolume.h" #include "VolumeBase.h" #include "NetlinkManager.h" #include "sehandle.h" struct selabel_handle* sehandle; using android::fs_mgr::Fstab; using android::fs_mgr::FstabEntry; static const unsigned int kMajorBlockMmc = 179; static const unsigned int kMajorBlockExperimentalMin = 240; static const unsigned int kMajorBlockExperimentalMax = 254; namespace android { namespace volmgr { static void do_coldboot(DIR* d, int lvl) { struct dirent* de; int dfd, fd; dfd = dirfd(d); fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC); if (fd >= 0) { write(fd, "add\n", 4); close(fd); } while ((de = readdir(d))) { DIR* d2; if (de->d_name[0] == '.') continue; if (de->d_type != DT_DIR && lvl > 0) continue; fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC); if (fd < 0) continue; d2 = fdopendir(fd); if (d2 == 0) close(fd); else { do_coldboot(d2, lvl + 1); closedir(d2); } } } static void coldboot(const char* path) { DIR* d = opendir(path); if (d) { do_coldboot(d, 0); closedir(d); } } static int process_config(VolumeManager* vm, FstabEntry* data_recp) { Fstab fstab; if (!ReadDefaultFstab(&fstab)) { PLOG(ERROR) << "Failed to open default fstab"; return -1; } /* Loop through entries looking for ones that vold manages */ for (int i = 0; i < fstab.size(); i++) { if (fstab[i].fs_mgr_flags.vold_managed) { std::string sysPattern(fstab[i].blk_device); std::string fstype = fstab[i].fs_type; std::string mntopts = fstab[i].fs_options; std::string nickname = fstab[i].label; int partnum = fstab[i].partnum; int flags = 0; if (fstab[i].is_encryptable()) { flags |= android::volmgr::Disk::Flags::kAdoptable; } if (fstab[i].fs_mgr_flags.no_emulated_sd || property_get_bool("vold.debug.default_primary", false)) { flags |= android::volmgr::Disk::Flags::kDefaultPrimary; } if (fstab[i].fs_mgr_flags.nonremovable) { flags |= android::volmgr::Disk::Flags::kNonRemovable; } vm->addDiskSource(new VolumeManager::DiskSource(sysPattern, nickname, partnum, flags, fstype, mntopts)); } else { if (data_recp->fs_type.empty() && fstab[i].mount_point == "/data") { char* detected_fs_type = blkid_get_tag_value(nullptr, "TYPE", fstab[i].blk_device.c_str()); if (!detected_fs_type || fstab[i].fs_type == detected_fs_type) { *data_recp = fstab[i]; } } } } return 0; } VolumeInfo::VolumeInfo(const VolumeBase* vol) : mId(vol->getId()), mLabel(vol->getPartLabel()), mPath(vol->getPath()), mMountable(vol->isMountable()) { // Empty } VolumeManager* VolumeManager::sInstance = nullptr; VolumeManager* VolumeManager::Instance(void) { if (!sInstance) { sInstance = new VolumeManager(); } return sInstance; } VolumeManager::VolumeManager(void) : mWatcher(nullptr), mNetlinkManager(NetlinkManager::Instance()), mInternalEmulated(nullptr) { // Empty } VolumeManager::~VolumeManager(void) { stop(); } bool VolumeManager::start(VolumeWatcher* watcher) { setenv("BLKID_FILE", "/tmp/vold_blkid.tab", 1); sehandle = selinux_android_file_context_handle(); if (sehandle) { selinux_android_set_sehandle(sehandle); } mkdir("/dev/block/volmgr", 0755); mWatcher = watcher; FstabEntry data_rec; if (process_config(this, &data_rec) != 0) { LOG(ERROR) << "Error reading configuration... continuing anyway"; } if (!data_rec.fs_type.empty()) { mInternalEmulated = new EmulatedVolume(&data_rec, "media/0"); mInternalEmulated->create(); } if (!mNetlinkManager->start()) { LOG(ERROR) << "Unable to start NetlinkManager"; return false; } coldboot("/sys/block"); unmountAll(); LOG(INFO) << "VolumeManager initialized"; return true; } void VolumeManager::stop(void) { for (auto& disk : mDisks) { disk->destroy(); delete disk; } mDisks.clear(); for (auto& source : mDiskSources) { delete source; } mDiskSources.clear(); mNetlinkManager->stop(); mWatcher = nullptr; } bool VolumeManager::reset(void) { return false; } bool VolumeManager::unmountAll(void) { std::lock_guard lock(mLock); if (mInternalEmulated) { mInternalEmulated->unmount(); } for (auto& disk : mDisks) { disk->unmountAll(); } return true; } void VolumeManager::getVolumeInfo(std::vector& info) { std::lock_guard lock(mLock); info.clear(); if (mInternalEmulated) { info.push_back(VolumeInfo(mInternalEmulated)); } for (const auto& disk : mDisks) { disk->getVolumeInfo(info); } } VolumeBase* VolumeManager::findVolume(const std::string& id) { if (mInternalEmulated && mInternalEmulated->getId() == id) { return mInternalEmulated; } for (const auto& disk : mDisks) { auto vol = disk->findVolume(id); if (vol != nullptr) { return vol.get(); } } return nullptr; } bool VolumeManager::volumeMount(const std::string& id) { std::lock_guard lock(mLock); auto vol = findVolume(id); if (!vol) { return false; } status_t res = vol->mount(); return (res == OK); } bool VolumeManager::volumeUnmount(const std::string& id, bool detach /* = false */) { std::lock_guard lock(mLock); auto vol = findVolume(id); if (!vol) { return false; } status_t res = vol->unmount(detach); return (res == OK); } void VolumeManager::addDiskSource(DiskSource* source) { std::lock_guard lock(mLock); mDiskSources.push_back(source); } void VolumeManager::handleBlockEvent(NetlinkEvent* evt) { std::lock_guard lock(mLock); const char* param; param = evt->findParam("DEVTYPE"); std::string devType(param ? param : ""); if (devType != "disk") { return; } param = evt->findParam("DEVPATH"); std::string eventPath(param ? param : ""); int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); dev_t device = makedev(major, minor); switch (evt->getAction()) { case NetlinkEvent::Action::kAdd: { for (const auto& source : mDiskSources) { if (source->matches(eventPath)) { // For now, assume that MMC, virtio-blk (the latter is // emulator-specific; see Disk.cpp for details) and UFS card // devices are SD, and that everything else is USB int flags = source->getFlags(); if (major == kMajorBlockMmc || (eventPath.find("ufs") != std::string::npos) || (IsRunningInEmulator() && major >= (int)kMajorBlockExperimentalMin && major <= (int)kMajorBlockExperimentalMax)) { flags |= Disk::Flags::kSd; } else { flags |= Disk::Flags::kUsb; } Disk* disk = (source->getPartNum() == -1) ? new Disk(eventPath, device, source->getNickname(), flags) : new DiskPartition(eventPath, device, source->getNickname(), flags, source->getPartNum(), source->getFsType(), source->getMntOpts()); disk->create(); mDisks.push_back(disk); break; } } break; } case NetlinkEvent::Action::kChange: { LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed"; for (const auto& disk : mDisks) { if (disk->getDevice() == device) { disk->readMetadata(); disk->readPartitions(); } } break; } case NetlinkEvent::Action::kRemove: { auto i = mDisks.begin(); while (i != mDisks.end()) { if ((*i)->getDevice() == device) { (*i)->destroy(); i = mDisks.erase(i); } else { ++i; } } break; } default: { LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction(); break; } } } void VolumeManager::notifyEvent(int code) { std::vector argv; notifyEvent(code, argv); } void VolumeManager::notifyEvent(int code, const std::string& arg) { std::vector argv; argv.push_back(arg); notifyEvent(code, argv); } void VolumeManager::notifyEvent(int code, const std::vector& argv) { mWatcher->handleEvent(code, argv); } } // namespace volmgr } // namespace android