diff --git a/init/Android.bp b/init/Android.bp index 9aeb8371f..8a0bb55a4 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -110,6 +110,7 @@ cc_library_static { "init.cpp", "keychords.cpp", "modalias_handler.cpp", + "mount_handler.cpp", "mount_namespace.cpp", "parser.cpp", "persistent_properties.cpp", diff --git a/init/README.md b/init/README.md index f0e5d55ce..b2039b4e2 100644 --- a/init/README.md +++ b/init/README.md @@ -660,12 +660,19 @@ The below pseudocode may explain this more clearly: Properties ---------- -Init provides information about the services that it is responsible -for via the below properties. +Init provides state information with the following properties. `init.svc.` > State of a named service ("stopped", "stopping", "running", "restarting") +`dev.mnt.blk.` +> Block device base name associated with a *mount_point*. + The *mount_point* has / replaced by . and if referencing the root mount point + "/", it will use "/root", specifically `dev.mnt.blk.root`. + Meant for references to `/sys/device/block/${dev.mnt.blk.}/` and + `/sys/fs/ext4/${dev.mnt.blk.}/` to tune the block device + characteristics in a device agnostic manner. + Boot timing ----------- diff --git a/init/init.cpp b/init/init.cpp index 7182fda25..a5f45497b 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -57,6 +57,7 @@ #include "first_stage_mount.h" #include "import_parser.h" #include "keychords.h" +#include "mount_handler.h" #include "mount_namespace.h" #include "property_service.h" #include "reboot.h" @@ -686,6 +687,7 @@ int SecondStageMain(int argc, char** argv) { fs_mgr_vendor_overlay_mount_all(); export_oem_lock_status(); StartPropertyService(&epoll); + MountHandler mount_handler(&epoll); set_usb_controller(); const BuiltinFunctionMap function_map; diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp new file mode 100644 index 000000000..12dfc6ddc --- /dev/null +++ b/init/mount_handler.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 The Android Open Source 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 "mount_handler.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "epoll.h" +#include "property_service.h" + +namespace android { +namespace init { + +namespace { + +MountHandlerEntry ParseMount(const std::string& line) { + auto fields = android::base::Split(line, " "); + while (fields.size() < 3) fields.emplace_back(""); + if (fields[0] == "/dev/root") { + if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) { + if (auto entry = GetEntryForMountPoint(&fstab, "/")) { + fields[0] = entry->blk_device; + } + } + } + if (android::base::StartsWith(fields[0], "/dev/")) { + if (std::string link; android::base::Readlink(fields[0], &link)) { + fields[0] = link; + } + } + return MountHandlerEntry(fields[0], fields[1], fields[2]); +} + +void SetMountProperty(const MountHandlerEntry& entry, bool add) { + static constexpr char devblock[] = "/dev/block/"; + if (!android::base::StartsWith(entry.blk_device, devblock)) return; + std::string value; + if (add) { + value = entry.blk_device.substr(strlen(devblock)); + if (android::base::StartsWith(value, "sd")) { + // All sd partitions inherit their queue characteristics + // from the whole device reference. Strip partition number. + auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); }); + if (it != value.end()) value.erase(it, value.end()); + } + auto queue = "/sys/block/" + value + "/queue"; + struct stat sb; + if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = ""; + if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = ""; + // Skip the noise associated with APEX until there is a need + if (android::base::StartsWith(value, "loop")) value = ""; + } + std::string property = + "dev.mnt.blk" + ((entry.mount_point == "/") ? "/root" : entry.mount_point); + std::replace(property.begin(), property.end(), '/', '.'); + if (value.empty() && android::base::GetProperty(property, "").empty()) return; + property_set(property, value); +} + +} // namespace + +MountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point, + const std::string& fs_type) + : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {} + +bool MountHandlerEntry::operator<(const MountHandlerEntry& r) const { + if (blk_device < r.blk_device) return true; + if (blk_device > r.blk_device) return false; + if (mount_point < r.mount_point) return true; + if (mount_point > r.mount_point) return false; + return fs_type < r.fs_type; +} + +MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) { + if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts"; + auto result = epoll->RegisterHandler( + fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI); + if (!result) LOG(FATAL) << result.error(); +} + +MountHandler::~MountHandler() { + if (fp_) epoll_->UnregisterHandler(fileno(fp_.get())).IgnoreError(); +} + +void MountHandler::MountHandlerFunction() { + rewind(fp_.get()); + char* buf = nullptr; + size_t len = 0; + auto untouched = mounts_; + while (getline(&buf, &len, fp_.get()) != -1) { + auto entry = ParseMount(std::string(buf, len)); + auto match = untouched.find(entry); + if (match == untouched.end()) { + SetMountProperty(entry, true); + mounts_.emplace(std::move(entry)); + } else { + untouched.erase(match); + } + } + free(buf); + for (auto entry : untouched) { + auto match = mounts_.find(entry); + if (match == mounts_.end()) continue; + mounts_.erase(match); + SetMountProperty(entry, false); + } +} + +} // namespace init +} // namespace android diff --git a/init/mount_handler.h b/init/mount_handler.h new file mode 100644 index 000000000..e524a74ae --- /dev/null +++ b/init/mount_handler.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source 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. + */ + +#pragma once + +#include + +#include +#include +#include + +#include "epoll.h" + +namespace android { +namespace init { + +struct MountHandlerEntry { + MountHandlerEntry(const std::string& blk_device, const std::string& mount_point, + const std::string& fs_type); + + bool operator<(const MountHandlerEntry& r) const; + + const std::string blk_device; + const std::string mount_point; + const std::string fs_type; +}; + +class MountHandler { + public: + explicit MountHandler(Epoll* epoll); + MountHandler(const MountHandler&) = delete; + MountHandler(MountHandler&&) = delete; + MountHandler& operator=(const MountHandler&) = delete; + MountHandler& operator=(MountHandler&&) = delete; + ~MountHandler(); + + private: + void MountHandlerFunction(); + + Epoll* epoll_; + std::unique_ptr fp_; + std::set mounts_; +}; + +} // namespace init +} // namespace android