Merge "adb: work around adbd push bug."
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index b236fb3..0c3327f 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -79,22 +79,24 @@
}
std::string escape_arg(const std::string& s) {
- std::string result = s;
-
// Escape any ' in the string (before we single-quote the whole thing).
// The correct way to do this for the shell is to replace ' with '\'' --- that is,
// close the existing single-quoted string, escape a single single-quote, and start
// a new single-quoted string. Like the C preprocessor, the shell will concatenate
// these pieces into one string.
- for (size_t i = 0; i < s.size(); ++i) {
- if (s[i] == '\'') {
- result.insert(i, "'\\'");
- i += 2;
- }
+
+ std::string result;
+ result.push_back('\'');
+
+ size_t base = 0;
+ while (true) {
+ size_t found = s.find('\'', base);
+ result.append(s, base, found - base);
+ if (found == s.npos) break;
+ result.append("'\\''");
+ base = found + 1;
}
- // Prefix and suffix the whole string with '.
- result.insert(result.begin(), '\'');
result.push_back('\'');
return result;
}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index e1b6287..341323f 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -82,30 +82,38 @@
#endif
TEST(adb_utils, escape_arg) {
- ASSERT_EQ(R"('')", escape_arg(""));
+ EXPECT_EQ(R"('')", escape_arg(""));
- ASSERT_EQ(R"('abc')", escape_arg("abc"));
+ EXPECT_EQ(R"('abc')", escape_arg("abc"));
- ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
- ASSERT_EQ(R"(''\''abc')", escape_arg("'abc"));
- ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
- ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
- ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
- ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
+ auto wrap = [](const std::string& x) { return '\'' + x + '\''; };
+ const std::string q = R"('\'')";
+ EXPECT_EQ(wrap(q), escape_arg("'"));
+ EXPECT_EQ(wrap(q + q), escape_arg("''"));
+ EXPECT_EQ(wrap(q + "abc" + q), escape_arg("'abc'"));
+ EXPECT_EQ(wrap(q + "abc"), escape_arg("'abc"));
+ EXPECT_EQ(wrap("abc" + q), escape_arg("abc'"));
+ EXPECT_EQ(wrap("abc" + q + "def"), escape_arg("abc'def"));
+ EXPECT_EQ(wrap("a" + q + "b" + q + "c"), escape_arg("a'b'c"));
+ EXPECT_EQ(wrap("a" + q + "bcde" + q + "f"), escape_arg("a'bcde'f"));
- ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
- ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc"));
- ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
- ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
- ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
- ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+ EXPECT_EQ(R"(' abc')", escape_arg(" abc"));
+ EXPECT_EQ(R"('"abc')", escape_arg("\"abc"));
+ EXPECT_EQ(R"('\abc')", escape_arg("\\abc"));
+ EXPECT_EQ(R"('(abc')", escape_arg("(abc"));
+ EXPECT_EQ(R"(')abc')", escape_arg(")abc"));
- ASSERT_EQ(R"('abc ')", escape_arg("abc "));
- ASSERT_EQ(R"('abc'\''')", escape_arg("abc'"));
- ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
- ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
- ASSERT_EQ(R"('abc(')", escape_arg("abc("));
- ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
+ EXPECT_EQ(R"('abc abc')", escape_arg("abc abc"));
+ EXPECT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
+ EXPECT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
+ EXPECT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
+ EXPECT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+
+ EXPECT_EQ(R"('abc ')", escape_arg("abc "));
+ EXPECT_EQ(R"('abc"')", escape_arg("abc\""));
+ EXPECT_EQ(R"('abc\')", escape_arg("abc\\"));
+ EXPECT_EQ(R"('abc(')", escape_arg("abc("));
+ EXPECT_EQ(R"('abc)')", escape_arg("abc)"));
}
void test_mkdirs(const std::string& basepath) {
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index f7086a8..f59fa84 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -52,6 +52,7 @@
"libcrypto",
"libcrypto_utils",
"liblp",
+ "libfs_mgr",
],
srcs: [
"builder_test.cpp",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 0e4838c..720590d 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -16,22 +16,49 @@
#include "liblp/builder.h"
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
#include <string.h>
+#include <sys/ioctl.h>
#include <algorithm>
+#include <android-base/unique_fd.h>
#include <uuid/uuid.h>
#include "liblp/metadata_format.h"
+#include "liblp/reader.h"
#include "utility.h"
namespace android {
namespace fs_mgr {
-// Align a byte count up to the nearest 512-byte sector.
-template <typename T>
-static inline T AlignToSector(T value) {
- return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
+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";
+ 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;
+ }
+ if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ return false;
+ }
+ 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 {
@@ -56,7 +83,7 @@
}
void Partition::ShrinkTo(uint64_t requested_size) {
- uint64_t aligned_size = AlignToSector(requested_size);
+ uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
if (size_ <= aligned_size) {
return;
}
@@ -82,11 +109,28 @@
DCHECK(size_ == requested_size);
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+ uint32_t slot_number) {
+ std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+ if (!metadata) {
+ return nullptr;
+ }
+ std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
+ if (!builder) {
+ return nullptr;
+ }
+ BlockDeviceInfo device_info;
+ if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
+ builder->set_block_device_info(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> builder(new MetadataBuilder());
- if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
+ if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
return nullptr;
}
return builder;
@@ -135,10 +179,13 @@
}
}
}
+
+ device_info_.alignment = geometry_.alignment;
+ device_info_.alignment_offset = geometry_.alignment_offset;
return true;
}
-bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
+bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
if (metadata_max_size < sizeof(LpMetadataHeader)) {
LERROR << "Invalid metadata maximum size.";
@@ -150,7 +197,22 @@
}
// Align the metadata size up to the nearest sector.
- metadata_max_size = AlignToSector(metadata_max_size);
+ metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+
+ // Check that device properties are sane.
+ 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;
+ }
+ device_info_ = device_info;
// 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
@@ -158,20 +220,36 @@
uint64_t reserved =
LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
uint64_t total_reserved = reserved * 2;
-
- if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
+ if (device_info_.size < total_reserved) {
LERROR << "Attempting to create metadata on a block device that is too small.";
return false;
}
- // The last sector is inclusive. We subtract one to make sure that logical
- // partitions won't overlap with the same sector as the backup metadata,
- // which could happen if the block device was not aligned to LP_SECTOR_SIZE.
- geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
- geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
+ // Compute the first free sector, factoring in alignment.
+ uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
+ uint64_t first_sector = free_area / LP_SECTOR_SIZE;
+
+ // Compute the last free sector, which is inclusive. We subtract 1 to make
+ // sure that logical partitions won't overlap with the same sector as the
+ // backup metadata, which could happen if the block device was not aligned
+ // to LP_SECTOR_SIZE.
+ uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
+
+ // If this check fails, it means either (1) we did not have free space to
+ // allocate a single sector, or (2) we did, but the alignment was high
+ // enough to bump the first sector out of range. Either way, we cannot
+ // continue.
+ if (first_sector > last_sector) {
+ LERROR << "Not enough space to allocate any partition tables.";
+ return false;
+ }
+
+ geometry_.first_logical_sector = first_sector;
+ geometry_.last_logical_sector = last_sector;
geometry_.metadata_max_size = metadata_max_size;
geometry_.metadata_slot_count = metadata_slot_count;
- DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
+ geometry_.alignment = device_info_.alignment;
+ geometry_.alignment_offset = device_info_.alignment_offset;
return true;
}
@@ -209,7 +287,7 @@
bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignToSector(requested_size);
+ uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
if (partition->size() >= aligned_size) {
return true;
}
@@ -259,10 +337,16 @@
continue;
}
+ uint64_t aligned = AlignSector(previous.end);
+ if (aligned >= current.start) {
+ // After alignment, this extent is not usable.
+ continue;
+ }
+
// This gap is enough to hold the remainder of the space requested, so we
// can allocate what we need and return.
- if (current.start - previous.end >= sectors_needed) {
- auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
+ if (current.start - aligned >= sectors_needed) {
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
sectors_needed -= extent->num_sectors();
new_extents.push_back(std::move(extent));
break;
@@ -270,7 +354,7 @@
// This gap is not big enough to fit the remainder of the space requested,
// so consume the whole thing and keep looking for more.
- auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
+ auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
sectors_needed -= extent->num_sectors();
new_extents.push_back(std::move(extent));
}
@@ -286,8 +370,12 @@
}
DCHECK(first_sector <= geometry_.last_logical_sector);
+ // Note: After alignment, |first_sector| may be > the last usable sector.
+ first_sector = AlignSector(first_sector);
+
// Note: the last usable sector is inclusive.
- if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+ if (first_sector > geometry_.last_logical_sector ||
+ geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
LERROR << "Not enough free space to expand partition: " << partition->name();
return false;
}
@@ -351,5 +439,26 @@
return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
}
+uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+ // 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, device_info_.alignment, device_info_.alignment_offset);
+ return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+}
+
+void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
+ device_info_.size = device_info.size;
+
+ // 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) {
+ device_info_.alignment = device_info.alignment;
+ }
+ if (device_info.alignment_offset) {
+ device_info_.alignment_offset = device_info.alignment_offset;
+ }
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 2983f0f..08440a3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -16,6 +16,8 @@
#include <gtest/gtest.h>
#include <liblp/builder.h>
+#include "fs_mgr.h"
+#include "utility.h"
using namespace std;
using namespace android::fs_mgr;
@@ -127,6 +129,89 @@
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
}
+TEST(liblp, InternalAlignment) {
+ // Test the metadata fitting within alignment.
+ BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+ 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);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+ // Test a large alignment offset thrown in.
+ device_info.alignment_offset = 753664;
+ builder = MetadataBuilder::New(device_info, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+ // Test only an alignment offset (which should simply bump up the first
+ // logical sector).
+ device_info.alignment = 0;
+ builder = MetadataBuilder::New(device_info, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.first_logical_sector, 1484);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+ // Test a small alignment with an alignment offset.
+ device_info.alignment = 12 * 1024;
+ device_info.alignment_offset = 3 * 1024;
+ builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+ ASSERT_NE(builder, nullptr);
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.first_logical_sector, 78);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+
+ // Test a small alignment with no alignment offset.
+ device_info.alignment = 11 * 1024;
+ builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+ ASSERT_NE(builder, nullptr);
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.first_logical_sector, 72);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+}
+
+TEST(liblp, InternalPartitionAlignment) {
+ BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
+
+ Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+ ASSERT_NE(a, nullptr);
+ Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+ ASSERT_NE(b, nullptr);
+
+ // Add a bunch of small extents to each, interleaving.
+ for (size_t i = 0; i < 10; i++) {
+ ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
+ ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
+ }
+ EXPECT_EQ(a->size(), 40960);
+ EXPECT_EQ(b->size(), 40960);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // Check that each starting sector is aligned.
+ for (const auto& extent : exported->extents) {
+ ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(extent.num_sectors, 8);
+
+ uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
+ uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+ EXPECT_EQ(lba, aligned_lba);
+ }
+
+ // Sanity check one extent.
+ EXPECT_EQ(exported->extents.back().target_data, 30656);
+}
+
TEST(liblp, UseAllDiskSpace) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
EXPECT_EQ(builder->AllocatableSpace(), 1036288);
@@ -312,15 +397,72 @@
static const size_t kMetadataSize = 64 * 1024;
// No space to store metadata + geometry.
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
+ BlockDeviceInfo device_info(kDiskSize, 0, 0);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
// No space to store metadata + geometry + one free sector.
- builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
+ device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+ builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
// Space for metadata + geometry + one free sector.
- builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
- kMetadataSize, 1);
+ device_info.size += LP_SECTOR_SIZE;
+ builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_NE(builder, nullptr);
+
+ // Test with alignment.
+ device_info.alignment = 131072;
+ builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ device_info.alignment = 0;
+ device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
+ builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, block_device_info) {
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ 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);
+
+ BlockDeviceInfo device_info;
+ ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+
+ // Sanity check that the device doesn't give us some weird inefficient
+ // alignment.
+ ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+ ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+ ASSERT_LE(device_info.alignment_offset, INT_MAX);
+
+ // Having an alignment offset > alignment doesn't really make sense.
+ ASSERT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST(liblp, UpdateBlockDeviceInfo) {
+ BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ EXPECT_EQ(builder->block_device_info().size, device_info.size);
+ EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
+ EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+ device_info.alignment = 0;
+ device_info.alignment_offset = 2048;
+ builder->set_block_device_info(device_info);
+ EXPECT_EQ(builder->block_device_info().alignment, 4096);
+ EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+ device_info.alignment = 8192;
+ device_info.alignment_offset = 0;
+ builder->set_block_device_info(device_info);
+ EXPECT_EQ(builder->block_device_info().alignment, 8192);
+ EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 671a3bd..3cd95ae 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -30,6 +30,24 @@
class LinearExtent;
+// By default, partitions are aligned on a 1MiB boundary.
+static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+
+struct BlockDeviceInfo {
+ BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
+ BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
+ : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+ // 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;
+};
+
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
@@ -107,14 +125,29 @@
// 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(uint64_t blockdevice_size,
+ static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
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,
+ uint32_t slot_number);
+
// Import an existing table for modification. If the table is not valid, for
// example it contains duplicate partition names, then nullptr is returned.
+ // This method is for testing or changing off-line tables.
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+ // 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);
+ return New(device_info, metadata_max_size, metadata_slot_count);
+ }
+
// Export metadata so it can be serialized to an image, to disk, or mounted
// via device-mapper.
std::unique_ptr<LpMetadata> Export();
@@ -156,16 +189,28 @@
// Amount of space that can be allocated to logical partitions.
uint64_t AllocatableSpace() const;
+ // Merge new block device information into previous values. Alignment values
+ // are only overwritten if the new values are non-zero.
+ void set_block_device_info(const BlockDeviceInfo& device_info);
+ const BlockDeviceInfo& block_device_info() const { return device_info_; }
+
private:
MetadataBuilder();
- bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
+ uint64_t AlignSector(uint64_t sector);
+
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
+ BlockDeviceInfo device_info_;
};
+// Read BlockDeviceInfo for a given block device. This always returns false
+// for non-Linux operating systems.
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8522435..27602ac 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -107,6 +107,28 @@
* backup geometry block at the very end.
*/
uint64_t last_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;
} __attribute__((packed)) LpMetadataGeometry;
/* The logical partition metadata has a number of tables; they are described
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 7938186..a0eeec9 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -41,11 +41,18 @@
LERROR << "Logical partition metadata has invalid geometry magic signature.";
return false;
}
+ // Reject if the struct size is larger than what we compiled. This is so we
+ // can compute a checksum with the |struct_size| field rather than using
+ // sizeof.
+ if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
+ LERROR << "Logical partition metadata has unrecognized fields.";
+ return false;
+ }
// Recompute and check the CRC32.
{
LpMetadataGeometry temp = *geometry;
memset(&temp.checksum, 0, sizeof(temp.checksum));
- SHA256(&temp, sizeof(temp), temp.checksum);
+ SHA256(&temp, temp.struct_size, temp.checksum);
if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
LERROR << "Logical partition metadata has invalid geometry checksum.";
return false;
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 09ed314..4522275 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -50,6 +50,30 @@
// Compute a SHA256 hash.
void SHA256(const void* data, size_t length, uint8_t out[32]);
+// Align |base| such that it is evenly divisible by |alignment|, which does not
+// have to be a power of two.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+ if (!alignment) {
+ return base;
+ }
+ uint64_t remainder = base % alignment;
+ if (remainder == 0) {
+ return base;
+ }
+ return base + (alignment - remainder);
+}
+
+// Same as the above |AlignTo|, except that |base| is only aligned when added to
+// |alignment_offset|.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
+ uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
+ if (aligned - alignment >= base) {
+ // We overaligned (base < alignment_offset).
+ return aligned - alignment;
+ }
+ return aligned;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 25e8a25..dcc569e 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -30,7 +30,7 @@
TEST(liblp, GetMetadataOffset) {
LpMetadataGeometry geometry = {
- LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000};
+ LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000, 0, 0};
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
@@ -41,3 +41,14 @@
EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
}
+
+TEST(liblp, AlignTo) {
+ EXPECT_EQ(AlignTo(37, 0), 37);
+ EXPECT_EQ(AlignTo(1024, 1024), 1024);
+ EXPECT_EQ(AlignTo(555, 1024), 1024);
+ EXPECT_EQ(AlignTo(555, 1000), 1000);
+ EXPECT_EQ(AlignTo(0, 1024), 0);
+ EXPECT_EQ(AlignTo(54, 32, 30), 62);
+ EXPECT_EQ(AlignTo(32, 32, 30), 62);
+ EXPECT_EQ(AlignTo(17, 32, 30), 30);
+}
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 750761f..da8d2d4 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -113,6 +113,9 @@
# graphics timestamp
# 60100 - 60199 reserved for surfaceflinger
+# audio
+# 61000 - 61199 reserved for audioserver
+
# 0 for screen off, 1 for screen on, 2 for key-guard done
70000 screen_toggled (screen_state|1|5)
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 625ffba..d3f038e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -318,8 +318,8 @@
start vndservicemanager
# Once everything is setup, no need to modify /.
- # The bind+ro combination avoids modifying any other mount flags.
- mount rootfs rootfs / remount bind ro
+ # The bind+remount combination allows this to work in containers.
+ mount rootfs rootfs / remount bind ro nodev
# Mount shared so changes propagate into child namespaces
mount rootfs rootfs / shared rec
# Mount default storage into root namespace
@@ -718,6 +718,9 @@
on property:security.perf_harden=0
write /proc/sys/kernel/perf_event_paranoid 1
+ write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
+ write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
+ write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
on property:security.perf_harden=1
write /proc/sys/kernel/perf_event_paranoid 3