Merge "Verify that the elf matches the expected arch."
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 8f9bf80..86d537d 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,6 +20,8 @@
#include <sys/types.h>
#include <string>
+#include "android-base/off64_t.h"
+
#if !defined(_WIN32) && !defined(O_BINARY)
/** Windows needs O_BINARY, but Unix never mangles line endings. */
#define O_BINARY 0
@@ -30,11 +32,6 @@
#define O_CLOEXEC O_NOINHERIT
#endif
-#if defined(__APPLE__)
-/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-typedef off_t off64_t;
-#endif
-
namespace android {
namespace base {
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 52d11ed..80513b1 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -17,6 +17,7 @@
#pragma once
#include "android-base/macros.h"
+#include "android-base/off64_t.h"
#include <sys/types.h>
diff --git a/base/include/android-base/off64_t.h b/base/include/android-base/off64_t.h
new file mode 100644
index 0000000..e6b71b8
--- /dev/null
+++ b/base/include/android-base/off64_t.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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
+
+#if defined(__APPLE__)
+/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 2347496..66b90bf 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -21,10 +21,15 @@
#include <algorithm>
#include <memory>
+#include <set>
+#include <string>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <sparse/sparse.h>
@@ -32,13 +37,35 @@
#include "fastboot_device.h"
#include "utility.h"
+using namespace android::fs_mgr;
+using namespace std::literals;
+
namespace {
constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;
-} // namespace
+void WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partition_name) {
+ // May be called, in the case of sparse data, multiple times so cache/skip.
+ static std::set<std::string> wiped;
+ if (wiped.find(partition_name) != wiped.end()) return;
+ wiped.insert(partition_name);
+ // Following appears to have a first time 2% impact on flashing speeds.
-using namespace android::fs_mgr;
+ // Convert partition_name to a validated mount point and wipe.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ for (auto i = 0; i < fstab->num_entries; i++) {
+ const auto mount_point = fstab->recs[i].mount_point;
+ if (!mount_point) continue;
+ auto partition = android::base::Basename(mount_point);
+ if ("/"s == mount_point) partition = "system";
+ if ((partition + device->GetCurrentSlot()) == partition_name) {
+ fs_mgr_overlayfs_teardown(mount_point);
+ }
+ }
+}
+
+} // namespace
int FlashRawDataChunk(int fd, const char* data, size_t len) {
size_t ret = 0;
@@ -101,15 +128,11 @@
} else if (data.size() > get_block_device_size(handle.fd())) {
return -EOVERFLOW;
}
+ WipeOverlayfsForPartition(device, partition_name);
return FlashBlockDevice(handle.fd(), data);
}
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) {
- std::optional<std::string> super = FindPhysicalPartition(partition_name);
- if (!super) {
- return device->WriteFail("Could not find partition: " + partition_name);
- }
-
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
std::vector<char> data = std::move(device->download_data());
if (data.empty()) {
return device->WriteFail("No data available");
@@ -125,47 +148,17 @@
// image.
std::string slot_suffix = device->GetCurrentSlot();
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
- std::unique_ptr<LpMetadata> metadata = ReadMetadata(super->c_str(), slot_number);
- if (!metadata || wipe) {
- if (!FlashPartitionTable(super.value(), *new_metadata.get())) {
+ if (wipe || !ReadMetadata(super_name, slot_number)) {
+ if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
return device->WriteOkay("Successfully flashed partition table");
}
- // There's a working super partition, and we don't want to wipe it - it may
- // may contain partitions created for the user. Instead, we create a zero-
- // sized partition for each entry in the new partition table. It is then
- // the host's responsibility to size it correctly via resize-logical-partition.
- std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*metadata.get());
- if (!builder) {
- return device->WriteFail("Unable to create a metadata builder");
- }
- for (const auto& partition : new_metadata->partitions) {
- std::string name = GetPartitionName(partition);
- if (builder->FindPartition(name)) {
- continue;
- }
- if (!builder->AddPartition(name, partition.attributes)) {
- return device->WriteFail("Unable to add partition: " + name);
- }
- }
-
- // The scratch partition may exist as temporary storage, created for
- // use by adb remount for overlayfs. If we're performing a flashall
- // operation then we want to start over with a clean slate, so we
- // remove the scratch partition until it is requested again.
- builder->RemovePartition("scratch");
-
- new_metadata = builder->Export();
- if (!new_metadata) {
- return device->WriteFail("Unable to export new partition table");
- }
-
// Write the new table to every metadata slot.
bool ok = true;
for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
- ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i);
+ ok &= UpdatePartitionTable(super_name, *new_metadata.get(), i);
}
if (!ok) {
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
index 89e20fc..b15f28b 100644
--- a/fastboot/device/flashing.h
+++ b/fastboot/device/flashing.h
@@ -22,4 +22,4 @@
class FastbootDevice;
int Flash(FastbootDevice* device, const std::string& partition_name);
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5101410..3c6b1b7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -852,7 +852,7 @@
buf->image_size = sz;
}
- lseek64(fd, 0, SEEK_SET);
+ lseek(fd, 0, SEEK_SET);
int64_t limit = get_sparse_limit(sz);
if (limit) {
sparse_file** s = load_sparse_files(fd, limit);
@@ -1092,12 +1092,20 @@
}
}
+static bool is_logical(const std::string& partition) {
+ std::string value;
+ return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
static void do_flash(const char* pname, const char* fname) {
struct fastboot_buffer buf;
if (!load_buf(fname, &buf)) {
die("cannot load '%s': %s", fname, strerror(errno));
}
+ if (is_logical(pname)) {
+ fb->ResizePartition(pname, std::to_string(buf.image_size));
+ }
flash_buf(pname, &buf);
}
@@ -1140,11 +1148,6 @@
return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
}
-static bool is_logical(const std::string& partition) {
- std::string value;
- return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
-}
-
static void reboot_to_userspace_fastboot() {
fb->RebootTo("fastboot");
@@ -1312,6 +1315,9 @@
if (!is_userspace_fastboot()) {
reboot_to_userspace_fastboot();
}
+ if (!is_userspace_fastboot()) {
+ die("Failed to boot into userspace; one or more components might be unbootable.");
+ }
fb->Download("super", fd, get_file_size(fd));
std::string command = "update-super:super";
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a6ef35b..ae2e2fe 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -50,7 +50,6 @@
#include <cutils/partition_utils.h>
#include <cutils/properties.h>
#include <ext4_utils/ext4.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <ext4_utils/ext4_sb.h>
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 804069a..4dacebf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -33,6 +33,7 @@
#include <sstream>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -50,8 +51,21 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
- const LpMetadataPartition& partition, DmTable* table) {
+bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
+ std::string* result) {
+ // Note: device-mapper will not accept symlinks, so we must use realpath
+ // here.
+ std::string name = GetBlockDevicePartitionName(block_device);
+ std::string path = "/dev/block/by-name/" + name;
+ if (!android::base::Realpath(path, result)) {
+ PERROR << "realpath: " << path;
+ return false;
+ }
+ return true;
+}
+
+static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
+ DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -60,10 +74,17 @@
case LP_TARGET_TYPE_ZERO:
target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
break;
- case LP_TARGET_TYPE_LINEAR:
- target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+ case LP_TARGET_TYPE_LINEAR: {
+ const auto& block_device = metadata.block_devices[extent.target_source];
+ std::string path;
+ if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
+ LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
+ return false;
+ }
+ target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
extent.target_data);
break;
+ }
default:
LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
return false;
@@ -79,13 +100,13 @@
return true;
}
-static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
- const LpMetadataPartition& partition, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
+static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
+ bool force_writable, const std::chrono::milliseconds& timeout_ms,
+ std::string* path) {
DeviceMapper& dm = DeviceMapper::Instance();
DmTable table;
- if (!CreateDmTable(block_device, metadata, partition, &table)) {
+ if (!CreateDmTable(metadata, partition, &table)) {
return false;
}
if (force_writable) {
@@ -122,7 +143,7 @@
continue;
}
std::string path;
- if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
+ if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@@ -140,8 +161,8 @@
}
for (const auto& partition : metadata->partitions) {
if (GetPartitionName(partition) == partition_name) {
- return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
- timeout_ms, path);
+ return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
+ path);
}
}
LERROR << "Could not find any partition with name: " << partition_name;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index ad488a9..79957f6 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -41,6 +41,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
@@ -56,11 +57,11 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
-bool fs_mgr_overlayfs_mount_all(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(fstab*) {
return false;
}
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
return {};
}
@@ -127,13 +128,63 @@
return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
}
-bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
// readonly filesystem, can not be mount -o remount,rw
// if squashfs or if free space is (near) zero making such a remount
// virtually useless, or if there are shared blocks that prevent remount,rw
- return ("squashfs"s == fsrec->fs_type) ||
- fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
- !fs_mgr_filesystem_has_space(fsrec->mount_point);
+ if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+ return true;
+ }
+ if (fs_mgr_is_logical(fsrec)) {
+ fs_mgr_update_logical_partition(fsrec);
+ }
+ return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
+}
+
+bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
+ auto save_errno = errno;
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+ if (!dir) {
+ if (errno == ENOENT) {
+ errno = save_errno;
+ return true;
+ }
+ PERROR << "opendir " << path << " depth=" << level;
+ if ((errno == EPERM) && (level != 0)) {
+ errno = save_errno;
+ return true;
+ }
+ return false;
+ }
+ dirent* entry;
+ auto ret = true;
+ while ((entry = readdir(dir.get()))) {
+ if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+ auto file = path + "/" + entry->d_name;
+ if (entry->d_type == DT_UNKNOWN) {
+ struct stat st;
+ save_errno = errno;
+ if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+ errno = save_errno;
+ }
+ if (entry->d_type == DT_DIR) {
+ ret &= fs_mgr_rm_all(file, change, level + 1);
+ if (!rmdir(file.c_str())) {
+ if (change) *change = true;
+ } else {
+ if (errno != ENOENT) ret = false;
+ PERROR << "rmdir " << file << " depth=" << level;
+ }
+ continue;
+ }
+ if (!unlink(file.c_str())) {
+ if (change) *change = true;
+ } else {
+ if (errno != ENOENT) ret = false;
+ PERROR << "rm " << file << " depth=" << level;
+ }
+ }
+ return ret;
}
const auto kUpperName = "upper"s;
@@ -235,7 +286,7 @@
return ret;
}
-bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
+bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
if (!fsrec) return false;
auto fsrec_mount_point = fsrec->mount_point;
@@ -260,53 +311,6 @@
return true;
}
-
-bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
- auto save_errno = errno;
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
- if (!dir) {
- if (errno == ENOENT) {
- errno = save_errno;
- return true;
- }
- PERROR << "opendir " << path << " depth=" << level;
- if ((errno == EPERM) && (level != 0)) {
- errno = save_errno;
- return true;
- }
- return false;
- }
- dirent* entry;
- auto ret = true;
- while ((entry = readdir(dir.get()))) {
- if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
- auto file = path + "/" + entry->d_name;
- if (entry->d_type == DT_UNKNOWN) {
- struct stat st;
- save_errno = errno;
- if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
- errno = save_errno;
- }
- if (entry->d_type == DT_DIR) {
- ret &= fs_mgr_rm_all(file, change, level + 1);
- if (!rmdir(file.c_str())) {
- if (change) *change = true;
- } else {
- if (errno != ENOENT) ret = false;
- PERROR << "rmdir " << file << " depth=" << level;
- }
- continue;
- }
- if (!unlink(file.c_str())) {
- if (change) *change = true;
- } else {
- if (errno != ENOENT) ret = false;
- PERROR << "rm " << file << " depth=" << level;
- }
- }
- return ret;
-}
-
constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
@@ -532,8 +536,7 @@
}
}
-std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
- const char* mount_point = nullptr) {
+std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
std::vector<std::string> mounts;
if (!fstab) return mounts;
@@ -734,7 +737,7 @@
} // namespace
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
auto ret = false;
if (!fs_mgr_wants_overlayfs()) return ret;
@@ -761,7 +764,7 @@
return ret;
}
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab) {
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
return {};
}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 3274e0e..550dd18 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,8 +21,8 @@
#include <string>
#include <vector>
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4953655..5689bdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -25,6 +25,7 @@
srcs: [
"builder.cpp",
"images.cpp",
+ "partition_opener.cpp",
"reader.cpp",
"utility.cpp",
"writer.cpp",
@@ -59,6 +60,7 @@
srcs: [
"builder_test.cpp",
"io_test.cpp",
+ "test_partition_opener.cpp",
"utility_test.cpp",
],
}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2c57a35..3cd33b1 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -16,11 +16,7 @@
#include "liblp/builder.h"
-#if defined(__linux__)
-#include <linux/fs.h>
-#endif
#include <string.h>
-#include <sys/ioctl.h>
#include <algorithm>
@@ -33,49 +29,19 @@
namespace android {
namespace fs_mgr {
-bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
-#if defined(__linux__)
- android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+bool LinearExtent::AddTo(LpMetadata* out) const {
+ if (device_index_ >= out->block_devices.size()) {
+ LERROR << "Extent references unknown block device.";
return false;
}
- if (!GetDescriptorSize(fd, &device_info->size)) {
- return false;
- }
- if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
- return false;
- }
-
- int alignment_offset;
- if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
- return false;
- }
- int logical_block_size;
- if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
- return false;
- }
-
- device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
- device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+ out->extents.emplace_back(
+ LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
return true;
-#else
- (void)block_device;
- (void)device_info;
- LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
- return false;
-#endif
}
-void LinearExtent::AddTo(LpMetadata* out) const {
- out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
-}
-
-void ZeroExtent::AddTo(LpMetadata* out) const {
- out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+ out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+ return true;
}
Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
@@ -85,15 +51,17 @@
size_ += extent->num_sectors() * LP_SECTOR_SIZE;
if (LinearExtent* new_extent = extent->AsLinearExtent()) {
- if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
- extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
- // If the previous extent can be merged into this new one, do so
- // to avoid creating unnecessary extents.
+ if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
- extent = std::make_unique<LinearExtent>(
- prev_extent->num_sectors() + new_extent->num_sectors(),
- prev_extent->physical_sector());
- extents_.pop_back();
+ if (prev_extent->end_sector() == new_extent->physical_sector() &&
+ prev_extent->device_index() == new_extent->device_index()) {
+ // If the previous extent can be merged into this new one, do so
+ // to avoid creating unnecessary extents.
+ extent = std::make_unique<LinearExtent>(
+ prev_extent->num_sectors() + new_extent->num_sectors(),
+ prev_extent->device_index(), prev_extent->physical_sector());
+ extents_.pop_back();
+ }
}
}
extents_.push_back(std::move(extent));
@@ -138,9 +106,10 @@
return sectors * LP_SECTOR_SIZE;
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,
+ const std::string& super_partition,
uint32_t slot_number) {
- std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+ std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);
if (!metadata) {
return nullptr;
}
@@ -148,18 +117,26 @@
if (!builder) {
return nullptr;
}
- BlockDeviceInfo device_info;
- if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
- builder->UpdateBlockDeviceInfo(device_info);
+ for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+ std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+ BlockDeviceInfo device_info;
+ if (opener.GetInfo(partition_name, &device_info)) {
+ builder->UpdateBlockDeviceInfo(i, device_info);
+ }
}
return builder;
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
- uint32_t metadata_max_size,
- uint32_t metadata_slot_count) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
+ uint32_t slot_number) {
+ return New(PartitionOpener(), super_partition, slot_number);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+ const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+ uint32_t metadata_max_size, uint32_t metadata_slot_count) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
- if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+ if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
return nullptr;
}
return builder;
@@ -186,10 +163,12 @@
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
+ header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
}
bool MetadataBuilder::Init(const LpMetadata& metadata) {
geometry_ = metadata.geometry;
+ block_devices_ = metadata.block_devices;
for (const auto& group : metadata.groups) {
std::string group_name = GetPartitionGroupName(group);
@@ -209,7 +188,8 @@
for (size_t i = 0; i < partition.num_extents; i++) {
const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
- auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+ extent.target_data);
builder->AddExtent(std::move(copy));
} else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
@@ -220,7 +200,37 @@
return true;
}
-bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+ if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " logical block size must be a multiple of 512.";
+ return false;
+ }
+ if (device_info.size % device_info.logical_block_size != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " size must be a multiple of its block size.";
+ return false;
+ }
+ if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " alignment offset is not sector-aligned.";
+ return false;
+ }
+ if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment is not sector-aligned.";
+ return false;
+ }
+ if (device_info.alignment_offset > device_info.alignment) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment offset is greater than its alignment.";
+ return false;
+ }
+ return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+ const std::string& super_partition, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
if (metadata_max_size < sizeof(LpMetadataHeader)) {
LERROR << "Invalid metadata maximum size.";
@@ -230,68 +240,102 @@
LERROR << "Invalid metadata slot count.";
return false;
}
+ if (block_devices.empty()) {
+ LERROR << "No block devices were specified.";
+ return false;
+ }
// Align the metadata size up to the nearest sector.
metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
- // Check that device properties are sane.
- if (device_info.size % LP_SECTOR_SIZE != 0) {
- LERROR << "Block device size must be a multiple of 512.";
+ // Validate and build the block device list.
+ uint32_t logical_block_size = 0;
+ for (const auto& device_info : block_devices) {
+ if (!VerifyDeviceProperties(device_info)) {
+ return false;
+ }
+
+ if (!logical_block_size) {
+ logical_block_size = device_info.logical_block_size;
+ }
+ if (logical_block_size != device_info.logical_block_size) {
+ LERROR << "All partitions must have the same logical block size.";
+ return false;
+ }
+
+ LpMetadataBlockDevice out = {};
+ out.alignment = device_info.alignment;
+ out.alignment_offset = device_info.alignment_offset;
+ out.size = device_info.size;
+ if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+ LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+ return false;
+ }
+ strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+ // In the case of the super partition, this field will be adjusted
+ // later. For all partitions, the first 512 bytes are considered
+ // untouched to be compatible code that looks for an MBR. Thus we
+ // start counting free sectors at sector 1, not 0.
+ uint64_t free_area_start = LP_SECTOR_SIZE;
+ if (out.alignment || out.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+ } else {
+ free_area_start = AlignTo(free_area_start, logical_block_size);
+ }
+ out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+ // There must be one logical block of space available.
+ uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+ if (device_info.size < minimum_size) {
+ LERROR << "Block device " << device_info.partition_name
+ << " is too small to hold any logical partitions.";
+ return false;
+ }
+
+ // The "root" of the super partition is always listed first.
+ if (device_info.partition_name == super_partition) {
+ block_devices_.emplace(block_devices_.begin(), out);
+ } else {
+ block_devices_.emplace_back(out);
+ }
+ }
+ if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+ LERROR << "No super partition was specified.";
return false;
}
- if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
- LERROR << "Logical block size must be a multiple of 512.";
- return false;
- }
- if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
- LERROR << "Alignment offset is not sector-aligned.";
- return false;
- }
- if (device_info.alignment % LP_SECTOR_SIZE != 0) {
- LERROR << "Partition alignment is not sector-aligned.";
- return false;
- }
- if (device_info.alignment_offset > device_info.alignment) {
- LERROR << "Partition alignment offset is greater than its alignment.";
- return false;
- }
+
+ LpMetadataBlockDevice& super = block_devices_[0];
// We reserve a geometry block (4KB) plus space for each copy of the
// maximum size of a metadata blob. Then, we double that space since
// we store a backup copy of everything.
- uint64_t reserved =
- LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
- uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved * 2;
- if (device_info.size < total_reserved) {
+ uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
+ if (super.size < total_reserved) {
LERROR << "Attempting to create metadata on a block device that is too small.";
return false;
}
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
- if (device_info.alignment || device_info.alignment_offset) {
- free_area_start =
- AlignTo(free_area_start, device_info.alignment, device_info.alignment_offset);
+ if (super.alignment || super.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
} else {
- free_area_start = AlignTo(free_area_start, device_info.logical_block_size);
+ free_area_start = AlignTo(free_area_start, logical_block_size);
}
- uint64_t first_sector = free_area_start / LP_SECTOR_SIZE;
+ super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
// There must be one logical block of free space remaining (enough for one partition).
- uint64_t minimum_disk_size = (first_sector * LP_SECTOR_SIZE) + device_info.logical_block_size;
- if (device_info.size < minimum_disk_size) {
+ uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+ if (super.size < minimum_disk_size) {
LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
- << device_info.size;
+ << super.size;
return false;
}
- geometry_.first_logical_sector = first_sector;
geometry_.metadata_max_size = metadata_max_size;
geometry_.metadata_slot_count = metadata_slot_count;
- geometry_.alignment = device_info.alignment;
- geometry_.alignment_offset = device_info.alignment_offset;
- geometry_.block_device_size = device_info.size;
- geometry_.logical_block_size = device_info.logical_block_size;
+ geometry_.logical_block_size = logical_block_size;
if (!AddGroup("default", 0)) {
return false;
@@ -375,8 +419,9 @@
for (size_t i = 1; i < extents.size(); i++) {
const Interval& previous = extents[i - 1];
const Interval& current = extents[i];
+ DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(previous.end);
+ uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -386,36 +431,43 @@
// The new interval represents the free space starting at the end of
// the previous interval, and ending at the start of the next interval.
- free_regions->emplace_back(aligned, current.start);
+ free_regions->emplace_back(current.device_index, aligned, current.start);
}
}
auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
std::vector<Interval> free_regions;
- // Collect all extents in the partition table, then sort them by starting
- // sector.
- std::vector<Interval> extents;
+ // Collect all extents in the partition table, per-device, then sort them
+ // by starting sector.
+ std::vector<std::vector<Interval>> device_extents(block_devices_.size());
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
if (!linear) {
continue;
}
- extents.emplace_back(linear->physical_sector(),
+ CHECK(linear->device_index() < device_extents.size());
+ auto& extents = device_extents[linear->device_index()];
+ extents.emplace_back(linear->device_index(), linear->physical_sector(),
linear->physical_sector() + extent->num_sectors());
}
}
// Add 0-length intervals for the first and last sectors. This will cause
- // ExtentsToFreeList() to treat the space in between as available.
- uint64_t last_sector = geometry_.block_device_size / LP_SECTOR_SIZE;
- extents.emplace_back(geometry_.first_logical_sector, geometry_.first_logical_sector);
- extents.emplace_back(last_sector, last_sector);
+ // ExtentToFreeList() to treat the space in between as available.
+ for (size_t i = 0; i < device_extents.size(); i++) {
+ auto& extents = device_extents[i];
+ const auto& block_device = block_devices_[i];
- std::sort(extents.begin(), extents.end());
+ uint64_t first_sector = block_device.first_logical_sector;
+ uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+ extents.emplace_back(i, first_sector, first_sector);
+ extents.emplace_back(i, last_sector, last_sector);
- ExtentsToFreeList(extents, &free_regions);
+ std::sort(extents.begin(), extents.end());
+ ExtentsToFreeList(extents, &free_regions);
+ }
return free_regions;
}
@@ -470,7 +522,7 @@
uint64_t sectors = std::min(sectors_needed, region.length());
CHECK(sectors % sectors_per_block == 0);
- auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+ auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
new_extents.push_back(std::move(extent));
sectors_needed -= sectors;
if (!sectors_needed) {
@@ -498,6 +550,9 @@
metadata->header = header_;
metadata->geometry = geometry_;
+ // Assign this early so the extent table can read it.
+ metadata->block_devices = block_devices_;
+
std::map<std::string, size_t> group_indices;
for (const auto& group : groups_) {
LpMetadataPartitionGroup out = {};
@@ -542,7 +597,9 @@
part.group_index = iter->second;
for (const auto& extent : partition->extents()) {
- extent->AddTo(metadata.get());
+ if (!extent->AddTo(metadata.get())) {
+ return nullptr;
+ }
}
metadata->partitions.push_back(part);
}
@@ -550,11 +607,17 @@
metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
+ metadata->header.block_devices.num_entries =
+ static_cast<uint32_t>(metadata->block_devices.size());
return metadata;
}
uint64_t MetadataBuilder::AllocatableSpace() const {
- return geometry_.block_device_size - (geometry_.first_logical_sector * LP_SECTOR_SIZE);
+ uint64_t total_size = 0;
+ for (const auto& block_device : block_devices_) {
+ total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);
+ }
+ return total_size;
}
uint64_t MetadataBuilder::UsedSpace() const {
@@ -565,26 +628,58 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(uint64_t sector) const {
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+ uint64_t sector) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, geometry_.alignment, geometry_.alignment_offset);
+ uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
}
-bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const {
- info->size = geometry_.block_device_size;
- info->alignment = geometry_.alignment;
- info->alignment_offset = geometry_.alignment_offset;
+bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
+ uint32_t* index) const {
+ for (size_t i = 0; i < block_devices_.size(); i++) {
+ if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
+ BlockDeviceInfo* info) const {
+ uint32_t index;
+ if (!FindBlockDeviceByName(partition_name, &index)) {
+ LERROR << "No device named " << partition_name;
+ return false;
+ }
+ info->size = block_devices_[index].size;
+ info->alignment = block_devices_[index].alignment;
+ info->alignment_offset = block_devices_[index].alignment_offset;
info->logical_block_size = geometry_.logical_block_size;
+ info->partition_name = partition_name;
return true;
}
-bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) {
- if (device_info.size != geometry_.block_device_size) {
+bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,
+ const BlockDeviceInfo& device_info) {
+ uint32_t index;
+ if (!FindBlockDeviceByName(partition_name, &index)) {
+ LERROR << "No device named " << partition_name;
+ return false;
+ }
+ return UpdateBlockDeviceInfo(index, device_info);
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
+ CHECK(index < block_devices_.size());
+
+ LpMetadataBlockDevice& block_device = block_devices_[index];
+ if (device_info.size != block_device.size) {
LERROR << "Device size does not match (got " << device_info.size << ", expected "
- << geometry_.block_device_size << ")";
+ << block_device.size << ")";
return false;
}
if (device_info.logical_block_size != geometry_.logical_block_size) {
@@ -596,10 +691,10 @@
// The kernel does not guarantee these values are present, so we only
// replace existing values if the new values are non-zero.
if (device_info.alignment) {
- geometry_.alignment = device_info.alignment;
+ block_device.alignment = device_info.alignment;
}
if (device_info.alignment_offset) {
- geometry_.alignment_offset = device_info.alignment_offset;
+ block_device.alignment_offset = device_info.alignment_offset;
}
return true;
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 27ad250..c27e300 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -27,6 +27,7 @@
TEST(liblp, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(partition, nullptr);
@@ -41,6 +42,7 @@
TEST(liblp, ResizePartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
@@ -94,6 +96,7 @@
TEST(liblp, PartitionAlignment) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
// Test that we align up to one sector.
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -120,6 +123,7 @@
TEST(liblp, MetadataAlignment) {
// Make sure metadata sizes get aligned up.
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+ ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
@@ -127,12 +131,14 @@
TEST(liblp, InternalAlignment) {
// Test the metadata fitting within alignment.
- BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
+ auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 1536);
// Test a large alignment offset thrown in.
device_info.alignment_offset = 753664;
@@ -140,7 +146,9 @@
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
+ super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 1472);
// Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
@@ -154,7 +162,9 @@
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 174);
+ super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 174);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@@ -162,11 +172,13 @@
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 160);
+ super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 160);
}
TEST(liblp, InternalPartitionAlignment) {
- BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
+ BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
Partition* a = builder->AddPartition("a", 0);
@@ -292,6 +304,9 @@
unique_ptr<LpMetadata> exported = builder->Export();
EXPECT_NE(exported, nullptr);
+ auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+
// Verify geometry. Some details of this may change if we change the
// metadata structures. So in addition to checking the exact values, we
// also check that they are internally consistent after.
@@ -300,11 +315,11 @@
EXPECT_EQ(geometry.struct_size, sizeof(geometry));
EXPECT_EQ(geometry.metadata_max_size, 1024);
EXPECT_EQ(geometry.metadata_slot_count, 2);
- EXPECT_EQ(geometry.first_logical_sector, 32);
+ EXPECT_EQ(super_device->first_logical_sector, 32);
static const size_t kMetadataSpace =
((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
- EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+ EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
// Verify header.
const LpMetadataHeader& header = exported->header;
@@ -383,7 +398,7 @@
static const size_t kMetadataSize = 64 * 1024;
// No space to store metadata + geometry.
- BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
@@ -413,13 +428,10 @@
fs_mgr_free_fstab);
ASSERT_NE(fstab, nullptr);
- // This should read from the "super" partition once we have a well-defined
- // way to access it.
- struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
- ASSERT_NE(rec, nullptr);
+ PartitionOpener opener;
BlockDeviceInfo device_info;
- ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+ ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
// Sanity check that the device doesn't give us some weird inefficient
// alignment.
@@ -433,12 +445,12 @@
}
TEST(liblp, UpdateBlockDeviceInfo) {
- BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
BlockDeviceInfo new_info;
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.size, device_info.size);
EXPECT_EQ(new_info.alignment, device_info.alignment);
@@ -447,37 +459,37 @@
device_info.alignment = 0;
device_info.alignment_offset = 2048;
- ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.alignment, 4096);
EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
device_info.alignment = 8192;
device_info.alignment_offset = 0;
- ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.alignment, 8192);
EXPECT_EQ(new_info.alignment_offset, 2048);
new_info.size += 4096;
- ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.size, 1024 * 1024);
new_info.logical_block_size = 512;
- ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.logical_block_size, 4096);
}
TEST(liblp, InvalidBlockSize) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
EXPECT_EQ(builder, nullptr);
}
TEST(liblp, AlignedExtentSize) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -489,13 +501,13 @@
TEST(liblp, AlignedFreeSpace) {
// Only one sector free - at least one block is required.
- BlockDeviceInfo device_info(10240, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
ASSERT_EQ(builder, nullptr);
}
TEST(liblp, HasDefaultGroup) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -503,7 +515,7 @@
}
TEST(liblp, GroupSizeLimits) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -522,6 +534,9 @@
constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
return x << 30;
}
+constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
+ return x << 20;
+}
TEST(liblp, RemoveAndAddFirstPartition) {
auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
@@ -547,7 +562,7 @@
}
TEST(liblp, ListGroups) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(builder->AddGroup("example", 0));
@@ -557,7 +572,7 @@
}
TEST(liblp, RemoveGroupAndPartitions) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(builder->AddGroup("example", 0));
@@ -572,3 +587,48 @@
builder->RemoveGroupAndPartitions("default");
ASSERT_NE(builder->FindPartition("system"), nullptr);
}
+
+TEST(liblp, MultipleBlockDevices) {
+ std::vector<BlockDeviceInfo> partitions = {
+ BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
+ BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
+ BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096),
+ };
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+ EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+
+ // Create a partition that spans 3 devices.
+ Partition* p = builder->AddPartition("system_a", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 466976768));
+
+ unique_ptr<LpMetadata> metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->block_devices.size(), 3);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a");
+ EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);
+ EXPECT_EQ(metadata->block_devices[0].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a");
+ EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);
+ EXPECT_EQ(metadata->block_devices[1].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a");
+ EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);
+ EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
+ ASSERT_EQ(metadata->extents.size(), 3);
+ EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+ EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[0].target_data, 1984);
+ EXPECT_EQ(metadata->extents[0].target_source, 0);
+ EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+ EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[1].target_data, 1472);
+ EXPECT_EQ(metadata->extents[1].target_source, 1);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+ EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[2].target_data, 1472);
+ EXPECT_EQ(metadata->extents[2].target_source, 2);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index dfa37fe..46bdfa4 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -99,11 +99,12 @@
block_size_(block_size),
file_(nullptr, sparse_file_destroy),
images_(images) {
+ uint64_t total_size = GetTotalSuperPartitionSize(metadata);
if (block_size % LP_SECTOR_SIZE != 0) {
LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
return;
}
- if (metadata.geometry.block_device_size % block_size != 0) {
+ if (total_size % block_size != 0) {
LERROR << "Device size must be a multiple of the block size, " << block_size;
return;
}
@@ -120,7 +121,7 @@
return;
}
- uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
+ uint64_t num_blocks = total_size % block_size;
if (num_blocks >= UINT_MAX) {
// libsparse counts blocks in unsigned 32-bit integers, so we check to
// make sure we're not going to overflow.
@@ -128,7 +129,10 @@
return;
}
- file_.reset(sparse_file_new(block_size_, geometry_.block_device_size));
+ file_.reset(sparse_file_new(block_size_, total_size));
+ if (!file_) {
+ LERROR << "Could not allocate sparse file of size " << total_size;
+ }
}
bool SparseBuilder::Export(const char* file) {
@@ -333,14 +337,7 @@
bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images) {
SparseBuilder builder(metadata, block_size, images);
- if (!builder.IsValid()) {
- LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
- return false;
- }
- if (!builder.Build()) {
- return false;
- }
- return builder.Export(file);
+ return builder.IsValid() && builder.Build() && builder.Export(file);
}
} // namespace fs_mgr
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 7e07df4..f9de106 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
#include <memory>
#include "liblp.h"
+#include "partition_opener.h"
namespace android {
namespace fs_mgr {
@@ -34,34 +35,13 @@
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
static const uint32_t kDefaultBlockSize = 4096;
-struct BlockDeviceInfo {
- BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
- BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
- uint32_t logical_block_size)
- : size(size),
- alignment(alignment),
- alignment_offset(alignment_offset),
- logical_block_size(logical_block_size) {}
- // Size of the block device, in bytes.
- uint64_t size;
- // Optimal target alignment, in bytes. Partition extents will be aligned to
- // this value by default. This value must be 0 or a multiple of 512.
- uint32_t alignment;
- // Alignment offset to parent device (if any), in bytes. The sector at
- // |alignment_offset| on the target device is correctly aligned on its
- // parent device. This value must be 0 or a multiple of 512.
- uint32_t alignment_offset;
- // Block size, for aligning extent sizes and partition sizes.
- uint32_t logical_block_size;
-};
-
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
virtual ~Extent() {}
- virtual void AddTo(LpMetadata* out) const = 0;
+ virtual bool AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
uint64_t num_sectors() const { return num_sectors_; }
@@ -74,16 +54,18 @@
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
- LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
- : Extent(num_sectors), physical_sector_(physical_sector) {}
+ LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)
+ : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
- void AddTo(LpMetadata* metadata) const override;
+ bool AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
uint64_t physical_sector() const { return physical_sector_; }
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+ uint32_t device_index() const { return device_index_; }
private:
+ uint32_t device_index_;
uint64_t physical_sector_;
};
@@ -92,7 +74,7 @@
public:
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
- void AddTo(LpMetadata* out) const override;
+ bool AddTo(LpMetadata* out) const override;
};
class PartitionGroup final {
@@ -142,22 +124,29 @@
class MetadataBuilder {
public:
- // Construct an empty logical partition table builder. The block device size
- // and maximum metadata size must be specified, as this will determine which
- // areas of the physical partition can be flashed for metadata vs for logical
- // partitions.
+ // Construct an empty logical partition table builder given the specified
+ // map of partitions that are available for storing logical partitions.
+ //
+ // At least one partition in the list must be the "super" device, where
+ // metadata will be stored.
//
// If the parameters would yield invalid metadata, nullptr is returned. This
- // could happen if the block device size is too small to store the metadata
- // and backup copies.
- static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+ // could happen if the super device is too small to store all required
+ // metadata.
+ static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+ const std::string& super_partition,
uint32_t metadata_max_size,
uint32_t metadata_slot_count);
// Import an existing table for modification. This reads metadata off the
// given block device and imports it. It also adjusts alignment information
// based on run-time values in the operating system.
- static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+ static std::unique_ptr<MetadataBuilder> New(const IPartitionOpener& opener,
+ const std::string& super_partition,
+ uint32_t slot_number);
+
+ // Same as above, but use the default PartitionOpener.
+ static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
uint32_t slot_number);
// Import an existing table for modification. If the table is not valid, for
@@ -165,11 +154,20 @@
// This method is for testing or changing off-line tables.
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+ // Helper function for a single super partition, for tests.
+ static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ return New({device_info}, device_info.partition_name, metadata_max_size,
+ metadata_slot_count);
+ }
+
// Wrapper around New() with a BlockDeviceInfo that only specifies a device
// size. This is a convenience method for tests.
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
- BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
+ BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,
+ kDefaultBlockSize);
return New(device_info, metadata_max_size, metadata_slot_count);
}
@@ -224,8 +222,8 @@
// Remove all partitions belonging to a group, then remove the group.
void RemoveGroupAndPartitions(const std::string& group_name);
- bool GetBlockDeviceInfo(BlockDeviceInfo* info) const;
- bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info);
+ bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
+ bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
private:
MetadataBuilder();
@@ -233,19 +231,27 @@
MetadataBuilder(MetadataBuilder&&) = delete;
MetadataBuilder& operator=(const MetadataBuilder&) = delete;
MetadataBuilder& operator=(MetadataBuilder&&) = delete;
- bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+ uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
bool GrowPartition(Partition* partition, uint64_t aligned_size);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(uint64_t sector) const;
+ uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+ bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
+ bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
struct Interval {
+ uint32_t device_index;
uint64_t start;
uint64_t end;
- Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+ Interval(uint32_t device_index, uint64_t start, uint64_t end)
+ : device_index(device_index), start(start), end(end) {}
uint64_t length() const { return end - start; }
+
+ // Note: the device index is not included in sorting (intervals are
+ // sorted in per-device lists).
bool operator<(const Interval& other) const {
return (start == other.start) ? end < other.end : start < other.start;
}
@@ -258,6 +264,7 @@
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
std::vector<std::unique_ptr<PartitionGroup>> groups_;
+ std::vector<LpMetadataBlockDevice> block_devices_;
};
// Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 5f95dca..4669cea 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -24,7 +24,10 @@
#include <memory>
#include <string>
+#include <android-base/unique_fd.h>
+
#include "metadata_format.h"
+#include "partition_opener.h"
namespace android {
namespace fs_mgr {
@@ -37,13 +40,15 @@
std::vector<LpMetadataPartition> partitions;
std::vector<LpMetadataExtent> extents;
std::vector<LpMetadataPartitionGroup> groups;
+ std::vector<LpMetadataBlockDevice> block_devices;
};
// Place an initial partition table on the device. This will overwrite the
// existing geometry, and should not be used for normal partition table
// updates. False can be returned if the geometry is incompatible with the
// block device or an I/O error occurs.
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata);
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata);
// Update the partition table for a given metadata slot number. False is
// returned if an error occurs, which can include:
@@ -51,12 +56,19 @@
// - I/O error.
// - Corrupt or missing metadata geometry on disk.
// - Incompatible geometry.
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
- uint32_t slot_number);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number);
// Read logical partition metadata from its predetermined location on a block
// device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+ const std::string& super_partition, uint32_t slot_number);
+
+// Helper functions that use the default PartitionOpener.
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata);
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+ uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
// Read/Write logical partition metadata to an image file, for diagnostics or
// flashing.
@@ -69,6 +81,14 @@
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
+
+// Return the block device that houses the super partition metadata; returns
+// null on failure.
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
+
+// Return the total size of all partitions comprising the super partition.
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
// Helper to return a slot number for a slot suffix.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 89b219c..1e40df3 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 6
+#define LP_METADATA_MAJOR_VERSION 8
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
@@ -103,42 +103,10 @@
*/
uint32_t metadata_slot_count;
- /* 48: First usable sector for allocating logical partitions. this will be
- * the first sector after the initial geometry blocks, followed by the
- * space consumed by metadata_max_size*metadata_slot_count*2.
- */
- uint64_t first_logical_sector;
-
- /* 64: Alignment for defining partitions or partition extents. For example,
- * an alignment of 1MiB will require that all partitions have a size evenly
- * divisible by 1MiB, and that the smallest unit the partition can grow by
- * is 1MiB.
- *
- * Alignment is normally determined at runtime when growing or adding
- * partitions. If for some reason the alignment cannot be determined, then
- * this predefined alignment in the geometry is used instead. By default
- * it is set to 1MiB.
- */
- uint32_t alignment;
-
- /* 68: Alignment offset for "stacked" devices. For example, if the "super"
- * partition itself is not aligned within the parent block device's
- * partition table, then we adjust for this in deciding where to place
- * |first_logical_sector|.
- *
- * Similar to |alignment|, this will be derived from the operating system.
- * If it cannot be determined, it is assumed to be 0.
- */
- uint32_t alignment_offset;
-
- /* 72: Block device size, as specified when the metadata was created. This
- * can be used to verify the geometry against a target device.
- */
- uint64_t block_device_size;
-
- /* 76: Logical block size of the super partition block device. This is the
- * minimal alignment for partition and extent sizes, and it must be a
- * multiple of LP_SECTOR_SIZE.
+ /* 48: Logical block size. This is the minimal alignment for partition and
+ * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
+ * this must be equal across all LUNs that comprise the super partition,
+ * and thus this field is stored in the geometry, not per-device.
*/
uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;
@@ -217,6 +185,8 @@
LpMetadataTableDescriptor extents;
/* 104: Updateable group descriptor. */
LpMetadataTableDescriptor groups;
+ /* 116: Block device table. */
+ LpMetadataTableDescriptor block_devices;
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
@@ -270,6 +240,13 @@
* ZERO: This field must be 0.
*/
uint64_t target_data;
+
+ /* 20: Contents depends on target_type.
+ *
+ * LINEAR: Must be an index into the block devices table.
+ * ZERO: This field must be 0.
+ */
+ uint32_t target_source;
} __attribute__((packed)) LpMetadataExtent;
/* This struct defines an entry in the groups table. Each group has a maximum
@@ -285,6 +262,48 @@
uint64_t maximum_size;
} LpMetadataPartitionGroup;
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
+ */
+typedef struct LpMetadataBlockDevice {
+ /* 0: First usable sector for allocating logical partitions. this will be
+ * the first sector after the initial geometry blocks, followed by the
+ * space consumed by metadata_max_size*metadata_slot_count*2.
+ */
+ uint64_t first_logical_sector;
+
+ /* 8: Alignment for defining partitions or partition extents. For example,
+ * an alignment of 1MiB will require that all partitions have a size evenly
+ * divisible by 1MiB, and that the smallest unit the partition can grow by
+ * is 1MiB.
+ *
+ * Alignment is normally determined at runtime when growing or adding
+ * partitions. If for some reason the alignment cannot be determined, then
+ * this predefined alignment in the geometry is used instead. By default
+ * it is set to 1MiB.
+ */
+ uint32_t alignment;
+
+ /* 12: Alignment offset for "stacked" devices. For example, if the "super"
+ * partition itself is not aligned within the parent block device's
+ * partition table, then we adjust for this in deciding where to place
+ * |first_logical_sector|.
+ *
+ * Similar to |alignment|, this will be derived from the operating system.
+ * If it cannot be determined, it is assumed to be 0.
+ */
+ uint32_t alignment_offset;
+
+ /* 16: Block device size, as specified when the metadata was created. This
+ * can be used to verify the geometry against a target device.
+ */
+ uint64_t size;
+
+ /* 24: Partition name in the GPT. Any unused characters must be 0. */
+ char partition_name[36];
+} LpMetadataBlockDevice;
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
new file mode 100644
index 0000000..e506bd5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -0,0 +1,77 @@
+//
+// Copyright (C) 2018 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 <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct BlockDeviceInfo {
+ BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+ BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+ uint32_t alignment_offset, uint32_t logical_block_size)
+ : size(size),
+ alignment(alignment),
+ alignment_offset(alignment_offset),
+ logical_block_size(logical_block_size),
+ partition_name(partition_name) {}
+ // Size of the block device, in bytes.
+ uint64_t size;
+ // Optimal target alignment, in bytes. Partition extents will be aligned to
+ // this value by default. This value must be 0 or a multiple of 512.
+ uint32_t alignment;
+ // Alignment offset to parent device (if any), in bytes. The sector at
+ // |alignment_offset| on the target device is correctly aligned on its
+ // parent device. This value must be 0 or a multiple of 512.
+ uint32_t alignment_offset;
+ // Block size, for aligning extent sizes and partition sizes.
+ uint32_t logical_block_size;
+ // The physical partition name for this block device, as it would appear in
+ // the GPT or under /dev/block/by-name.
+ std::string partition_name;
+};
+
+// Test-friendly interface for interacting with partitions.
+class IPartitionOpener {
+ public:
+ virtual ~IPartitionOpener() = default;
+
+ // Open the given named physical partition with the provided open() flags.
+ // The name can be an absolute path if the full path is already known.
+ virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0;
+
+ // Return block device information about the given named physical partition.
+ // The name can be an absolute path if the full path is already known.
+ virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+};
+
+// Helper class to implement IPartitionOpener. If |partition_name| is not an
+// absolute path, /dev/block/by-name/ will be prepended.
+class PartitionOpener : public IPartitionOpener {
+ public:
+ virtual android::base::unique_fd Open(const std::string& partition_name,
+ int flags) const override;
+ virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2aa41f3..603e5c0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -26,6 +26,7 @@
#include "images.h"
#include "reader.h"
+#include "test_partition_opener.h"
#include "utility.h"
#include "writer.h"
@@ -101,7 +102,9 @@
if (!exported) {
return {};
}
- if (!FlashPartitionTable(fd, *exported.get())) {
+
+ TestPartitionOpener opener({{"super", fd}});
+ if (!FlashPartitionTable(opener, "super", *exported.get())) {
return {};
}
return fd;
@@ -116,14 +119,16 @@
ASSERT_TRUE(GetDescriptorSize(fd, &size));
ASSERT_EQ(size, kDiskSize);
+ TestPartitionOpener opener({{"super", fd}});
+
// Verify that we can't read unwritten metadata.
- ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+ ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
}
// Flashing metadata should not work if the metadata was created for a larger
// disk than the destination disk.
TEST(liblp, ExportDiskTooSmall) {
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -133,7 +138,9 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- EXPECT_FALSE(FlashPartitionTable(fd, *exported.get()));
+ TestPartitionOpener opener({{"super", fd}});
+
+ EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
}
// Test the basics of flashing a partition and reading it back.
@@ -145,22 +152,23 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Export and flash.
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
- ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+ ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
// Read back. Note that some fields are only filled in during
// serialization, so exported and imported will not be identical. For
// example, table sizes and checksums are computed in WritePartitionTable.
// Therefore we check on a field-by-field basis.
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Check geometry and header.
EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
- EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
EXPECT_EQ(exported->header.major_version, imported->header.major_version);
EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
EXPECT_EQ(exported->header.header_size, imported->header.header_size);
@@ -178,6 +186,11 @@
EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+
+ // Check block devices table.
+ ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());
+ EXPECT_EQ(exported->block_devices[0].first_logical_sector,
+ imported->block_devices[0].first_logical_sector);
}
// Test that we can update metadata slots without disturbing others.
@@ -185,35 +198,40 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
// Change the name before writing to the next slot.
strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
- ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
// Read back the original slot, make sure it hasn't changed.
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
// Now read back the new slot, and verify that it has a different name.
- imported = ReadMetadata(fd, 1);
+ imported = ReadMetadata(opener, "super", 1);
ASSERT_NE(imported, nullptr);
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
- uint64_t last_sector = imported->geometry.block_device_size / LP_SECTOR_SIZE;
+ auto super_device = GetMetadataSuperBlockDevice(*imported.get());
+ ASSERT_NE(super_device, nullptr);
+
+ uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
// Verify that we didn't overwrite anything in the logical paritition area.
// We expect the disk to be filled with 0xcc on creation so we can read
// this back and compare it.
char expected[LP_SECTOR_SIZE];
memset(expected, 0xcc, sizeof(expected));
- for (uint64_t i = imported->geometry.first_logical_sector; i < last_sector; i++) {
+ for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) {
char buffer[LP_SECTOR_SIZE];
ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
@@ -225,15 +243,17 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Make sure all slots are filled.
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
ASSERT_NE(metadata, nullptr);
for (uint32_t i = 1; i < kMetadataSlots; i++) {
- ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *metadata.get(), i));
}
// Verify that we can't read unavailable slots.
- EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+ EXPECT_EQ(ReadMetadata(opener, "super", kMetadataSlots), nullptr);
}
// Test that updating a metadata slot does not allow it to be computed based
@@ -242,24 +262,27 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
imported->geometry.metadata_slot_count++;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- imported->geometry.first_logical_sector++;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_EQ(imported->block_devices.size(), 1);
+ imported->block_devices[0].first_logical_sector++;
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
}
@@ -268,6 +291,8 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
LpMetadataGeometry geometry;
ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
@@ -276,7 +301,7 @@
bad_geometry.metadata_slot_count++;
ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
ASSERT_NE(metadata, nullptr);
EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
}
@@ -285,25 +310,29 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
char corruption[LP_METADATA_GEOMETRY_SIZE];
memset(corruption, 0xff, sizeof(corruption));
// Corrupt the primary geometry.
ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+ EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
// Corrupt the backup geometry.
ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+ EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
}
TEST(liblp, ReadBackupMetadata) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
char corruption[kMetadataSize];
memset(corruption, 0xff, sizeof(corruption));
@@ -312,14 +341,14 @@
ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+ EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
offset = GetBackupMetadataOffset(metadata->geometry, 0);
// Corrupt the backup metadata.
ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+ EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
}
// Test that we don't attempt to write metadata if it would overflow its
@@ -329,9 +358,11 @@
ASSERT_NE(builder, nullptr);
// Compute the maximum number of partitions we can fit in 512 bytes of
- // metadata. By default there is the header, and one partition group.
- static const size_t kMaxPartitionTableSize =
- kMetadataSize - sizeof(LpMetadataHeader) - sizeof(LpMetadataPartitionGroup);
+ // metadata. By default there is the header, one partition group, and a
+ // block device entry.
+ static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
+ sizeof(LpMetadataPartitionGroup) -
+ sizeof(LpMetadataBlockDevice);
size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
// Add this number of partitions.
@@ -347,8 +378,10 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Check that we are able to write our table.
- ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+ ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
// Check that adding one more partition overflows the metadata allotment.
partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE);
@@ -358,14 +391,17 @@
ASSERT_NE(exported, nullptr);
// The new table should be too large to be written.
- ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *exported.get(), 1));
+
+ auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
// Check that the first and last logical sectors weren't touched when we
// wrote this almost-full metadata.
char expected[LP_SECTOR_SIZE];
memset(expected, 0xcc, sizeof(expected));
char buffer[LP_SECTOR_SIZE];
- ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
}
@@ -451,23 +487,25 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Read and write it back.
writer.FailOnWrite(1);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
// We should still be able to read the backup copy.
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Flash again, this time fail the backup copy. We should still be able
// to read the primary.
writer.FailOnWrite(3);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
- imported = ReadMetadata(fd, 0);
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
}
@@ -477,23 +515,25 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Read and write it back.
writer.FailOnWrite(2);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
// We should still be able to read the primary copy.
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Flash again, this time fail the primary copy. We should still be able
// to read the primary.
writer.FailOnWrite(2);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
- imported = ReadMetadata(fd, 0);
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
}
@@ -504,20 +544,22 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Change the name of the existing partition.
- unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> new_table = ReadMetadata(opener, "super", 0);
ASSERT_NE(new_table, nullptr);
ASSERT_GE(new_table->partitions.size(), 1);
new_table->partitions[0].name[0]++;
// Flash it, but fail to write the backup copy.
writer.FailAfterWrite(2);
- ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
// When we read back, we should get the updated primary copy.
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_GE(new_table->partitions.size(), 1);
ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
@@ -526,9 +568,9 @@
// Note that the sync step should have used the primary to sync, not
// the backup.
writer.Reset();
- ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_GE(new_table->partitions.size(), 1);
ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
@@ -539,7 +581,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- BlockDeviceInfo device_info(kDiskSize, 0, 0, 512);
+ BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512);
unique_ptr<MetadataBuilder> builder =
MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
ASSERT_NE(builder, nullptr);
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
new file mode 100644
index 0000000..77b0e62
--- /dev/null
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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 "liblp/partition_opener.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string GetPartitionAbsolutePath(const std::string& path) {
+ if (path[0] == '/') {
+ return path;
+ }
+ return "/dev/block/by-name/" + path;
+}
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+ unique_fd fd(open(block_device.c_str(), O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+ return false;
+ }
+ if (!GetDescriptorSize(fd, &device_info->size)) {
+ return false;
+ }
+ if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ return false;
+ }
+
+ int alignment_offset;
+ if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ return false;
+ }
+ int logical_block_size;
+ if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+ return false;
+ }
+
+ device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+ device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+ device_info->partition_name = android::base::Basename(block_device);
+ return true;
+#else
+ (void)block_device;
+ (void)device_info;
+ LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+ return false;
+#endif
+}
+
+} // namespace
+
+unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
+ std::string path = GetPartitionAbsolutePath(partition_name);
+ return unique_fd{open(path.c_str(), flags)};
+}
+
+bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+ std::string path = GetPartitionAbsolutePath(partition_name);
+ return GetBlockDeviceInfo(path, info);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 43d8076..a02e746 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -108,15 +108,6 @@
LERROR << "Metadata max size is not sector-aligned.";
return false;
}
-
- // Check that the metadata area and logical partition areas don't overlap.
- int64_t end_of_metadata =
- GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
- geometry->metadata_max_size;
- if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
- LERROR << "Logical partition metadata overlaps with logical partition contents.";
- return false;
- }
return true;
}
@@ -195,7 +186,8 @@
}
if (!ValidateTableBounds(header, header.partitions) ||
!ValidateTableBounds(header, header.extents) ||
- !ValidateTableBounds(header, header.groups)) {
+ !ValidateTableBounds(header, header.groups) ||
+ !ValidateTableBounds(header, header.block_devices)) {
LERROR << "Logical partition metadata has invalid table bounds.";
return false;
}
@@ -282,6 +274,12 @@
memcpy(&extent, cursor, sizeof(extent));
cursor += header.extents.entry_size;
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+ extent.target_source >= header.block_devices.num_entries) {
+ LERROR << "Logical partition extent has invalid block device.";
+ return nullptr;
+ }
+
metadata->extents.push_back(extent);
}
@@ -294,6 +292,28 @@
metadata->groups.push_back(group);
}
+ cursor = buffer.get() + header.block_devices.offset;
+ for (size_t i = 0; i < header.block_devices.num_entries; i++) {
+ LpMetadataBlockDevice device = {};
+ memcpy(&device, cursor, sizeof(device));
+ cursor += header.block_devices.entry_size;
+
+ metadata->block_devices.push_back(device);
+ }
+
+ const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
+ if (!super_device) {
+ LERROR << "Metadata does not specify a super device.";
+ return nullptr;
+ }
+
+ // Check that the metadata area and logical partition areas don't overlap.
+ uint64_t metadata_region =
+ GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
+ if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Logical partition metadata overlaps with logical partition contents.";
+ return nullptr;
+ }
return metadata;
}
@@ -328,7 +348,14 @@
return ParseMetadata(geometry, fd);
}
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+ const std::string& super_partition, uint32_t slot_number) {
+ android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+ return nullptr;
+ }
+
LpMetadataGeometry geometry;
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
return nullptr;
@@ -347,13 +374,8 @@
return ReadBackupMetadata(fd, geometry, slot_number);
}
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
- android::base::unique_fd fd(open(block_device, O_RDONLY));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
- return nullptr;
- }
- return ReadMetadata(fd, slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
+ return ReadMetadata(PartitionOpener(), super_partition, slot_number);
}
static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
@@ -374,5 +396,9 @@
return NameFromFixedArray(group.name, sizeof(group.name));
}
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
+ return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 24b2611..d5d5188 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -31,7 +31,6 @@
bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
// Helper functions for manually reading geometry and metadata.
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
size_t size);
diff --git a/fs_mgr/liblp/test_partition_opener.cpp b/fs_mgr/liblp/test_partition_opener.cpp
new file mode 100644
index 0000000..c796f6c
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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 "test_partition_opener.h"
+
+#include <errno.h>
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+TestPartitionOpener::TestPartitionOpener(
+ const std::map<std::string, int>& partition_map,
+ const std::map<std::string, BlockDeviceInfo>& partition_info)
+ : partition_map_(partition_map), partition_info_(partition_info) {}
+
+unique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const {
+ auto iter = partition_map_.find(partition_name);
+ if (iter == partition_map_.end()) {
+ errno = ENOENT;
+ return {};
+ }
+ return unique_fd{dup(iter->second)};
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+ auto iter = partition_info_.find(partition_name);
+ if (iter == partition_info_.end()) {
+ errno = ENOENT;
+ return false;
+ }
+ *info = iter->second;
+ return true;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/test_partition_opener.h b/fs_mgr/liblp/test_partition_opener.h
new file mode 100644
index 0000000..b90fee7
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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 <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/partition_opener.h>
+
+namespace android {
+namespace fs_mgr {
+
+class TestPartitionOpener : public PartitionOpener {
+ public:
+ explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,
+ const std::map<std::string, BlockDeviceInfo>& partition_info = {});
+
+ android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+ bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+
+ private:
+ std::map<std::string, int> partition_map_;
+ std::map<std::string, BlockDeviceInfo> partition_info_;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 0556833..742ad82 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -16,7 +16,6 @@
#include <fcntl.h>
#include <stdint.h>
-#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -66,11 +65,8 @@
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
-
int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
geometry.metadata_max_size * slot_number;
- CHECK(offset + geometry.metadata_max_size <=
- int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
return offset;
}
@@ -81,6 +77,18 @@
return start + int64_t(geometry.metadata_max_size * slot_number);
}
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
+ return LP_PARTITION_RESERVED_BYTES +
+ (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
+}
+
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
+ if (metadata.block_devices.empty()) {
+ return nullptr;
+ }
+ return &metadata.block_devices[0];
+}
+
void SHA256(const void* data, size_t length, uint8_t out[32]) {
SHA256_CTX c;
SHA256_Init(&c);
@@ -100,5 +108,13 @@
return suffix[1] - 'a';
}
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
+ uint64_t size = 0;
+ for (const auto& block_device : metadata.block_devices) {
+ size += block_device.size;
+ }
+ return size;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 61e7d31..65e643b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -23,7 +23,7 @@
#include <android-base/logging.h>
-#include "liblp/metadata_format.h"
+#include "liblp/liblp.h"
#define LP_TAG "[liblp]"
#define LWARN LOG(WARNING) << LP_TAG
@@ -50,6 +50,10 @@
// device.
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+// Return the total space at the start of the super partition that must be set
+// aside from headers/metadata and backups.
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
+
// Cross-platform helper for lseek64().
int64_t SeekFile64(int fd, int64_t offset, int whence);
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 8baf9e7..bdf6dfd 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -36,10 +36,6 @@
{0},
16384,
4,
- 10000,
- 0,
- 0,
- 1024 * 1024,
4096};
static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index f857d8c..c740bd4 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -43,9 +43,7 @@
static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
return g1.metadata_max_size == g2.metadata_max_size &&
g1.metadata_slot_count == g2.metadata_slot_count &&
- g1.block_device_size == g2.block_device_size &&
- g1.logical_block_size == g2.logical_block_size &&
- g1.first_logical_sector == g2.first_logical_sector;
+ g1.logical_block_size == g2.logical_block_size;
}
std::string SerializeMetadata(const LpMetadata& input) {
@@ -59,15 +57,18 @@
metadata.extents.size() * sizeof(LpMetadataExtent));
std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
+ std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
+ metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
// Compute positions of tables.
header.partitions.offset = 0;
header.extents.offset = header.partitions.offset + partitions.size();
header.groups.offset = header.extents.offset + extents.size();
- header.tables_size = header.groups.offset + groups.size();
+ header.block_devices.offset = header.groups.offset + groups.size();
+ header.tables_size = header.block_devices.offset + block_devices.size();
// Compute payload checksum.
- std::string tables = partitions + extents + groups;
+ std::string tables = partitions + extents + groups + block_devices;
SHA256(tables.data(), tables.size(), header.tables_checksum);
// Compute header checksum.
@@ -105,14 +106,20 @@
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
uint64_t total_reserved = reserved_size * 2;
+ const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+ if (!super_device) {
+ LERROR << "Logical partition metadata does not have a super block device.";
+ return false;
+ }
+
if (total_reserved > blockdevice_size ||
- total_reserved > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+ total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
- if (blockdevice_size != metadata.geometry.block_device_size) {
+ if (blockdevice_size != super_device->size) {
LERROR << "Block device size " << blockdevice_size
- << " does not match metadata requested size " << metadata.geometry.block_device_size;
+ << " does not match metadata requested size " << super_device->size;
return false;
}
@@ -125,11 +132,11 @@
}
// Make sure all linear extents have a valid range.
- uint64_t last_sector = geometry.block_device_size / LP_SECTOR_SIZE;
+ uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
for (const auto& extent : metadata.extents) {
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
uint64_t physical_sector = extent.target_data;
- if (physical_sector < geometry.first_logical_sector ||
+ if (physical_sector < super_device->first_logical_sector ||
physical_sector + extent.num_sectors > last_sector) {
LERROR << "Extent table entry is out of bounds.";
return false;
@@ -139,10 +146,28 @@
return true;
}
-static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+// Check that the given region is within metadata bounds.
+static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
+ const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+ if (!super_device) {
+ LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
+ return false;
+ }
+ if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
+ << " overlaps with logical partition contents";
+ return false;
+ }
+ return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
- int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
+ if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
+ return false;
+ }
if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
return false;
@@ -154,18 +179,15 @@
return true;
}
-static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
- int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
- int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET);
- if (abs_offset == (int64_t)-1) {
- PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
+ int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
+ if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
return false;
}
- if (abs_offset >= int64_t(geometry.first_logical_sector) * LP_SECTOR_SIZE) {
- PERROR << __PRETTY_FUNCTION__ << " backup offset " << abs_offset
- << " is within logical partition bounds, sector " << geometry.first_logical_sector;
+ if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
return false;
}
if (!writer(fd, blob)) {
@@ -175,18 +197,18 @@
return true;
}
-static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
// Make sure we're writing to a valid metadata slot.
- if (slot_number >= geometry.metadata_slot_count) {
+ if (slot_number >= metadata.geometry.metadata_slot_count) {
LERROR << "Invalid logical partition metadata slot number.";
return false;
}
- if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+ if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
return false;
}
- if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+ if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
return false;
}
return true;
@@ -196,7 +218,14 @@
return android::base::WriteFully(fd, blob.data(), blob.size());
}
-bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata) {
+ android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+ return false;
+ }
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
@@ -216,6 +245,8 @@
return false;
}
+ LWARN << "Flashing new logical partition geometry to " << super_partition;
+
// Write geometry to the primary and backup locations.
std::string blob = SerializeGeometry(metadata.geometry);
if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
@@ -237,18 +268,29 @@
bool ok = true;
for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
- ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
+ ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
}
return ok;
}
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
+ return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
+}
+
static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
return !memcmp(a.header.header_checksum, b.header.header_checksum,
sizeof(a.header.header_checksum));
}
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number,
const std::function<bool(int, const std::string&)>& writer) {
+ android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+ return false;
+ }
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
@@ -289,7 +331,7 @@
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
- if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+ if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
LERROR << "Error writing primary metadata to repair corrupted backup";
return false;
}
@@ -301,46 +343,31 @@
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
- if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+ if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
LERROR << "Error writing primary metadata to repair corrupted backup";
return false;
}
}
// Both copies should now be in sync, so we can continue the update.
- return WriteMetadata(fd, geometry, slot_number, blob, writer);
-}
+ if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
+ return false;
+ }
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
- android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
- return false;
- }
- if (!FlashPartitionTable(fd, metadata)) {
- return false;
- }
- LWARN << "Flashed new logical partition geometry to " << block_device;
- return true;
-}
-
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
- uint32_t slot_number) {
- android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
- return false;
- }
- if (!UpdatePartitionTable(fd, metadata, slot_number)) {
- return false;
- }
LINFO << "Updated logical partition table at slot " << slot_number << " on device "
- << block_device;
+ << super_partition;
return true;
}
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
- return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number) {
+ return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
+}
+
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+ uint32_t slot_number) {
+ PartitionOpener opener;
+ return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
}
} // namespace fs_mgr
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
index ab18d45..6f1da0f 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,10 +30,8 @@
// These variants are for testing only. The path-based functions should be used
// for actual operation, so that open() is called with the correct flags.
-bool FlashPartitionTable(int fd, const LpMetadata& metadata);
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
-
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number,
const std::function<bool(int, const std::string&)>& writer);
} // namespace fs_mgr
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index d4ca574..b6a8eef 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1,14 +1,21 @@
#! /bin/bash
-#
-# adb remount tests (overlayfs focus)
-#
-# Conditions:
-# - Must be a userdebug build.
-# - Must be in adb mode.
-# - Kernel must have overlayfs enabled and patched to support override_creds.
-# - Must have either squashfs, ext4-dedupe or right-sized partitions.
-# - Minimum expectation system and vender are overlayfs covered partitions.
-#
+
+USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+
+adb remount tests (overlayfs focus)
+
+Conditions:
+ - Must be a userdebug build.
+ - Must be in adb mode.
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either squashfs, ext4-dedupe or right-sized partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
+"
+
+if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
+ echo "${USAGE}" >&2
+ exit 0
+fi
# Helper Variables
@@ -86,11 +93,15 @@
adb reboot remount-test
}
-[ "USAGE: adb_wait
+[ "USAGE: adb_wait [timeout]
-Returns: waits until the device has returned" ]
+Returns: waits until the device has returned or the optional timeout" ]
adb_wait() {
- adb wait-for-device
+ if [ -n "${1}" ]; then
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-device
+ else
+ adb wait-for-device
+ fi
}
[ "USAGE: adb_root
@@ -178,6 +189,10 @@
fi
inFastboot && die "device in fastboot mode"
+if ! inAdb; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
+ adb_wait 2m
+fi
inAdb || die "device not in adb mode"
isDebuggable || die "device not a debug build"
@@ -205,8 +220,9 @@
adb_reboot &&
adb_wait &&
adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ adb_sh df -k </dev/null | grep "^overlay " ||
+ die "overlay takeover failed"
+adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
adb_root &&
@@ -251,8 +267,33 @@
B="`adb_cat /vendor/hello`" ||
die "re-read vendor hello after reboot"
check_eq "${A}" "${B}" vendor after reboot
+
+adb reboot-fastboot &&
+ fastboot flash vendor &&
+ fastboot reboot ||
+ die "fastbootd flash vendor"
+adb_wait &&
+ adb_root &&
+ adb_wait &&
+ adb_sh df -k </dev/null | head -1 &&
+ adb_sh df -k </dev/null | grep "^overlay " &&
+ adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ die "overlay system takeover after flash vendor"
+adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+ die "overlay minus vendor takeover after flash vendor"
+B="`adb_cat /system/hello`" ||
+ die "re-read system hello after flash vendor"
+check_eq "${A}" "${B}" system after flash vendor
+adb_root &&
+ adb_wait ||
+ die "adb root"
+B="`adb_cat /vendor/hello`" &&
+ die "re-read vendor hello after flash vendor"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+
adb remount &&
- adb_sh rm /system/hello /vendor/hello </dev/null ||
+ ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
+ adb_sh rm /system/hello </dev/null ||
die "cleanup hello"
B="`adb_cat /system/hello`" &&
die "re-read system hello after rm"
diff --git a/init/Android.bp b/init/Android.bp
index c793971..ff3b61f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,6 +74,7 @@
"libdl",
"libext4_utils",
"libfs_mgr",
+ "libfscrypt",
"libhidl-gen-utils",
"libkeyutils",
"liblog",
diff --git a/init/Android.mk b/init/Android.mk
index ef08329..c85727c 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -73,6 +73,7 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
+ libfscrypt \
libseccomp_policy \
libcrypto_utils \
libsparse \
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 2d497b3..4f8bd16 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -40,6 +40,18 @@
return true;
}
+ static constexpr const char* kPartnerPrefixes[] = {
+ "init.svc.vendor.", "ro.vendor.", "persist.vendor.",
+ "vendor.", "init.svc.odm.", "ro.odm.",
+ "persist.odm.", "odm.", "ro.boot.",
+ };
+
+ for (const auto& prefix : kPartnerPrefixes) {
+ if (android::base::StartsWith(prop_name, prefix)) {
+ return true;
+ }
+ }
+
return CanReadProperty(subcontext->context(), prop_name);
}
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7da2526..5d62c0b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -50,9 +50,9 @@
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_crypt.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <fs_mgr.h>
+#include <fscrypt/fscrypt.h>
+#include <fscrypt/fscrypt_init_extensions.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
@@ -307,8 +307,8 @@
}
}
- if (e4crypt_is_native()) {
- if (e4crypt_set_directory_policy(args[1].c_str())) {
+ if (fscrypt_is_native()) {
+ if (fscrypt_set_directory_policy(args[1].c_str())) {
return reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
}
@@ -517,8 +517,8 @@
return reboot_into_recovery(options);
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -528,8 +528,8 @@
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
return Success();
} else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -539,8 +539,8 @@
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
return Success();
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -1016,7 +1016,7 @@
}
service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- if (e4crypt_is_native()) {
+ if (fscrypt_is_native()) {
LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
if (auto result = reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1038,7 +1038,7 @@
static Result<Success> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return Success();
- auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+ auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
return ErrnoError() << "Failed to create " << unencrypted_dir;
}
diff --git a/init/parser.cpp b/init/parser.cpp
index fa0fd11..bbfbdc6 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -53,7 +53,12 @@
int section_start_line = -1;
std::vector<std::string> args;
+ // If we encounter a bad section start, there is no valid parser object to parse the subsequent
+ // sections, so we must suppress errors until the next valid section is found.
+ bool bad_section_found = false;
+
auto end_section = [&] {
+ bad_section_found = false;
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
@@ -101,6 +106,7 @@
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
+ bad_section_found = true;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
@@ -108,7 +114,7 @@
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
- } else {
+ } else if (!bad_section_found) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line
<< ": Invalid section keyword found";
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index b24468b..285f150 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __CUTILS_SOCKETS_H
-#define __CUTILS_SOCKETS_H
+#pragma once
#include <errno.h>
#include <limits.h>
@@ -141,19 +140,6 @@
const cutils_socket_buffer_t* buffers,
size_t num_buffers);
-/*
- * socket_peer_is_trusted - Takes a socket which is presumed to be a
- * connected local socket (e.g. AF_LOCAL) and returns whether the peer
- * (the userid that owns the process on the other end of that socket)
- * is one of the two trusted userids, root or shell.
- *
- * Note: This only works as advertised on the Android OS and always
- * just returns true when called on other operating systems.
- */
-extern bool socket_peer_is_trusted(int fd);
-
#ifdef __cplusplus
}
#endif
-
-#endif /* __CUTILS_SOCKETS_H */
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 0cb8a4d..2248817 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -32,34 +32,6 @@
#include "android_get_control_env.h"
-#if defined(__ANDROID__)
-/* For the socket trust (credentials) check */
-#include <private/android_filesystem_config.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused) {
-#if defined(__ANDROID__)
- ucred cr;
- socklen_t len = sizeof(cr);
- int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
- if (n != 0) {
- ALOGE("could not get socket credentials: %s\n", strerror(errno));
- return false;
- }
-
- if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
- ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
- return false;
- }
-#endif
-
- return true;
-}
-
int socket_close(int sock) {
return close(sock);
}
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 1551b5b..2d327ee 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -11,7 +11,11 @@
export_include_dirs: ["include"],
local_include_dirs: ["include"],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libstatssocket",
+ ],
whole_static_libs: ["libgtest_prod"],
cflags: [
@@ -23,17 +27,20 @@
// metricslogger shared library
// -----------------------------------------------------------------------------
-cc_library_shared {
+cc_library {
name: "libmetricslogger",
srcs: metricslogger_lib_src_files,
defaults: ["metricslogger_defaults"],
+ export_shared_lib_headers: ["libstatssocket"],
}
// static version of libmetricslogger, needed by a few art static binaries
+// TODO(b/117829226): Remove once dependencies are cleaned up.
cc_library_static {
name: "libmetricslogger_static",
srcs: metricslogger_lib_src_files,
defaults: ["metricslogger_defaults"],
+ export_shared_lib_headers: ["libstatssocket"],
}
// metricslogger shared library, debug
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index c305db2..56bd6c4 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -15,6 +15,7 @@
*/
#include <log/log_event_list.h>
+#include <stats_event_list.h>
#include <cstdint>
#include <string>
@@ -43,6 +44,7 @@
class ComplexEventLogger {
private:
android_log_event_list logger;
+ stats_event_list stats_logger;
public:
// Create a complex event with category|category|.
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6a32153..2a1b137 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,11 +18,15 @@
#include <cstdlib>
+#include <android-base/chrono_utils.h>
#include <log/event_tag_map.h>
-#include <log/log_event_list.h>
+
+using namespace android;
namespace {
+const static int kStatsEventTag = 1937006964;
+const static int kKeyValuePairAtomId = 83;
#ifdef __ANDROID__
EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
const int kSysuiMultiActionTag = android_lookupEventTagNum(
@@ -32,6 +36,12 @@
const int kSysuiMultiActionTag = 0;
#endif
+int64_t getElapsedTimeNanoSinceBoot() {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
+ android::base::boot_clock::now().time_since_epoch())
+ .count();
+}
+
} // namespace
namespace android {
@@ -42,6 +52,12 @@
android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
<< LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+
+ stats_event_list stats_log(kStatsEventTag);
+ stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data
+ << LOGBUILDER_VALUE << 1;
+ stats_log.write(LOG_ID_STATS);
}
// Mirror com.android.internal.logging.MetricsLogger#count().
@@ -49,6 +65,11 @@
android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
<< val << LOG_ID_EVENTS;
+
+ stats_event_list stats_log(kStatsEventTag);
+ stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val;
+ stats_log.write(LOG_ID_STATS);
}
// Mirror com.android.internal.logging.MetricsLogger#action().
@@ -56,34 +77,48 @@
android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
<< field << value << LOG_ID_EVENTS;
+
+ stats_event_list stats_log(kStatsEventTag);
+ stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << category << LOGBUILDER_TYPE << TYPE_ACTION << field << value;
+ stats_log.write(LOG_ID_STATS);
}
-ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ComplexEventLogger::ComplexEventLogger(int category)
+ : logger(kSysuiMultiActionTag), stats_logger(kStatsEventTag) {
logger << LOGBUILDER_CATEGORY << category;
+ stats_logger << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << category;
}
void ComplexEventLogger::SetPackageName(const std::string& package_name) {
logger << LOGBUILDER_PACKAGENAME << package_name;
+ stats_logger << LOGBUILDER_PACKAGENAME << package_name;
}
void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::AddTaggedData(int tag, float value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::Record() {
logger << LOG_ID_EVENTS;
+ stats_logger.write(LOG_ID_STATS);
}
} // namespace metricslogger
diff --git a/libstats/Android.bp b/libstats/Android.bp
index d58f294..f5ee1da 100644
--- a/libstats/Android.bp
+++ b/libstats/Android.bp
@@ -17,12 +17,13 @@
// ==========================================================
// Native library to write stats log to statsd socket
// ==========================================================
-cc_library_static {
+cc_library {
name: "libstatssocket",
srcs: [
"stats_event_list.c",
"statsd_writer.c",
],
+ host_supported: true,
cflags: [
"-Wall",
"-Werror",
@@ -32,6 +33,7 @@
],
export_include_dirs: ["include"],
shared_libs: [
+ "libcutils",
"liblog",
],
}
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 8eedc60..72770d4 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -17,6 +17,7 @@
#include "include/stats_event_list.h"
#include <string.h>
+#include <sys/time.h>
#include "statsd_writer.h"
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
@@ -156,7 +157,14 @@
}
save_errno = errno;
+#if defined(__ANDROID__)
clock_gettime(CLOCK_REALTIME, &ts);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+#endif
int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
errno = save_errno;
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index afe401f..88f7d44 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -15,8 +15,9 @@
*/
#include "statsd_writer.h"
+#include <cutils/fs.h>
#include <cutils/sockets.h>
-#include <endian.h>
+#include <cutils/threads.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -37,6 +38,14 @@
/* branchless on many architectures. */
#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole32(x) (x)
+#else
+#define htole32(x) __bswap_32(x)
+#endif
+#endif
+
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
@@ -78,7 +87,14 @@
i = atomic_load(&statsdLoggerWrite.sock);
if (i < 0) {
- int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ int flags = SOCK_DGRAM;
+#ifdef SOCK_CLOEXEC
+ flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+ flags |= SOCK_NONBLOCK;
+#endif
+ int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
if (sock < 0) {
ret = -errno;
} else {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 608afb7..2095189 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -73,6 +73,7 @@
enabled: true,
},
double_loadable: true,
+ export_shared_lib_headers: ["libbase"],
defaults: [
"libziparchive_defaults",
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 3952532..ab38dfd 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,8 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include "android-base/off64_t.h"
+
/* Zip compression methods we support */
enum {
kCompressStored = 0, // no compression
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
index b4766f8..8c6ca79 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -15,12 +15,13 @@
*/
// Read-only stream access to Zip archives entries.
-#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
-#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
#include <vector>
-#include <ziparchive/zip_archive.h>
+#include "android-base/off64_t.h"
class ZipArchiveStreamEntry {
public:
@@ -43,5 +44,3 @@
off64_t offset_ = 0;
uint32_t crc32_ = 0u;
};
-
-#endif // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index 6e4ca62..f6c8427 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -24,6 +24,7 @@
#include <vector>
#include "android-base/macros.h"
+#include "android-base/off64_t.h"
struct z_stream_s;
typedef struct z_stream_s z_stream;
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 9d6d919..9eb7f2c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -34,6 +34,10 @@
#include <memory>
#include <vector>
+#if defined(__APPLE__)
+#define lseek64 lseek
+#endif
+
#if defined(__BIONIC__)
#include <android/fdsan.h>
#endif
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index af9f4c8..0ea7d5d 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -348,7 +348,7 @@
// Read the file back to a buffer and make sure the contents are
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
ASSERT_EQ(file_contents, buffer);
@@ -392,7 +392,7 @@
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
@@ -404,7 +404,7 @@
// Assert that the total length of the file is sane
ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
- lseek64(tmp_file.fd, 0, SEEK_END));
+ lseek(tmp_file.fd, 0, SEEK_END));
}
#if !defined(_WIN32)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index e3c4ccc..c9c9e8e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1260,6 +1260,8 @@
return maxprocp;
}
+static int last_killed_pid = -1;
+
/* Kill one process specified by procp. Returns the size of the process killed */
static int kill_one_process(struct proc* procp) {
int pid = procp->pid;
@@ -1304,6 +1306,8 @@
TRACE_KILL_END();
+ last_killed_pid = pid;
+
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
goto out;
@@ -1331,14 +1335,12 @@
}
/*
- * Find processes to kill to free required number of pages.
- * If pages_to_free is set to 0 only one process will be killed.
- * Returns the size of the killed processes.
+ * Find one process to kill at or above the given oom_adj level.
+ * Returns size of the killed process.
*/
-static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
+static int find_and_kill_process(int min_score_adj) {
int i;
- int killed_size;
- int pages_freed = 0;
+ int killed_size = 0;
#ifdef LMKD_LOG_STATS
bool lmk_state_change_start = false;
@@ -1363,20 +1365,12 @@
LMK_STATE_CHANGE_START);
}
#endif
-
- pages_freed += killed_size;
- if (pages_freed >= pages_to_free) {
-
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log && lmk_state_change_start) {
- stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
- LMK_STATE_CHANGE_STOP);
- }
-#endif
- return pages_freed;
- }
+ break;
}
}
+ if (killed_size) {
+ break;
+ }
}
#ifdef LMKD_LOG_STATS
@@ -1385,7 +1379,7 @@
}
#endif
- return pages_freed;
+ return killed_size;
}
static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1445,6 +1439,23 @@
level - 1 : level);
}
+static bool is_kill_pending(void) {
+ char buf[24];
+
+ if (last_killed_pid < 0) {
+ return false;
+ }
+
+ snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
+ if (access(buf, F_OK) == 0) {
+ return true;
+ }
+
+ // reset last killed PID because there's nothing pending
+ last_killed_pid = -1;
+ return false;
+}
+
static void mp_event_common(int data, uint32_t events __unused) {
int ret;
unsigned long long evcount;
@@ -1459,7 +1470,6 @@
enum vmpressure_level level = (enum vmpressure_level)data;
long other_free = 0, other_file = 0;
int min_score_adj;
- int pages_to_free = 0;
int minfree = 0;
static struct reread_data mem_usage_file_data = {
.filename = MEMCG_MEMORY_USAGE,
@@ -1490,7 +1500,11 @@
}
if (kill_timeout_ms) {
- if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+ // If we're within the timeout, see if there's pending reclaim work
+ // from the last killed process. If there is (as evidenced by
+ // /proc/<pid> continuing to exist), skip killing for now.
+ if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
+ (low_ram_device || is_kill_pending())) {
kill_skip_count++;
return;
}
@@ -1537,9 +1551,6 @@
return;
}
- /* Free up enough pages to push over the highest minfree level */
- pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
- ((other_free < other_file) ? other_free : other_file);
goto do_kill;
}
@@ -1595,7 +1606,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
+ if (find_and_kill_process(level_oomadj[level]) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -1619,10 +1630,7 @@
return;
}
/* Free up enough memory to downgrate the memory pressure to low level */
- if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
- pages_to_free = low_pressure_mem.max_nr_free_pages -
- mi.field.nr_free_pages;
- } else {
+ if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
if (debug_process_killing) {
ALOGI("Ignoring pressure since more memory is "
"available (%" PRId64 ") than watermark (%" PRId64 ")",
@@ -1633,7 +1641,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+ pages_freed = find_and_kill_process(min_score_adj);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -1641,25 +1649,24 @@
report_skip_count++;
return;
}
+ } else {
+ /* If we killed anything, update the last killed timestamp. */
+ last_kill_tm = curr_tm;
}
/* Log meminfo whenever we kill or when report rate limit allows */
meminfo_log(&mi);
- if (pages_freed >= pages_to_free) {
- /* Reset kill time only if reclaimed enough memory */
- last_kill_tm = curr_tm;
- }
if (use_minfree_levels) {
- ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+ ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
"free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
- pages_to_free * page_k, pages_freed * page_k,
+ pages_freed * page_k,
other_file * page_k, mi.field.nr_free_pages * page_k,
zi.field.totalreserve_pages * page_k,
minfree * page_k, min_score_adj);
} else {
- ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
- pages_to_free * page_k, pages_freed * page_k, min_score_adj);
+ ALOGI("Reclaimed %ldkB at oom_adj %d",
+ pages_freed * page_k, min_score_adj);
}
if (report_skip_count > 0) {