Merge "Include sys/select.h for fd_set"
diff --git a/fastboot/README.md b/fastboot/README.md
index c224448..d3b6c1a 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -27,16 +27,16 @@
1. Host sends a command, which is an ascii string in a single
packet no greater than 64 bytes.
-2. Client response with a single packet no greater than 64 bytes.
+2. Client response with a single packet no greater than 256 bytes.
The first four bytes of the response are "OKAY", "FAIL", "DATA",
or "INFO". Additional bytes may contain an (ascii) informative
message.
- a. INFO -> the remaining 60 bytes are an informative message
+ a. INFO -> the remaining 252 bytes are an informative message
(providing progress or diagnostic messages). They should
be displayed and then step #2 repeats
- b. FAIL -> the requested command failed. The remaining 60 bytes
+ b. FAIL -> the requested command failed. The remaining 252 bytes
of the response (if present) provide a textual failure message
to present to the user. Stop.
@@ -53,13 +53,13 @@
until the client has sent or received the number of bytes indicated
in the "DATA" response above.
-4. Client responds with a single packet no greater than 64 bytes.
+4. Client responds with a single packet no greater than 256 bytes.
The first four bytes of the response are "OKAY", "FAIL", or "INFO".
Similar to #2:
- a. INFO -> display the remaining 60 bytes and return to #4
+ a. INFO -> display the remaining 252 bytes and return to #4
- b. FAIL -> display the remaining 60 bytes (if present) as a failure
+ b. FAIL -> display the remaining 252 bytes (if present) as a failure
reason and consider the command failed. Stop.
c. OKAY -> success. Go to #5
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index d4fc2b9..6170aee 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -65,6 +65,7 @@
"-D_FILE_OFFSET_BITS=64",
],
srcs: [
+ "blockdev.cpp",
"file_wait.cpp",
"fs_mgr.cpp",
"fs_mgr_format.cpp",
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
new file mode 100644
index 0000000..14b217c
--- /dev/null
+++ b/fs_mgr/blockdev.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <dirent.h>
+#include <libdm/dm.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include "blockdev.h"
+
+using android::base::Basename;
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+
+// Return the parent device of a partition. Converts e.g. "sda26" into "sda".
+static std::string PartitionParent(const std::string& blockdev) {
+ if (blockdev.find('/') != std::string::npos) {
+ LOG(ERROR) << __func__ << ": invalid argument " << blockdev;
+ return blockdev;
+ }
+ auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir};
+ if (!dir) {
+ return blockdev;
+ }
+ for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+ std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str());
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) >= 0) {
+ return ent->d_name;
+ }
+ }
+ return blockdev;
+}
+
+// Convert a major:minor pair into a block device name.
+static std::string BlockdevName(dev_t dev) {
+ auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
+ if (!dir) {
+ return {};
+ }
+ for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+ const std::string path = std::string("/dev/block/") + ent->d_name;
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {
+ return ent->d_name;
+ }
+ }
+ return {};
+}
+
+// Trim whitespace from the end of a string.
+static void rtrim(std::string& s) {
+ s.erase(s.find_last_not_of('\n') + 1, s.length());
+}
+
+// For file `file_path`, retrieve the block device backing the filesystem on
+// which the file exists and return the queue depth of the block device.
+static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
+ struct stat statbuf;
+ int res = stat(file_path.c_str(), &statbuf);
+ if (res < 0) {
+ return ErrnoError() << "stat(" << file_path << ")";
+ }
+ std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
+ LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
+ if (blockdev.empty()) {
+ const std::string err_msg =
+ StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
+ minor(statbuf.st_dev), file_path.c_str());
+ return ResultError(err_msg, 0);
+ }
+ auto& dm = DeviceMapper::Instance();
+ for (;;) {
+ std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
+ if (!child) {
+ break;
+ }
+ LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child;
+ blockdev = *child;
+ }
+ std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
+ if (!maybe_blockdev) {
+ return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
+ }
+ blockdev = PartitionParent(*maybe_blockdev);
+ LOG(DEBUG) << __func__ << ": "
+ << "Partition parent: " << blockdev;
+ const std::string nr_tags_path =
+ StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
+ std::string nr_tags;
+ if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
+ return ResultError("Failed to read " + nr_tags_path, 0);
+ }
+ rtrim(nr_tags);
+ LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
+ << " and that block device supports queue depth " << nr_tags;
+ return strtol(nr_tags.c_str(), NULL, 0);
+}
+
+// Set 'nr_requests' of `loop_device_path` to the queue depth of the block
+// device backing `file_path`.
+Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+ const std::string& file_path) {
+ if (!StartsWith(loop_device_path, "/dev/")) {
+ return Error() << "Invalid argument " << loop_device_path;
+ }
+
+ const std::string loop_device_name = Basename(loop_device_path);
+
+ const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
+ if (!qd.ok()) {
+ LOG(DEBUG) << __func__ << ": "
+ << "BlockDeviceQueueDepth() returned " << qd.error();
+ return ResultError(qd.error());
+ }
+ const std::string nr_requests = StringPrintf("%u", *qd);
+ const std::string sysfs_path =
+ StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str());
+ unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
+ if (sysfs_fd == -1) {
+ return ErrnoError() << "Failed to open " << sysfs_path;
+ }
+
+ const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());
+ if (res < 0) {
+ return ErrnoError() << "Failed to write to " << sysfs_path;
+ }
+ return {};
+}
diff --git a/fs_mgr/blockdev.h b/fs_mgr/blockdev.h
new file mode 100644
index 0000000..2c0d68a
--- /dev/null
+++ b/fs_mgr/blockdev.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 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 <android-base/result.h>
+#include <string>
+
+android::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+ const std::string& file_path);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ec66144..5f15aad 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -72,6 +72,7 @@
#include <log/log_properties.h>
#include <logwrap/logwrap.h>
+#include "blockdev.h"
#include "fs_mgr_priv.h"
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
@@ -2110,6 +2111,8 @@
ConfigureIoScheduler(loop_device);
+ ConfigureQueueDepth(loop_device, "/");
+
// set block size & direct IO
unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
if (loop_fd.get() == -1) {
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index fdaffbe..d275320 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "android-base/file.h"
#include "fs_mgr/roots.h"
#include <sys/mount.h>
@@ -39,18 +40,26 @@
while (true) {
auto entry = GetEntryForMountPoint(fstab, str);
if (entry != nullptr) return entry;
- if (str == "/") break;
- auto slash = str.find_last_of('/');
- if (slash == std::string::npos) break;
- if (slash == 0) {
- str = "/";
- } else {
- str = str.substr(0, slash);
- }
+ str = android::base::Dirname(str);
+ if (!str.compare(".") || !str.compare("/")) break;
}
return nullptr;
}
+std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path) {
+ std::vector<FstabEntry*> entries;
+ if (path.empty()) return entries;
+
+ std::string str(path);
+ while (true) {
+ entries = GetEntriesForMountPoint(fstab, str);
+ if (!entries.empty()) return entries;
+ str = android::base::Dirname(str);
+ if (!str.compare(".") || !str.compare("/")) break;
+ }
+ return entries;
+}
+
enum class MountState {
ERROR = -1,
NOT_MOUNTED = 0,
@@ -71,12 +80,7 @@
return MountState::NOT_MOUNTED;
}
-bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
- auto rec = GetEntryForPath(fstab, path);
- if (rec == nullptr) {
- LERROR << "unknown volume for path [" << path << "]";
- return false;
- }
+bool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {
if (rec->fs_type == "ramdisk") {
// The ramdisk is always mounted.
return true;
@@ -136,6 +140,21 @@
return true;
}
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point) {
+ auto entries = GetEntriesForPath(fstab, path);
+ if (entries.empty()) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+
+ for (auto entry : entries) {
+ if (TryPathMount(entry, mount_point)) return true;
+ }
+
+ LERROR << "Failed to mount for path [" << path << "]";
+ return false;
+}
+
bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
auto rec = GetEntryForPath(fstab, path);
if (rec == nullptr) {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 8e3d5a5..4034e30 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -578,34 +578,30 @@
return std::string{spec.target_type, sizeof(spec.target_type)};
}
-static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
static constexpr std::string_view kDevBlockPrefix("/dev/block/");
if (android::base::StartsWith(path, kDevBlockPrefix)) {
- *name = path.substr(kDevBlockPrefix.length());
- return true;
+ return path.substr(kDevBlockPrefix.length());
}
- return false;
+ return {};
}
bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
- std::string name;
- if (!ExtractBlockDeviceName(path, &name)) {
- return false;
- }
- return android::base::StartsWith(name, "dm-");
+ std::optional<std::string> name = ExtractBlockDeviceName(path);
+ return name && android::base::StartsWith(*name, "dm-");
}
std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
- std::string name;
- if (!ExtractBlockDeviceName(path, &name)) {
+ std::optional<std::string> name = ExtractBlockDeviceName(path);
+ if (!name) {
LOG(WARNING) << path << " is not a block device";
return std::nullopt;
}
- if (!android::base::StartsWith(name, "dm-")) {
+ if (!android::base::StartsWith(*name, "dm-")) {
LOG(WARNING) << path << " is not a dm device";
return std::nullopt;
}
- std::string dm_name_file = "/sys/block/" + name + "/dm/name";
+ std::string dm_name_file = "/sys/block/" + *name + "/dm/name";
std::string dm_name;
if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
PLOG(ERROR) << "Failed to read file " << dm_name_file;
@@ -616,16 +612,16 @@
}
std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
- std::string name;
- if (!ExtractBlockDeviceName(path, &name)) {
+ std::optional<std::string> name = ExtractBlockDeviceName(path);
+ if (!name) {
LOG(WARNING) << path << " is not a block device";
return std::nullopt;
}
- if (!android::base::StartsWith(name, "dm-")) {
+ if (!android::base::StartsWith(*name, "dm-")) {
// Reached bottom of the device mapper stack.
return std::nullopt;
}
- auto slaves_dir = "/sys/block/" + name + "/slaves";
+ auto slaves_dir = "/sys/block/" + *name + "/slaves";
auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
if (dir == nullptr) {
PLOG(ERROR) << "Failed to open: " << slaves_dir;
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 31c3003..7e01b85 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -51,6 +51,10 @@
static constexpr uint64_t kSectorSize = 512;
+// Returns `path` without /dev/block prefix if and only if `path` starts with
+// that prefix.
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
+
class DeviceMapper final {
public:
class DmBlockDevice final {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index 0457986..3655c01 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -36,6 +36,8 @@
MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
+ MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+ (override));
MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e788e4..c5f1ee4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -1035,6 +1035,20 @@
return;
}
}
+ } else if (reboot_target == "quiescent") {
+ bootloader_message boot = {};
+ if (std::string err; !read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << "Failed to read bootloader message: " << err;
+ }
+ // Update the boot command field if it's empty, and preserve
+ // the other arguments in the bootloader message.
+ if (!CommandIsPresent(&boot)) {
+ strlcpy(boot.command, "boot-quiescent", sizeof(boot.command));
+ if (std::string err; !write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return;
+ }
+ }
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"