Merge "Assume UNSUPPORTED if the battery capacity level sysfs node does not exist."
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index eb28668..8c41c5e 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -108,9 +108,12 @@
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
// AID_UHID for using 'hid' command to read/write to /dev/uhid
+ // AID_EXT_DATA_RW for writing to /sdcard/Android/data (devices without sdcardfs)
+ // AID_EXT_OBB_RW for writing to /sdcard/Android/obb (devices without sdcardfs)
gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
- AID_NET_BW_STATS, AID_READPROC, AID_UHID};
+ AID_NET_BW_STATS, AID_READPROC, AID_UHID, AID_EXT_DATA_RW,
+ AID_EXT_OBB_RW};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1462cc9..d6fd513 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -521,13 +521,13 @@
}
// Enable casefold if needed.
-static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
- int* fs_stat) {
+static void tune_casefold(const std::string& blk_device, const FstabEntry& entry,
+ const struct ext4_super_block* sb, int* fs_stat) {
bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
bool wants_casefold =
android::base::GetBoolProperty("external_storage.casefold.enabled", false);
- if (!wants_casefold || has_casefold) return;
+ if (entry.mount_point != "data" || !wants_casefold || has_casefold ) return;
std::string casefold_support;
if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {
@@ -696,7 +696,7 @@
tune_reserved_size(blk_device, entry, &sb, &fs_stat);
tune_encrypt(blk_device, entry, &sb, &fs_stat);
tune_verity(blk_device, entry, &sb, &fs_stat);
- tune_casefold(blk_device, &sb, &fs_stat);
+ tune_casefold(blk_device, entry, &sb, &fs_stat);
tune_metadata_csum(blk_device, entry, &sb, &fs_stat);
}
}
@@ -976,6 +976,19 @@
}
}
+static void set_type_property(int status) {
+ switch (status) {
+ case FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED:
+ SetProperty("ro.crypto.type", "block");
+ break;
+ case FS_MGR_MNTALL_DEV_FILE_ENCRYPTED:
+ case FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED:
+ case FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION:
+ SetProperty("ro.crypto.type", "file");
+ break;
+ }
+}
+
static bool call_vdc(const std::vector<std::string>& args, int* ret) {
std::vector<char const*> argv;
argv.emplace_back("/system/bin/vdc");
@@ -1366,6 +1379,7 @@
avb_handle = AvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open AvbHandle";
+ set_type_property(encryptable);
return FS_MGR_MNTALL_FAIL;
}
}
@@ -1422,6 +1436,7 @@
attempted_entry.mount_point},
nullptr)) {
LERROR << "Encryption failed";
+ set_type_property(encryptable);
return FS_MGR_MNTALL_FAIL;
}
}
@@ -1519,6 +1534,8 @@
}
}
+ set_type_property(encryptable);
+
#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
fs_mgr_overlayfs_mount_all(fstab);
#endif
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 54102ec..233d15f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -640,13 +640,14 @@
entry.fs_mgr_flags.wait = true;
entry.fs_mgr_flags.logical = true;
entry.fs_mgr_flags.first_stage_mount = true;
+ fstab->emplace_back(entry);
} else {
// If the corresponding partition exists, transform all its Fstab
// by pointing .blk_device to the DSU partition.
for (auto&& entry : entries) {
entry->blk_device = partition;
// AVB keys for DSU should always be under kDsuKeysDir.
- entry->avb_keys += kDsuKeysDir;
+ entry->avb_keys = kDsuKeysDir;
}
// Make sure the ext4 is included to support GSI.
auto partition_ext4 =
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index f32e0eb..93fc131 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -640,16 +640,22 @@
return false;
}
+ bool ok = true;
for (const auto& partition : metadata->partitions) {
auto name = GetPartitionName(partition);
auto image_path = GetImageHeaderPath(name);
auto fiemap = SplitFiemap::Open(image_path);
- if (!fiemap || !fiemap->HasPinnedExtents()) {
- LOG(ERROR) << "Image is missing or was moved: " << image_path;
- return false;
+ if (fiemap == nullptr) {
+ LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+ ok = false;
+ continue;
+ }
+ if (!fiemap->HasPinnedExtents()) {
+ LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+ ok = false;
}
}
- return true;
+ return ok;
}
bool ImageManager::DisableImage(const std::string& name) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index eaef180..db50e58 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -122,6 +122,39 @@
],
}
+cc_defaults {
+ name: "libsnapshot_cow_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ srcs: [
+ "cow_reader.cpp",
+ "cow_writer.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libsnapshot_cow",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
+ host_supported: true,
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "liblog",
+ ],
+ static_libs: [
+ "libz",
+ ],
+}
+
cc_library_static {
name: "libsnapshot_test_helpers",
defaults: ["libsnapshot_defaults"],
@@ -343,3 +376,57 @@
static_executable: true,
system_shared_libs: [],
}
+
+cc_test {
+ name: "cow_api_test",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "cow_api_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "liblog",
+ "libz",
+ ],
+ static_libs: [
+ "libgtest",
+ "libsnapshot_cow",
+ ],
+ test_min_api_level: 30,
+ auto_gen_config: true,
+ require_root: false,
+}
+
+cc_binary {
+ name: "make_cow_from_ab_ota",
+ host_supported: true,
+ device_supported: false,
+ static_libs: [
+ "libbase",
+ "libbspatch",
+ "libbrotli",
+ "libbz",
+ "libchrome",
+ "libcrypto",
+ "libgflags",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libpuffpatch",
+ "libsnapshot_cow",
+ "libsparse",
+ "libxz",
+ "libz",
+ "libziparchive",
+ "update_metadata-protos",
+ ],
+ srcs: [
+ "make_cow_from_ab_ota.cpp",
+ ],
+}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
new file mode 100644
index 0000000..3b3fc47
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -0,0 +1,244 @@
+// 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 <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ cow_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_->fd, 0) << strerror(errno);
+ }
+
+ void TearDown() override { cow_ = nullptr; }
+
+ std::unique_ptr<TemporaryFile> cow_;
+};
+
+// Sink that always appends to the end of a string.
+class StringSink : public IByteSink {
+ public:
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ size_t old_size = stream_.size();
+ stream_.resize(old_size + requested, '\0');
+ *actual = requested;
+ return stream_.data() + old_size;
+ }
+ bool ReturnData(void*, size_t) override { return true; }
+ void Reset() { stream_.clear(); }
+
+ std::string& stream() { return stream_; }
+
+ private:
+ std::string stream_;
+};
+
+TEST_F(CowTest, ReadWrite) {
+ CowOptions options;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddCopy(10, 20));
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ CowHeader header;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.GetHeader(&header));
+ ASSERT_EQ(header.magic, kCowMagicNumber);
+ ASSERT_EQ(header.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.num_ops, 4);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10);
+ ASSERT_EQ(op->source, 20);
+
+ StringSink sink;
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 104);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ // Note: the zero operation gets split into two blocks.
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 52);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressGz) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ StringSink sink;
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->data_length, 56); // compressed!
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 104);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressTwoBlocks) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size * 2, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+
+ StringSink sink;
+
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+}
+
+// Only return 1-byte buffers, to stress test the partial read logic in
+// CowReader.
+class HorribleStringSink : public StringSink {
+ public:
+ void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
+};
+
+TEST_F(CowTest, HorribleSink) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+
+ HorribleStringSink sink;
+ auto op = &iter->Get();
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
new file mode 100644
index 0000000..86565c4
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -0,0 +1,264 @@
+//
+// Copyright (C) 2020 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 <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_reader.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+
+static void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+bool CowReader::Parse(android::base::unique_fd&& fd) {
+ owned_fd_ = std::move(fd);
+ return Parse(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowReader::Parse(android::base::borrowed_fd fd) {
+ fd_ = fd;
+
+ auto pos = lseek(fd_.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "read header failed";
+ return false;
+ }
+
+ // Validity check the ops range.
+ if (header_.ops_offset >= fd_size_) {
+ LOG(ERROR) << "ops offset " << header_.ops_offset << " larger than fd size " << fd_size_;
+ return false;
+ }
+ if (fd_size_ - header_.ops_offset < header_.ops_size) {
+ LOG(ERROR) << "ops size " << header_.ops_size << " is too large";
+ return false;
+ }
+
+ uint8_t header_csum[32];
+ {
+ CowHeader tmp = header_;
+ memset(&tmp.header_checksum, 0, sizeof(tmp.header_checksum));
+ SHA256(&tmp, sizeof(tmp), header_csum);
+ }
+ if (memcmp(header_csum, header_.header_checksum, sizeof(header_csum)) != 0) {
+ LOG(ERROR) << "header checksum is invalid";
+ return false;
+ }
+ return true;
+}
+
+bool CowReader::GetHeader(CowHeader* header) {
+ *header = header_;
+ return true;
+}
+
+class CowOpIter final : public ICowOpIter {
+ public:
+ CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len);
+
+ bool Done() override;
+ const CowOperation& Get() override;
+ void Next() override;
+
+ private:
+ bool HasNext();
+
+ std::unique_ptr<uint8_t[]> ops_;
+ const uint8_t* pos_;
+ const uint8_t* end_;
+ bool done_;
+};
+
+CowOpIter::CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len)
+ : ops_(std::move(ops)), pos_(ops_.get()), end_(pos_ + len), done_(!HasNext()) {}
+
+bool CowOpIter::Done() {
+ return done_;
+}
+
+bool CowOpIter::HasNext() {
+ return pos_ < end_ && size_t(end_ - pos_) >= sizeof(CowOperation);
+}
+
+void CowOpIter::Next() {
+ CHECK(!Done());
+
+ pos_ += sizeof(CowOperation);
+ if (!HasNext()) done_ = true;
+}
+
+const CowOperation& CowOpIter::Get() {
+ CHECK(!Done());
+ CHECK(HasNext());
+ return *reinterpret_cast<const CowOperation*>(pos_);
+}
+
+std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return nullptr;
+ }
+ auto ops_buffer = std::make_unique<uint8_t[]>(header_.ops_size);
+ if (!android::base::ReadFully(fd_, ops_buffer.get(), header_.ops_size)) {
+ PLOG(ERROR) << "read ops failed";
+ return nullptr;
+ }
+
+ uint8_t csum[32];
+ SHA256(ops_buffer.get(), header_.ops_size, csum);
+ if (memcmp(csum, header_.ops_checksum, sizeof(csum)) != 0) {
+ LOG(ERROR) << "ops checksum does not match";
+ return nullptr;
+ }
+
+ return std::make_unique<CowOpIter>(std::move(ops_buffer), header_.ops_size);
+}
+
+bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len) {
+ // Validate the offset, taking care to acknowledge possible overflow of offset+len.
+ if (offset < sizeof(header_) || offset >= header_.ops_offset || len >= fd_size_ ||
+ offset + len > header_.ops_offset) {
+ LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
+ return false;
+ }
+ if (lseek(fd_.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek to read raw bytes failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd_, buffer, len)) {
+ PLOG(ERROR) << "read raw bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
+ uint64_t offset = op.source;
+
+ switch (op.compression) {
+ case kCowCompressNone: {
+ size_t remaining = op.data_length;
+ while (remaining) {
+ size_t amount = remaining;
+ void* buffer = sink->GetBuffer(amount, &amount);
+ if (!buffer) {
+ LOG(ERROR) << "Could not acquire buffer from sink";
+ return false;
+ }
+ if (!GetRawBytes(offset, buffer, amount)) {
+ return false;
+ }
+ if (!sink->ReturnData(buffer, amount)) {
+ LOG(ERROR) << "Could not return buffer to sink";
+ return false;
+ }
+ remaining -= amount;
+ offset += amount;
+ }
+ return true;
+ }
+ case kCowCompressGz: {
+ auto input = std::make_unique<Bytef[]>(op.data_length);
+ if (!GetRawBytes(offset, input.get(), op.data_length)) {
+ return false;
+ }
+
+ z_stream z = {};
+ z.next_in = input.get();
+ z.avail_in = op.data_length;
+ if (int rv = inflateInit(&z); rv != Z_OK) {
+ LOG(ERROR) << "inflateInit returned error code " << rv;
+ return false;
+ }
+
+ while (z.total_out < header_.block_size) {
+ // If no more output buffer, grab a new buffer.
+ if (z.avail_out == 0) {
+ size_t amount = header_.block_size - z.total_out;
+ z.next_out = reinterpret_cast<Bytef*>(sink->GetBuffer(amount, &amount));
+ if (!z.next_out) {
+ LOG(ERROR) << "Could not acquire buffer from sink";
+ return false;
+ }
+ z.avail_out = amount;
+ }
+
+ // Remember the position of the output buffer so we can call ReturnData.
+ auto buffer = z.next_out;
+ auto avail_out = z.avail_out;
+
+ // Decompress.
+ int rv = inflate(&z, Z_NO_FLUSH);
+ if (rv != Z_OK && rv != Z_STREAM_END) {
+ LOG(ERROR) << "inflate returned error code " << rv;
+ return false;
+ }
+
+ // Return the section of the buffer that was updated.
+ if (z.avail_out < avail_out && !sink->ReturnData(buffer, avail_out - z.avail_out)) {
+ LOG(ERROR) << "Could not return buffer to sink";
+ return false;
+ }
+
+ if (rv == Z_STREAM_END) {
+ // Error if the stream has ended, but we didn't fill the entire block.
+ if (z.total_out != header_.block_size) {
+ LOG(ERROR) << "Reached gz stream end but did not read a full block of data";
+ return false;
+ }
+ break;
+ }
+
+ CHECK(rv == Z_OK);
+
+ // Error if the stream is expecting more data, but we don't have any to read.
+ if (z.avail_in == 0) {
+ LOG(ERROR) << "Gz stream ended prematurely";
+ return false;
+ }
+ }
+ return true;
+ }
+ default:
+ LOG(ERROR) << "Unknown compression type: " << op.compression;
+ return false;
+ }
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
new file mode 100644
index 0000000..ea8e534
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -0,0 +1,230 @@
+//
+// Copyright (C) 2020 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 <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_writer.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+ SetupHeaders();
+}
+
+void CowWriter::SetupHeaders() {
+ header_ = {};
+ header_.magic = kCowMagicNumber;
+ header_.major_version = kCowVersionMajor;
+ header_.minor_version = kCowVersionMinor;
+ header_.block_size = options_.block_size;
+}
+
+bool CowWriter::Initialize(android::base::unique_fd&& fd) {
+ owned_fd_ = std::move(fd);
+ return Initialize(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowWriter::Initialize(android::base::borrowed_fd fd) {
+ fd_ = fd;
+
+ // This limitation is tied to the data field size in CowOperation.
+ if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Block size is too large";
+ return false;
+ }
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+
+ if (options_.compression == "gz") {
+ compression_ = kCowCompressGz;
+ } else if (!options_.compression.empty()) {
+ LOG(ERROR) << "unrecognized compression: " << options_.compression;
+ return false;
+ }
+
+ // Headers are not complete, but this ensures the file is at the right
+ // position.
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+ header_.num_ops++;
+
+ CowOperation op = {};
+ op.type = kCowCopyOp;
+ op.new_block = new_block;
+ op.source = old_block;
+ ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+
+ return true;
+}
+
+bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % header_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << header_.block_size;
+ return false;
+ }
+
+ uint64_t pos;
+ if (!GetDataPos(&pos)) {
+ return false;
+ }
+
+ const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+ for (size_t i = 0; i < size / header_.block_size; i++) {
+ header_.num_ops++;
+
+ CowOperation op = {};
+ op.type = kCowReplaceOp;
+ op.new_block = new_block_start + i;
+ op.source = pos;
+
+ if (compression_) {
+ auto data = Compress(iter, header_.block_size);
+ if (data.empty()) {
+ PLOG(ERROR) << "AddRawBlocks: compression failed";
+ return false;
+ }
+ if (data.size() > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, data.data(), data.size())) {
+ PLOG(ERROR) << "AddRawBlocks: write failed";
+ return false;
+ }
+ op.compression = compression_;
+ op.data_length = static_cast<uint16_t>(data.size());
+ pos += data.size();
+ } else {
+ op.data_length = static_cast<uint16_t>(header_.block_size);
+ pos += header_.block_size;
+ }
+
+ ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+ iter += header_.block_size;
+ }
+
+ if (!compression_ && !android::base::WriteFully(fd_, data, size)) {
+ PLOG(ERROR) << "AddRawBlocks: write failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ header_.num_ops++;
+
+ CowOperation op = {};
+ op.type = kCowZeroOp;
+ op.new_block = new_block_start + i;
+ op.source = 0;
+ ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+ }
+ return true;
+}
+
+std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
+ switch (compression_) {
+ case kCowCompressGz: {
+ auto bound = compressBound(length);
+ auto buffer = std::make_unique<uint8_t[]>(bound);
+
+ uLongf dest_len = bound;
+ auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+ length, Z_BEST_COMPRESSION);
+ if (rv != Z_OK) {
+ LOG(ERROR) << "compress2 returned: " << rv;
+ return {};
+ }
+ return std::basic_string<uint8_t>(buffer.get(), dest_len);
+ }
+ default:
+ LOG(ERROR) << "unhandled compression type: " << compression_;
+ break;
+ }
+ return {};
+}
+
+static void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+bool CowWriter::Finalize() {
+ auto offs = lseek(fd_.get(), 0, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ header_.ops_offset = offs;
+ header_.ops_size = ops_.size();
+
+ SHA256(ops_.data(), ops_.size(), header_.ops_checksum);
+ SHA256(&header_, sizeof(header_), header_.header_checksum);
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek start failed";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write header failed";
+ return false;
+ }
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, ops_.data(), ops_.size())) {
+ PLOG(ERROR) << "write ops failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::GetDataPos(uint64_t* pos) {
+ off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ *pos = offs;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
new file mode 100644
index 0000000..6d500e7
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
+static constexpr uint32_t kCowVersionMajor = 1;
+static constexpr uint32_t kCowVersionMinor = 0;
+
+// This header appears as the first sequence of bytes in the COW. All fields
+// in the layout are little-endian encoded. The on-disk layout is:
+//
+// +-----------------------+
+// | Header (fixed) |
+// +-----------------------+
+// | Raw Data (variable) |
+// +-----------------------+
+// | Operations (variable) |
+// +-----------------------+
+//
+// The "raw data" occurs immediately after the header, and the operation
+// sequence occurs after the raw data. This ordering is intentional. While
+// streaming an OTA, we can immediately write compressed data, but store the
+// metadata in memory. At the end, we can simply append the metadata and flush
+// the file. There is no need to create separate files to store the metadata
+// and block data.
+struct CowHeader {
+ uint64_t magic;
+ uint16_t major_version;
+ uint16_t minor_version;
+
+ // Offset to the location of the operation sequence, and size of the
+ // operation sequence buffer. |ops_offset| is also the end of the
+ // raw data region.
+ uint64_t ops_offset;
+ uint64_t ops_size;
+ uint64_t num_ops;
+
+ // The size of block operations, in bytes.
+ uint32_t block_size;
+
+ // SHA256 checksums of this header, with this field set to 0.
+ uint8_t header_checksum[32];
+
+ // SHA256 of the operation sequence.
+ uint8_t ops_checksum[32];
+} __attribute__((packed));
+
+// Cow operations are currently fixed-size entries, but this may change if
+// needed.
+struct CowOperation {
+ // The operation code (see the constants and structures below).
+ uint8_t type;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the compression type of that data (see constants below).
+ uint8_t compression;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the length.
+ uint16_t data_length;
+
+ // The block of data in the new image that this operation modifies.
+ uint64_t new_block;
+
+ // The value of |source| depends on the operation code.
+ //
+ // For copy operations, this is a block location in the source image.
+ //
+ // For replace operations, this is a byte offset within the COW's data
+ // section (eg, not landing within the header or metadata). It is an
+ // absolute position within the image.
+ //
+ // For zero operations (replace with all zeroes), this is unused and must
+ // be zero.
+ uint64_t source;
+} __attribute__((packed));
+
+static constexpr uint8_t kCowCopyOp = 1;
+static constexpr uint8_t kCowReplaceOp = 2;
+static constexpr uint8_t kCowZeroOp = 3;
+
+static constexpr uint8_t kCowCompressNone = 0;
+static constexpr uint8_t kCowCompressGz = 1;
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
new file mode 100644
index 0000000..a3b1291
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -0,0 +1,107 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+class ICowOpIter;
+
+// A ByteSink object handles requests for a buffer of a specific size. It
+// always owns the underlying buffer. It's designed to minimize potential
+// copying as we parse or decompress the COW.
+class IByteSink {
+ public:
+ virtual ~IByteSink() {}
+
+ // Called when the reader has data. The size of the request is given. The
+ // sink must return a valid pointer (or null on failure), and return the
+ // maximum number of bytes that can be written to the returned buffer.
+ //
+ // The returned buffer is owned by IByteSink, but must remain valid until
+ // the ready operation has completed (or the entire buffer has been
+ // covered by calls to ReturnData).
+ //
+ // After calling GetBuffer(), all previous buffers returned are no longer
+ // valid.
+ virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
+
+ // Called when a section returned by |GetBuffer| has been filled with data.
+ virtual bool ReturnData(void* buffer, size_t length) = 0;
+};
+
+// Interface for reading from a snapuserd COW.
+class ICowReader {
+ public:
+ virtual ~ICowReader() {}
+
+ // Return the file header.
+ virtual bool GetHeader(CowHeader* header) = 0;
+
+ // Return an iterator for retrieving CowOperation entries.
+ virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+
+ // Get raw bytes from the data section.
+ virtual bool GetRawBytes(uint64_t offset, void* buffer, size_t len) = 0;
+
+ // Get decoded bytes from the data section, handling any decompression.
+ // All retrieved data is passed to the sink.
+ virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
+};
+
+// Iterate over a sequence of COW operations.
+class ICowOpIter {
+ public:
+ virtual ~ICowOpIter() {}
+
+ // True if there are more items to read, false otherwise.
+ virtual bool Done() = 0;
+
+ // Read the current operation.
+ virtual const CowOperation& Get() = 0;
+
+ // Advance to the next item.
+ virtual void Next() = 0;
+};
+
+class CowReader : public ICowReader {
+ public:
+ CowReader();
+
+ bool Parse(android::base::unique_fd&& fd);
+ bool Parse(android::base::borrowed_fd fd);
+
+ bool GetHeader(CowHeader* header) override;
+ std::unique_ptr<ICowOpIter> GetOpIter() override;
+ bool GetRawBytes(uint64_t offset, void* buffer, size_t len) override;
+ bool ReadData(const CowOperation& op, IByteSink* sink) override;
+
+ private:
+ android::base::unique_fd owned_fd_;
+ android::base::borrowed_fd fd_;
+ CowHeader header_;
+ uint64_t fd_size_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
new file mode 100644
index 0000000..5a2cbd6
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct CowOptions {
+ uint32_t block_size = 4096;
+ std::string compression;
+};
+
+// Interface for writing to a snapuserd COW. All operations are ordered; merges
+// will occur in the sequence they were added to the COW.
+class ICowWriter {
+ public:
+ explicit ICowWriter(const CowOptions& options) : options_(options) {}
+
+ virtual ~ICowWriter() {}
+
+ // Encode an operation that copies the contents of |old_block| to the
+ // location of |new_block|.
+ virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0;
+
+ // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
+ virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+
+ // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
+ virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+ protected:
+ CowOptions options_;
+};
+
+class CowWriter : public ICowWriter {
+ public:
+ explicit CowWriter(const CowOptions& options);
+
+ // Set up the writer.
+ bool Initialize(android::base::unique_fd&& fd);
+ bool Initialize(android::base::borrowed_fd fd);
+
+ bool AddCopy(uint64_t new_block, uint64_t old_block) override;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+
+ // Finalize all COW operations and flush pending writes.
+ bool Finalize();
+
+ private:
+ void SetupHeaders();
+ bool GetDataPos(uint64_t* pos);
+ std::basic_string<uint8_t> Compress(const void* data, size_t length);
+
+ private:
+ android::base::unique_fd owned_fd_;
+ android::base::borrowed_fd fd_;
+ CowHeader header_;
+ int compression_ = 0;
+
+ // :TODO: this is not efficient, but stringstream ubsan aborts because some
+ // bytes overflow a signed char.
+ std::basic_string<uint8_t> ops_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
new file mode 100644
index 0000000..0b40fd6
--- /dev/null
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -0,0 +1,690 @@
+//
+// Copyright (C) 2020 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 <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <limits>
+#include <string>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <bsdiff/bspatch.h>
+#include <bzlib.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+#include <puffin/puffpatch.h>
+#include <sparse/sparse.h>
+#include <update_engine/update_metadata.pb.h>
+#include <xz.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::Extent;
+using chromeos_update_engine::InstallOperation;
+using chromeos_update_engine::PartitionUpdate;
+
+static constexpr uint64_t kBlockSize = 4096;
+
+DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
+DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+ unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+uint64_t ToLittleEndian(uint64_t value) {
+ union {
+ uint64_t u64;
+ char bytes[8];
+ } packed;
+ packed.u64 = value;
+ std::swap(packed.bytes[0], packed.bytes[7]);
+ std::swap(packed.bytes[1], packed.bytes[6]);
+ std::swap(packed.bytes[2], packed.bytes[5]);
+ std::swap(packed.bytes[3], packed.bytes[4]);
+ return packed.u64;
+}
+
+class PayloadConverter final {
+ public:
+ PayloadConverter(const std::string& in_file, const std::string& out_dir)
+ : in_file_(in_file), out_dir_(out_dir), source_tf_zip_(nullptr, &CloseArchive) {}
+
+ bool Run();
+
+ private:
+ bool OpenPayload();
+ bool OpenSourceTargetFiles();
+ bool ProcessPartition(const PartitionUpdate& update);
+ bool ProcessOperation(const InstallOperation& op);
+ bool ProcessZero(const InstallOperation& op);
+ bool ProcessCopy(const InstallOperation& op);
+ bool ProcessReplace(const InstallOperation& op);
+ bool ProcessDiff(const InstallOperation& op);
+ borrowed_fd OpenSourceImage();
+
+ std::string in_file_;
+ std::string out_dir_;
+ unique_fd in_fd_;
+ uint64_t payload_offset_ = 0;
+ DeltaArchiveManifest manifest_;
+ std::unordered_set<std::string> dap_;
+ unique_fd source_tf_fd_;
+ std::unique_ptr<ZipArchive, decltype(&CloseArchive)> source_tf_zip_;
+
+ // Updated during ProcessPartition().
+ std::string partition_name_;
+ std::unique_ptr<CowWriter> writer_;
+ unique_fd source_image_;
+};
+
+bool PayloadConverter::Run() {
+ if (!OpenPayload()) {
+ return false;
+ }
+
+ if (manifest_.has_dynamic_partition_metadata()) {
+ const auto& dpm = manifest_.dynamic_partition_metadata();
+ for (const auto& group : dpm.groups()) {
+ for (const auto& partition : group.partition_names()) {
+ dap_.emplace(partition);
+ }
+ }
+ }
+
+ if (dap_.empty()) {
+ LOG(ERROR) << "No dynamic partitions found.";
+ return false;
+ }
+
+ if (!OpenSourceTargetFiles()) {
+ return false;
+ }
+
+ for (const auto& update : manifest_.partitions()) {
+ if (!ProcessPartition(update)) {
+ return false;
+ }
+ writer_ = nullptr;
+ source_image_.reset();
+ }
+ return true;
+}
+
+bool PayloadConverter::OpenSourceTargetFiles() {
+ if (FLAGS_source_tf.empty()) {
+ return true;
+ }
+
+ source_tf_fd_.reset(open(FLAGS_source_tf.c_str(), O_RDONLY));
+ if (source_tf_fd_ < 0) {
+ LOG(ERROR) << "open failed: " << FLAGS_source_tf;
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(source_tf_fd_.get(), &s) < 0) {
+ LOG(ERROR) << "fstat failed: " << FLAGS_source_tf;
+ return false;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+
+ // Otherwise, assume it's a zip file.
+ ZipArchiveHandle handle;
+ if (OpenArchiveFd(source_tf_fd_.get(), FLAGS_source_tf.c_str(), &handle, false)) {
+ LOG(ERROR) << "Could not open " << FLAGS_source_tf << " as a zip archive.";
+ return false;
+ }
+ source_tf_zip_.reset(handle);
+ return true;
+}
+
+bool PayloadConverter::ProcessPartition(const PartitionUpdate& update) {
+ auto partition_name = update.partition_name();
+ if (dap_.find(partition_name) == dap_.end()) {
+ // Skip non-DAP partitions.
+ return true;
+ }
+
+ auto path = out_dir_ + "/" + partition_name + ".cow";
+ unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << path;
+ return false;
+ }
+
+ CowOptions options;
+ options.block_size = kBlockSize;
+ options.compression = FLAGS_compression;
+
+ writer_ = std::make_unique<CowWriter>(options);
+ if (!writer_->Initialize(std::move(fd))) {
+ LOG(ERROR) << "Unable to initialize COW writer";
+ return false;
+ }
+
+ partition_name_ = partition_name;
+
+ for (const auto& op : update.operations()) {
+ if (!ProcessOperation(op)) {
+ return false;
+ }
+ }
+
+ if (!writer_->Finalize()) {
+ LOG(ERROR) << "Unable to finalize COW for " << partition_name;
+ return false;
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessOperation(const InstallOperation& op) {
+ switch (op.type()) {
+ case InstallOperation::SOURCE_COPY:
+ return ProcessCopy(op);
+ case InstallOperation::BROTLI_BSDIFF:
+ case InstallOperation::PUFFDIFF:
+ return ProcessDiff(op);
+ case InstallOperation::REPLACE:
+ case InstallOperation::REPLACE_XZ:
+ case InstallOperation::REPLACE_BZ:
+ return ProcessReplace(op);
+ case InstallOperation::ZERO:
+ return ProcessZero(op);
+ default:
+ LOG(ERROR) << "Unsupported op: " << (int)op.type();
+ return false;
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessZero(const InstallOperation& op) {
+ for (const auto& extent : op.dst_extents()) {
+ if (!writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks())) {
+ LOG(ERROR) << "Could not add zero operation";
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+static uint64_t SizeOfAllExtents(const T& extents) {
+ uint64_t total = 0;
+ for (const auto& extent : extents) {
+ total += extent.num_blocks() * kBlockSize;
+ }
+ return total;
+}
+
+class PuffInputStream final : public puffin::StreamInterface {
+ public:
+ PuffInputStream(uint8_t* buffer, size_t length) : buffer_(buffer), length_(length) {}
+
+ bool GetSize(uint64_t* size) const override {
+ *size = length_;
+ return true;
+ }
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = pos_;
+ return true;
+ }
+ bool Seek(uint64_t offset) override {
+ if (offset > length_) return false;
+ pos_ = offset;
+ return true;
+ }
+ bool Read(void* buffer, size_t length) override {
+ if (length_ - pos_ < length) return false;
+ memcpy(buffer, buffer_ + pos_, length);
+ pos_ += length;
+ return true;
+ }
+ bool Write(const void*, size_t) override { return false; }
+ bool Close() override { return true; }
+
+ private:
+ uint8_t* buffer_;
+ size_t length_;
+ size_t pos_;
+};
+
+class PuffOutputStream final : public puffin::StreamInterface {
+ public:
+ PuffOutputStream(std::vector<uint8_t>& stream) : stream_(stream), pos_(0) {}
+
+ bool GetSize(uint64_t* size) const override {
+ *size = stream_.size();
+ return true;
+ }
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = pos_;
+ return true;
+ }
+ bool Seek(uint64_t offset) override {
+ if (offset > stream_.size()) {
+ return false;
+ }
+ pos_ = offset;
+ return true;
+ }
+ bool Read(void* buffer, size_t length) override {
+ if (stream_.size() - pos_ < length) {
+ return false;
+ }
+ memcpy(buffer, &stream_[0] + pos_, length);
+ pos_ += length;
+ return true;
+ }
+ bool Write(const void* buffer, size_t length) override {
+ auto remaining = stream_.size() - pos_;
+ if (remaining < length) {
+ stream_.resize(stream_.size() + (length - remaining));
+ }
+ memcpy(&stream_[0] + pos_, buffer, length);
+ pos_ += length;
+ return true;
+ }
+ bool Close() override { return true; }
+
+ private:
+ std::vector<uint8_t>& stream_;
+ size_t pos_;
+};
+
+bool PayloadConverter::ProcessDiff(const InstallOperation& op) {
+ auto source_image = OpenSourceImage();
+ if (source_image < 0) {
+ return false;
+ }
+
+ uint64_t src_length = SizeOfAllExtents(op.src_extents());
+ auto src = std::make_unique<uint8_t[]>(src_length);
+ size_t src_pos = 0;
+
+ // Read source bytes.
+ for (const auto& extent : op.src_extents()) {
+ uint64_t offset = extent.start_block() * kBlockSize;
+ if (lseek(source_image.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek source image failed";
+ return false;
+ }
+
+ uint64_t size = extent.num_blocks() * kBlockSize;
+ CHECK(src_length - src_pos >= size);
+ if (!android::base::ReadFully(source_image, src.get() + src_pos, size)) {
+ PLOG(ERROR) << "read source image failed";
+ return false;
+ }
+ src_pos += size;
+ }
+ CHECK(src_pos == src_length);
+
+ // Read patch bytes.
+ auto patch = std::make_unique<uint8_t[]>(op.data_length());
+ if (lseek(in_fd_.get(), payload_offset_ + op.data_offset(), SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek payload failed";
+ return false;
+ }
+ if (!android::base::ReadFully(in_fd_, patch.get(), op.data_length())) {
+ PLOG(ERROR) << "read payload failed";
+ return false;
+ }
+
+ std::vector<uint8_t> dest(SizeOfAllExtents(op.dst_extents()));
+
+ // Apply the diff.
+ if (op.type() == InstallOperation::BROTLI_BSDIFF) {
+ size_t dest_pos = 0;
+ auto sink = [&](const uint8_t* data, size_t length) -> size_t {
+ CHECK(dest.size() - dest_pos >= length);
+ memcpy(&dest[dest_pos], data, length);
+ dest_pos += length;
+ return length;
+ };
+ if (int rv = bsdiff::bspatch(src.get(), src_pos, patch.get(), op.data_length(), sink)) {
+ LOG(ERROR) << "bspatch failed, error code " << rv;
+ return false;
+ }
+ } else if (op.type() == InstallOperation::PUFFDIFF) {
+ auto src_stream = std::make_unique<PuffInputStream>(src.get(), src_length);
+ auto dest_stream = std::make_unique<PuffOutputStream>(dest);
+ bool ok = PuffPatch(std::move(src_stream), std::move(dest_stream), patch.get(),
+ op.data_length());
+ if (!ok) {
+ LOG(ERROR) << "puffdiff operation failed to apply";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "unsupported diff operation: " << op.type();
+ return false;
+ }
+
+ // Write the final blocks to the COW.
+ size_t dest_pos = 0;
+ for (const auto& extent : op.dst_extents()) {
+ uint64_t size = extent.num_blocks() * kBlockSize;
+ CHECK(dest.size() - dest_pos >= size);
+
+ if (!writer_->AddRawBlocks(extent.start_block(), &dest[dest_pos], size)) {
+ return false;
+ }
+ dest_pos += size;
+ }
+ return true;
+}
+
+borrowed_fd PayloadConverter::OpenSourceImage() {
+ if (source_image_ >= 0) {
+ return source_image_;
+ }
+
+ unique_fd unzip_fd;
+
+ auto local_path = "IMAGES/" + partition_name_ + ".img";
+ if (source_tf_zip_) {
+ {
+ TemporaryFile tmp;
+ if (tmp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return -1;
+ }
+ unzip_fd.reset(tmp.release());
+ }
+
+ ZipEntry64 entry;
+ if (FindEntry(source_tf_zip_.get(), local_path, &entry)) {
+ LOG(ERROR) << "not found in archive: " << local_path;
+ return -1;
+ }
+ if (ExtractEntryToFile(source_tf_zip_.get(), &entry, unzip_fd.get())) {
+ LOG(ERROR) << "could not extract " << local_path;
+ return -1;
+ }
+ if (lseek(unzip_fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return -1;
+ }
+ } else if (source_tf_fd_ >= 0) {
+ unzip_fd.reset(openat(source_tf_fd_.get(), local_path.c_str(), O_RDONLY));
+ if (unzip_fd < 0) {
+ PLOG(ERROR) << "open failed: " << FLAGS_source_tf << "/" << local_path;
+ return -1;
+ }
+ } else {
+ LOG(ERROR) << "No source target files package was specified; need -source_tf";
+ return -1;
+ }
+
+ std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
+ sparse_file_import(unzip_fd.get(), false, false), &sparse_file_destroy);
+ if (s) {
+ TemporaryFile tmp;
+ if (tmp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return -1;
+ }
+ if (sparse_file_write(s.get(), tmp.fd, false, false, false) < 0) {
+ LOG(ERROR) << "sparse_file_write failed";
+ return -1;
+ }
+ source_image_.reset(tmp.release());
+ } else {
+ source_image_ = std::move(unzip_fd);
+ }
+ return source_image_;
+}
+
+template <typename ContainerType>
+class ExtentIter final {
+ public:
+ ExtentIter(const ContainerType& container)
+ : iter_(container.cbegin()), end_(container.cend()) {}
+
+ bool GetNext(uint64_t* block) {
+ while (iter_ != end_) {
+ if (dst_index_ < iter_->num_blocks()) {
+ break;
+ }
+ iter_++;
+ dst_index_ = 0;
+ }
+ if (iter_ == end_) {
+ return false;
+ }
+ *block = iter_->start_block() + dst_index_;
+ dst_index_++;
+ return true;
+ }
+
+ private:
+ typename ContainerType::const_iterator iter_;
+ typename ContainerType::const_iterator end_;
+ uint64_t dst_index_;
+};
+
+bool PayloadConverter::ProcessCopy(const InstallOperation& op) {
+ ExtentIter dst_blocks(op.dst_extents());
+
+ for (const auto& extent : op.src_extents()) {
+ for (uint64_t i = 0; i < extent.num_blocks(); i++) {
+ uint64_t src_block = extent.start_block() + i;
+ uint64_t dst_block;
+ if (!dst_blocks.GetNext(&dst_block)) {
+ LOG(ERROR) << "SOURCE_COPY contained mismatching extents";
+ return false;
+ }
+ if (src_block == dst_block) continue;
+ if (!writer_->AddCopy(dst_block, src_block)) {
+ LOG(ERROR) << "Could not add copy operation";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessReplace(const InstallOperation& op) {
+ auto buffer_size = op.data_length();
+ auto buffer = std::make_unique<char[]>(buffer_size);
+ uint64_t offs = payload_offset_ + op.data_offset();
+ if (lseek(in_fd_.get(), offs, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek " << offs << " failed";
+ return false;
+ }
+ if (!android::base::ReadFully(in_fd_, buffer.get(), buffer_size)) {
+ PLOG(ERROR) << "read " << buffer_size << " bytes from offset " << offs << "failed";
+ return false;
+ }
+
+ uint64_t dst_size = 0;
+ for (const auto& extent : op.dst_extents()) {
+ dst_size += extent.num_blocks() * kBlockSize;
+ }
+
+ if (op.type() == InstallOperation::REPLACE_BZ) {
+ auto tmp = std::make_unique<char[]>(dst_size);
+
+ uint32_t actual_size;
+ if (dst_size > std::numeric_limits<typeof(actual_size)>::max()) {
+ LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+ return false;
+ }
+ actual_size = static_cast<uint32_t>(dst_size);
+
+ auto rv = BZ2_bzBuffToBuffDecompress(tmp.get(), &actual_size, buffer.get(), buffer_size, 0,
+ 0);
+ if (rv) {
+ LOG(ERROR) << "bz2 decompress failed: " << rv;
+ return false;
+ }
+ if (actual_size != dst_size) {
+ LOG(ERROR) << "bz2 returned " << actual_size << " bytes, expected " << dst_size;
+ return false;
+ }
+ buffer = std::move(tmp);
+ buffer_size = dst_size;
+ } else if (op.type() == InstallOperation::REPLACE_XZ) {
+ constexpr uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
+
+ if (dst_size > std::numeric_limits<size_t>::max()) {
+ LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+ return false;
+ }
+
+ std::unique_ptr<struct xz_dec, decltype(&xz_dec_end)> s(
+ xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize), xz_dec_end);
+ if (!s) {
+ LOG(ERROR) << "xz_dec_init failed";
+ return false;
+ }
+
+ auto tmp = std::make_unique<char[]>(dst_size);
+
+ struct xz_buf args;
+ args.in = reinterpret_cast<const uint8_t*>(buffer.get());
+ args.in_pos = 0;
+ args.in_size = buffer_size;
+ args.out = reinterpret_cast<uint8_t*>(tmp.get());
+ args.out_pos = 0;
+ args.out_size = dst_size;
+
+ auto rv = xz_dec_run(s.get(), &args);
+ if (rv != XZ_STREAM_END) {
+ LOG(ERROR) << "xz decompress failed: " << (int)rv;
+ return false;
+ }
+ buffer = std::move(tmp);
+ buffer_size = dst_size;
+ }
+
+ uint64_t buffer_pos = 0;
+ for (const auto& extent : op.dst_extents()) {
+ uint64_t extent_size = extent.num_blocks() * kBlockSize;
+ if (buffer_size - buffer_pos < extent_size) {
+ LOG(ERROR) << "replace op ran out of input buffer";
+ return false;
+ }
+ if (!writer_->AddRawBlocks(extent.start_block(), buffer.get() + buffer_pos, extent_size)) {
+ LOG(ERROR) << "failed to add raw blocks from replace op";
+ return false;
+ }
+ buffer_pos += extent_size;
+ }
+ return true;
+}
+
+bool PayloadConverter::OpenPayload() {
+ in_fd_.reset(open(in_file_.c_str(), O_RDONLY));
+ if (in_fd_ < 0) {
+ PLOG(ERROR) << "open " << in_file_;
+ return false;
+ }
+
+ char magic[4];
+ if (!android::base::ReadFully(in_fd_, magic, sizeof(magic))) {
+ PLOG(ERROR) << "read magic";
+ return false;
+ }
+ if (std::string(magic, sizeof(magic)) != "CrAU") {
+ LOG(ERROR) << "Invalid magic in " << in_file_;
+ return false;
+ }
+
+ uint64_t version;
+ uint64_t manifest_size;
+ uint32_t manifest_signature_size = 0;
+ if (!android::base::ReadFully(in_fd_, &version, sizeof(version))) {
+ PLOG(ERROR) << "read version";
+ return false;
+ }
+ version = ToLittleEndian(version);
+ if (version < 2) {
+ LOG(ERROR) << "Only payload version 2 or higher is supported.";
+ return false;
+ }
+
+ if (!android::base::ReadFully(in_fd_, &manifest_size, sizeof(manifest_size))) {
+ PLOG(ERROR) << "read manifest_size";
+ return false;
+ }
+ manifest_size = ToLittleEndian(manifest_size);
+ if (!android::base::ReadFully(in_fd_, &manifest_signature_size,
+ sizeof(manifest_signature_size))) {
+ PLOG(ERROR) << "read manifest_signature_size";
+ return false;
+ }
+ manifest_signature_size = ntohl(manifest_signature_size);
+
+ auto manifest = std::make_unique<uint8_t[]>(manifest_size);
+ if (!android::base::ReadFully(in_fd_, manifest.get(), manifest_size)) {
+ PLOG(ERROR) << "read manifest";
+ return false;
+ }
+
+ // Skip past manifest signature.
+ auto offs = lseek(in_fd_, manifest_signature_size, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ payload_offset_ = offs;
+
+ if (!manifest_.ParseFromArray(manifest.get(), manifest_size)) {
+ LOG(ERROR) << "could not parse manifest";
+ return false;
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+ gflags::SetUsageMessage("Convert OTA payload to a Virtual A/B COW");
+ int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+ xz_crc32_init();
+
+ if (argc - arg_start != 2) {
+ std::cerr << "Usage: [options] <payload.bin> <out-dir>\n";
+ return 1;
+ }
+
+ android::snapshot::PayloadConverter pc(argv[arg_start], argv[arg_start + 1]);
+ return pc.Run() ? 0 : 1;
+}
diff --git a/init/Android.mk b/init/Android.mk
index 2fbbf4a..da94daf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -119,13 +119,6 @@
# First stage init is weird: it may start without stdout/stderr, and no /proc.
LOCAL_NOSANITIZE := hwaddress
include $(BUILD_EXECUTABLE)
-
-# LOCAL_FORCE_STATIC_EXECUTABLE targets are skipped and not defined for ASAN builds
-init_vendor_deps :=
-ifneq (true,$(my_skip_this_target))
- init_vendor_deps += init_first_stage
-endif # my_skip_this_target is true
-
endif
include $(CLEAR_VARS)
@@ -140,7 +133,8 @@
LOCAL_MODULE := init_vendor
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-LOCAL_REQUIRED_MODULES := $(init_vendor_deps)
+LOCAL_REQUIRED_MODULES := \
+ init_first_stage \
+
endif
include $(BUILD_PHONY_PACKAGE)
-init_vendor_deps :=
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 0b456e7..f5de1ad 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -570,7 +570,6 @@
trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
@@ -595,7 +594,6 @@
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
@@ -606,7 +604,6 @@
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -617,7 +614,6 @@
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
diff --git a/libcrypto_utils/.clang-format b/libcrypto_utils/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libcrypto_utils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index d7175e0..923b291 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -23,7 +23,7 @@
},
host_supported: true,
srcs: [
- "android_pubkey.c",
+ "android_pubkey.cpp",
],
cflags: [
"-Wall",
diff --git a/libcrypto_utils/android_pubkey.c b/libcrypto_utils/android_pubkey.cpp
similarity index 65%
rename from libcrypto_utils/android_pubkey.c
rename to libcrypto_utils/android_pubkey.cpp
index 3052e52..21e5663 100644
--- a/libcrypto_utils/android_pubkey.c
+++ b/libcrypto_utils/android_pubkey.cpp
@@ -35,37 +35,29 @@
// little-endian 32 bit words. Note that Android only supports little-endian
// processors, so we don't do any byte order conversions when parsing the binary
// struct.
-typedef struct RSAPublicKey {
- // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
- uint32_t modulus_size_words;
+struct RSAPublicKey {
+ // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
+ uint32_t modulus_size_words;
- // Precomputed montgomery parameter: -1 / n[0] mod 2^32
- uint32_t n0inv;
+ // Precomputed montgomery parameter: -1 / n[0] mod 2^32
+ uint32_t n0inv;
- // RSA modulus as a little-endian array.
- uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
+ // RSA modulus as a little-endian array.
+ uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
- // Montgomery parameter R^2 as a little-endian array of little-endian words.
- uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
+ // Montgomery parameter R^2 as a little-endian array.
+ uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
- // RSA modulus: 3 or 65537
- uint32_t exponent;
-} RSAPublicKey;
-
-// Reverses byte order in |buffer|.
-static void reverse_bytes(uint8_t* buffer, size_t size) {
- for (size_t i = 0; i < (size + 1) / 2; ++i) {
- uint8_t tmp = buffer[i];
- buffer[i] = buffer[size - i - 1];
- buffer[size - i - 1] = tmp;
- }
-}
+ // RSA modulus: 3 or 65537
+ uint32_t exponent;
+};
bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
bool ret = false;
- uint8_t modulus_buffer[ANDROID_PUBKEY_MODULUS_SIZE];
RSA* new_key = RSA_new();
+ BIGNUM* n = NULL;
+ BIGNUM* e = NULL;
if (!new_key) {
goto cleanup;
}
@@ -79,19 +71,24 @@
}
// Convert the modulus to big-endian byte order as expected by BN_bin2bn.
- memcpy(modulus_buffer, key_struct->modulus, sizeof(modulus_buffer));
- reverse_bytes(modulus_buffer, sizeof(modulus_buffer));
- new_key->n = BN_bin2bn(modulus_buffer, sizeof(modulus_buffer), NULL);
- if (!new_key->n) {
+ n = BN_le2bn(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, NULL);
+ if (!n) {
goto cleanup;
}
// Read the exponent.
- new_key->e = BN_new();
- if (!new_key->e || !BN_set_word(new_key->e, key_struct->exponent)) {
+ e = BN_new();
+ if (!e || !BN_set_word(e, key_struct->exponent)) {
goto cleanup;
}
+ if (!RSA_set0_key(new_key, n, e, NULL)) {
+ goto cleanup;
+ }
+ // RSA_set0_key takes ownership of its inputs on success.
+ n = NULL;
+ e = NULL;
+
// Note that we don't extract the montgomery parameters n0inv and rr from
// the RSAPublicKey structure. They assume a word size of 32 bits, but
// BoringSSL may use a word size of 64 bits internally, so we're lacking the
@@ -101,24 +98,16 @@
// pre-computed montgomery parameters.
*key = new_key;
+ new_key = NULL;
ret = true;
cleanup:
- if (!ret && new_key) {
- RSA_free(new_key);
- }
+ RSA_free(new_key);
+ BN_free(n);
+ BN_free(e);
return ret;
}
-static bool android_pubkey_encode_bignum(const BIGNUM* num, uint8_t* buffer) {
- if (!BN_bn2bin_padded(buffer, ANDROID_PUBKEY_MODULUS_SIZE, num)) {
- return false;
- }
-
- reverse_bytes(buffer, ANDROID_PUBKEY_MODULUS_SIZE);
- return true;
-}
-
bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
bool ret = false;
@@ -127,8 +116,7 @@
BIGNUM* n0inv = BN_new();
BIGNUM* rr = BN_new();
- if (sizeof(RSAPublicKey) > size ||
- RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
+ if (sizeof(RSAPublicKey) > size || RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
goto cleanup;
}
@@ -136,27 +124,26 @@
key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
// Compute and store n0inv = -1 / N[0] mod 2^32.
- if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) ||
- !BN_mod(n0inv, key->n, r32, ctx) ||
+ if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) || !BN_mod(n0inv, RSA_get0_n(key), r32, ctx) ||
!BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
goto cleanup;
}
key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
// Store the modulus.
- if (!android_pubkey_encode_bignum(key->n, key_struct->modulus)) {
+ if (!BN_bn2le_padded(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, RSA_get0_n(key))) {
goto cleanup;
}
// Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
- !BN_mod_sqr(rr, rr, key->n, ctx) ||
- !android_pubkey_encode_bignum(rr, key_struct->rr)) {
+ !BN_mod_sqr(rr, rr, RSA_get0_n(key), ctx) ||
+ !BN_bn2le_padded(key_struct->rr, ANDROID_PUBKEY_MODULUS_SIZE, rr)) {
goto cleanup;
}
// Store the exponent.
- key_struct->exponent = (uint32_t)BN_get_word(key->e);
+ key_struct->exponent = (uint32_t)BN_get_word(RSA_get0_e(key));
ret = true;
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index c74ee3e..793e2ce 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_SYSPROP (1<<27)
+#define ATRACE_TAG_LAST ATRACE_TAG_SYSPROP
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index bf244d2..40bbd5c 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -101,7 +101,7 @@
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- addr.nl_pid = getpid();
+ addr.nl_pid = 0;
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2c0156e..85556e8 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -31,84 +31,13 @@
#include <unordered_map>
#include <log/event_tag_map.h>
-#include <log/log_properties.h>
#include <private/android_logger.h>
#include <utils/FastStrcmp.h>
#include <utils/RWLock.h>
-#include "logd_reader.h"
-
#define OUT_TAG "EventTagMap"
-class MapString {
- private:
- const std::string* alloc; // HAS-AN
- const std::string_view str; // HAS-A
-
- public:
- operator const std::string_view() const {
- return str;
- }
-
- const char* data() const {
- return str.data();
- }
- size_t length() const {
- return str.length();
- }
-
- bool operator==(const MapString& rval) const {
- if (length() != rval.length()) return false;
- if (length() == 0) return true;
- return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
- }
- bool operator!=(const MapString& rval) const {
- return !(*this == rval);
- }
-
- MapString(const char* str, size_t len) : alloc(NULL), str(str, len) {
- }
- explicit MapString(const std::string& str)
- : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
- }
- MapString(MapString&& rval) noexcept
- : alloc(rval.alloc), str(rval.data(), rval.length()) {
- rval.alloc = NULL;
- }
- explicit MapString(const MapString& rval)
- : alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
- str(alloc ? alloc->data() : rval.data(), rval.length()) {
- }
-
- ~MapString() {
- if (alloc) delete alloc;
- }
-};
-
-// Hash for MapString
-template <>
-struct std::hash<MapString>
- : public std::unary_function<const MapString&, size_t> {
- size_t operator()(const MapString& __t) const noexcept {
- if (!__t.length()) return 0;
- return std::hash<std::string_view>()(std::string_view(__t));
- }
-};
-
-typedef std::pair<MapString, MapString> TagFmt;
-
-template <>
-struct std::hash<TagFmt> : public std::unary_function<const TagFmt&, size_t> {
- size_t operator()(const TagFmt& __t) const noexcept {
- // Tag is typically unique. Will cost us an extra 100ns for the
- // unordered_map lookup if we instead did a hash that combined
- // both of tag and fmt members, e.g.:
- //
- // return std::hash<MapString>()(__t.first) ^
- // std::hash<MapString>()(__t.second);
- return std::hash<MapString>()(__t.first);
- }
-};
+typedef std::pair<std::string_view, std::string_view> TagFmt;
// Map
struct EventTagMap {
@@ -119,8 +48,7 @@
private:
std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
- std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
- std::unordered_map<MapString, uint32_t> Tag2Idx;
+ std::unordered_map<std::string_view, uint32_t> Tag2Idx;
// protect unordered sets
android::RWLock rwlock;
@@ -132,7 +60,6 @@
~EventTagMap() {
Idx2TagFmt.clear();
- TagFmt2Idx.clear();
Tag2Idx.clear();
for (size_t which = 0; which < NUM_MAPS; ++which) {
if (mapAddr[which]) {
@@ -144,8 +71,7 @@
bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
const TagFmt* find(uint32_t tag) const;
- int find(TagFmt&& tagfmt) const;
- int find(MapString&& tag) const;
+ int find(std::string_view tag) const;
};
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
@@ -156,8 +82,7 @@
":%.*s:%.*s)\n";
android::RWLock::AutoWLock writeLock(rwlock);
{
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
- it = Idx2TagFmt.find(tag);
+ auto it = Idx2TagFmt.find(tag);
if (it != Idx2TagFmt.end()) {
if (verbose) {
fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
@@ -173,25 +98,7 @@
}
{
- std::unordered_map<TagFmt, uint32_t>::const_iterator it;
- it = TagFmt2Idx.find(tagfmt);
- if (it != TagFmt2Idx.end()) {
- if (verbose) {
- fprintf(stderr, errorFormat, it->second, (int)it->first.first.length(),
- it->first.first.data(), (int)it->first.second.length(),
- it->first.second.data(), tag, (int)tagfmt.first.length(),
- tagfmt.first.data(), (int)tagfmt.second.length(),
- tagfmt.second.data());
- }
- ret = false;
- } else {
- TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
- }
- }
-
- {
- std::unordered_map<MapString, uint32_t>::const_iterator it;
- it = Tag2Idx.find(tagfmt.first);
+ auto it = Tag2Idx.find(tagfmt.first);
if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
Tag2Idx.erase(it);
it = Tag2Idx.end();
@@ -205,25 +112,15 @@
}
const TagFmt* EventTagMap::find(uint32_t tag) const {
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = Idx2TagFmt.find(tag);
+ auto it = Idx2TagFmt.find(tag);
if (it == Idx2TagFmt.end()) return NULL;
return &(it->second);
}
-int EventTagMap::find(TagFmt&& tagfmt) const {
- std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+int EventTagMap::find(std::string_view tag) const {
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = TagFmt2Idx.find(std::move(tagfmt));
- if (it == TagFmt2Idx.end()) return -1;
- return it->second;
-}
-
-int EventTagMap::find(MapString&& tag) const {
- std::unordered_map<MapString, uint32_t>::const_iterator it;
- android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = Tag2Idx.find(std::move(tag));
+ auto it = Tag2Idx.find(std::move(tag));
if (it == Tag2Idx.end()) return -1;
return it->second;
}
@@ -241,29 +138,20 @@
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
-// lineNum = 0 removes verbose comments and requires us to cache the
-// content rather than make direct raw references since the content
-// will disappear after the call. A non-zero lineNum means we own the
-// data and it will outlive the call.
-//
// Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int line_num) {
char* ep;
unsigned long val = strtoul(pData, &ep, 10);
const char* cp = ep;
if (cp == pData) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", line_num);
errno = EINVAL;
return -1;
}
uint32_t tagIndex = val;
if (tagIndex != val) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", line_num);
errno = ERANGE;
return -1;
}
@@ -272,9 +160,7 @@
}
if (*cp == '\n') {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", line_num);
errno = EINVAL;
return -1;
}
@@ -284,10 +170,7 @@
size_t tagLen = cp - tag;
if (!isspace(*cp)) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
- lineNum);
- }
+ fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp, line_num);
errno = EINVAL;
return -1;
}
@@ -317,25 +200,15 @@
while (*cp && (*cp != '\n')) ++cp;
#ifdef DEBUG
- fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
+ fprintf(stderr, "%d: %p: %.*s\n", line_num, tag, (int)(cp - pData), pData);
#endif
pData = cp;
- if (lineNum) {
- if (map->emplaceUnique(tagIndex,
- TagFmt(std::make_pair(MapString(tag, tagLen),
- MapString(fmt, fmtLen))),
- verbose)) {
- return 0;
- }
- } else {
- // cache
- if (map->emplaceUnique(
- tagIndex,
- TagFmt(std::make_pair(MapString(std::string(tag, tagLen)),
- MapString(std::string(fmt, fmtLen)))))) {
- return 0;
- }
+ if (map->emplaceUnique(
+ tagIndex,
+ TagFmt(std::make_pair(std::string_view(tag, tagLen), std::string_view(fmt, fmtLen))),
+ verbose)) {
+ return 0;
}
errno = EMLINK;
return -1;
@@ -491,57 +364,10 @@
if (map) delete map;
}
-// Cache miss, go to logd to acquire a public reference.
-// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
- // call event tag service to arrange for a new tag
- char* buf = NULL;
- // Can not use android::base::StringPrintf, asprintf + free instead.
- static const char command_template[] = "getEventTag id=%u";
- int ret = asprintf(&buf, command_template, tag);
- if (ret > 0) {
- // Add some buffer margin for an estimate of the full return content.
- size_t size =
- ret - strlen(command_template) +
- strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
- if (size > (size_t)ret) {
- char* np = static_cast<char*>(realloc(buf, size));
- if (np) {
- buf = np;
- } else {
- size = ret;
- }
- } else {
- size = ret;
- }
-#ifdef __ANDROID__
- // Ask event log tag service for an existing entry
- if (SendLogdControlMessage(buf, size) >= 0) {
- buf[size - 1] = '\0';
- char* ep;
- unsigned long val = strtoul(buf, &ep, 10); // return size
- const char* cp = ep;
- if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
- ++cp;
- if (!scanTagLine(map, cp, 0)) {
- free(buf);
- return map->find(tag);
- }
- }
- }
-#endif
- free(buf);
- }
- return NULL;
-}
-
// Look up an entry in the map.
const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
- if (!str) {
- str = __getEventTag(const_cast<EventTagMap*>(map), tag);
- }
if (!str) return NULL;
if (len) *len = str->first.length();
return str->first.data();
@@ -551,83 +377,8 @@
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
- if (!str) {
- str = __getEventTag(const_cast<EventTagMap*>(map), tag);
- }
if (!str) return NULL;
if (len) *len = str->second.length();
return str->second.data();
}
-// Look up tagname, generate one if necessary, and return a tag
-int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
- const char* ep = endOfTag(tagname);
- size_t len = ep - tagname;
- if (!len || *ep) {
- errno = EINVAL;
- return -1;
- }
-
- if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
- !__android_log_is_loggable_len(prio, tagname, len,
- __android_log_is_debuggable()
- ? ANDROID_LOG_VERBOSE
- : ANDROID_LOG_DEBUG)) {
- errno = EPERM;
- return -1;
- }
-
- if (!format) format = "";
- ssize_t fmtLen = strlen(format);
- int ret = map->find(TagFmt(
- std::make_pair(MapString(tagname, len), MapString(format, fmtLen))));
- if (ret != -1) return ret;
-
- // call event tag service to arrange for a new tag
- char* buf = NULL;
- // Can not use android::base::StringPrintf, asprintf + free instead.
- static const char command_template[] = "getEventTag name=%s format=\"%s\"";
- ret = asprintf(&buf, command_template, tagname, format);
- if (ret > 0) {
- // Add some buffer margin for an estimate of the full return content.
- char* cp;
- size_t size =
- ret - strlen(command_template) +
- strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
- if (size > (size_t)ret) {
- cp = static_cast<char*>(realloc(buf, size));
- if (cp) {
- buf = cp;
- } else {
- size = ret;
- }
- } else {
- size = ret;
- }
-#ifdef __ANDROID__
- // Ask event log tag service for an allocation
- if (SendLogdControlMessage(buf, size) >= 0) {
- buf[size - 1] = '\0';
- unsigned long val = strtoul(buf, &cp, 10); // return size
- if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
- val = strtoul(cp + 1, &cp, 10); // allocated tag number
- if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
- free(buf);
- ret = val;
- // cache
- map->emplaceUnique(ret, TagFmt(std::make_pair(
- MapString(std::string(tagname, len)),
- MapString(std::string(format, fmtLen)))));
- return ret;
- }
- }
- }
-#endif
- free(buf);
- }
-
- // Hail Mary
- ret = map->find(MapString(tagname, len));
- if (ret == -1) errno = ESRCH;
- return ret;
-}
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 4d0ebf9..de49fbf 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -53,12 +53,6 @@
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
unsigned int tag);
-/*
- * Look up tagname, generate one if necessary, and return a tag
- */
-int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
- const char* format, int prio);
-
#ifdef __cplusplus
}
#endif
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 8beb679..f8d5ef0 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -89,6 +89,5 @@
android_log_processLogBuffer;
android_log_read_next;
android_log_write_list_buffer;
- android_lookupEventTagNum;
create_android_log_parser;
};
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index a230749..f5d19ca 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -59,8 +59,7 @@
return;
}
- int new_socket =
- TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (new_socket <= 0) {
return;
}
@@ -91,8 +90,6 @@
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
- static atomic_int dropped;
- static atomic_int droppedSecurity;
GetSocket();
@@ -110,6 +107,7 @@
return 0;
}
+ header.id = logId;
header.tid = gettid();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
@@ -117,44 +115,6 @@
newVec[0].iov_base = (unsigned char*)&header;
newVec[0].iov_len = sizeof(header);
- int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
- if (snapshot) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_SECURITY;
- buffer.header.tag = LIBLOG_LOG_TAG;
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = snapshot;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
- }
- }
- snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
- ANDROID_LOG_VERBOSE)) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_EVENTS;
- buffer.header.tag = LIBLOG_LOG_TAG;
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = snapshot;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
- }
- }
-
- header.id = logId;
-
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
newVec[i].iov_base = vec[i - headerLength].iov_base;
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
@@ -168,11 +128,8 @@
}
}
- // The write below could be lost, but will never block.
- // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
- // the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
- if (ret < 0 && errno != EAGAIN) {
+ if (ret < 0) {
LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
@@ -182,14 +139,5 @@
ret = -errno;
}
- if (ret > (ssize_t)sizeof(header)) {
- ret -= sizeof(header);
- } else if (ret < 0) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
- if (logId == LOG_ID_SECURITY) {
- atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
- }
- }
-
return ret;
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3bd5cf2..d2f12d6 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -879,30 +879,6 @@
}
BENCHMARK(BM_lookupEventFormat);
-/*
- * Measure the time it takes for android_lookupEventTagNum plus above
- */
-static void BM_lookupEventTagNum(benchmark::State& state) {
- prechargeEventMap();
-
- std::unordered_set<uint32_t>::const_iterator it = set.begin();
-
- while (state.KeepRunning()) {
- size_t len;
- const char* name = android_lookupEventTag_len(map, &len, (*it));
- std::string Name(name, len);
- const char* format = android_lookupEventFormat_len(map, &len, (*it));
- std::string Format(format, len);
- state.ResumeTiming();
- android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
- ANDROID_LOG_UNKNOWN);
- state.PauseTiming();
- ++it;
- if (it == set.end()) it = set.begin();
- }
-}
-BENCHMARK(BM_lookupEventTagNum);
-
// Must be functionally identical to liblog internal SendLogdControlMessage()
static void send_to_control(char* buf, size_t len) {
int sock =
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index fbc3d7a..c49d87b 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -2768,20 +2768,3 @@
#endif
}
#endif // ENABLE_FLAKY_TESTS
-
-TEST(liblog, android_lookupEventTagNum) {
-#ifdef __ANDROID__
- EventTagMap* map = android_openEventTagMap(NULL);
- EXPECT_TRUE(NULL != map);
- std::string Name = android::base::StringPrintf("a%d", getpid());
- int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)",
- ANDROID_LOG_UNKNOWN);
- android_closeEventTagMap(map);
- if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
- EXPECT_NE(-1, tag);
- EXPECT_NE(0, tag);
- EXPECT_GT(UINT32_MAX, (unsigned)tag);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 0341902..4518487 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -39,19 +39,21 @@
"Mode": "0755",
"UID": "system",
"GID": "system"
- },
- {
- "Controller": "freezer",
- "Path": "/dev/freezer",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
}
],
"Cgroups2": {
- "Path": "/dev/cg2_bpf",
- "Mode": "0600",
- "UID": "root",
- "GID": "root"
+ "Path": "/sys/fs/cgroup",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system",
+ "Controllers": [
+ {
+ "Controller": "freezer",
+ "Path": "freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ]
}
}
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index f4070c5..13adcae 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,19 +24,24 @@
Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
}
-// Next: 6
+// Next: 7
message Cgroup {
string controller = 1 [json_name = "Controller"];
string path = 2 [json_name = "Path"];
string mode = 3 [json_name = "Mode"];
string uid = 4 [json_name = "UID"];
string gid = 5 [json_name = "GID"];
+// Booleans default to false when not specified. File reconstruction fails
+// when a boolean is specified as false, so leave unspecified in that case
+// https://developers.google.com/protocol-buffers/docs/proto3#default
+ bool needs_activation = 6 [json_name = "NeedsActivation"];
}
-// Next: 5
+// Next: 6
message Cgroups2 {
string path = 1 [json_name = "Path"];
string mode = 2 [json_name = "Mode"];
string uid = 3 [json_name = "UID"];
string gid = 4 [json_name = "GID"];
+ repeated Cgroup controllers = 5 [json_name = "Controllers"];
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index a515e58..c4dbf8e 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -49,6 +49,11 @@
"Name": "UClampMax",
"Controller": "cpu",
"File": "cpu.uclamp.max"
+ },
+ {
+ "Name": "FreezerState",
+ "Controller": "freezer",
+ "File": "cgroup.freeze"
}
],
@@ -74,7 +79,7 @@
"Params":
{
"Controller": "freezer",
- "Path": "frozen"
+ "Path": ""
}
}
]
@@ -87,7 +92,7 @@
"Params":
{
"Controller": "freezer",
- "Path": ""
+ "Path": "../"
}
}
]
@@ -531,6 +536,32 @@
}
}
]
+ },
+ {
+ "Name": "FreezerDisabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerEnabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "1"
+ }
+ }
+ ]
}
],
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 9e2a3cd..bf86e6e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -465,13 +465,9 @@
eval_info->return_address_undefined = true;
}
break;
- case DWARF_LOCATION_PSEUDO_REGISTER: {
- if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
- last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
- return false;
- }
- break;
- }
+ case DWARF_LOCATION_PSEUDO_REGISTER:
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
default:
break;
}
@@ -543,11 +539,15 @@
// Skip this unknown register.
continue;
}
- }
-
- reg_ptr = eval_info.regs_info.Save(reg);
- if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
- return false;
+ if (!eval_info.regs_info.regs->SetPseudoRegister(reg, entry.second.values[0])) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ } else {
+ reg_ptr = eval_info.regs_info.Save(reg);
+ if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
+ return false;
+ }
}
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2d867cd..b904632 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -75,7 +75,7 @@
frame->rel_pc = dex_pc - info->start;
} else {
frame->rel_pc = dex_pc;
- return;
+ warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
}
if (!resolve_names_) {
@@ -142,6 +142,7 @@
void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
frames_.clear();
+ warnings_ = WARNING_NONE;
last_error_.code = ERROR_NONE;
last_error_.address = 0;
elf_from_memory_not_file_ = false;
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 72ec454..66fefe7 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -21,6 +21,13 @@
namespace unwindstack {
+// A bit map of warnings, multiple warnings can be set at the same time.
+enum WarningCode : uint64_t {
+ WARNING_NONE = 0,
+ WARNING_DEX_PC_NOT_IN_MAP = 0x1, // A dex pc was found, but it doesn't exist
+ // in any valid map.
+};
+
enum ErrorCode : uint8_t {
ERROR_NONE, // No error.
ERROR_MEMORY_INVALID, // Memory read failed.
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 4d49f23..a974b63 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -113,6 +113,7 @@
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
+ uint64_t warnings() { return warnings_; }
// Builds a frame for symbolization using the maps from this unwinder. The
// constructed frame contains just enough information to be used to symbolize
@@ -141,6 +142,7 @@
// file. This is only true if there is an actual file backing up the elf.
bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
+ uint64_t warnings_;
};
class UnwinderFromPid : public Unwinder {
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index d57cd33..a08a8d0 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -492,6 +492,40 @@
EXPECT_EQ(0x80000000U, regs.pc());
}
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pseudo_register_invalid) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ regs.set_pseudo_reg(11);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+
+ loc_regs.clear();
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[12] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pseudo_register) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ regs.set_pseudo_reg(11);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[11] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ uint64_t pseudo_value = 0;
+ ASSERT_TRUE(regs.GetPseudoRegister(11, &pseudo_value));
+ EXPECT_EQ(20U, pseudo_value);
+}
+
TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
DwarfCie cie{};
cie.cfa_instructions_offset = 0x3000;
@@ -581,6 +615,7 @@
Eval_invalid_register, Eval_different_reg_locations,
Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ Eval_pseudo_register_invalid, Eval_pseudo_register,
GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index 75fc9d0..f67d7dc 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -83,15 +83,33 @@
uint64_t sp() override { return fake_sp_; }
void set_pc(uint64_t pc) override { fake_pc_ = pc; }
void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+ void set_pseudo_reg(uint64_t reg) { fake_pseudo_reg_ = reg; }
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+ bool SetPseudoRegister(uint16_t reg, uint64_t value) override {
+ if (fake_pseudo_reg_ != reg) {
+ return false;
+ }
+ fake_pseudo_reg_value_ = value;
+ return true;
+ }
+ bool GetPseudoRegister(uint16_t reg, uint64_t* value) override {
+ if (fake_pseudo_reg_ != reg) {
+ return false;
+ }
+ *value = fake_pseudo_reg_value_;
+ return true;
+ }
+
Regs* Clone() override { return nullptr; }
private:
uint64_t fake_pc_ = 0;
uint64_t fake_sp_ = 0;
+ uint16_t fake_pseudo_reg_ = 0;
+ uint64_t fake_pseudo_reg_value_ = 0;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index dd33aa9..d10af2f 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -168,6 +168,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -233,6 +234,7 @@
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -293,6 +295,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -323,6 +326,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -353,6 +357,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -384,6 +389,7 @@
unwinder.SetEmbeddedSoname(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -421,6 +427,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -454,6 +461,7 @@
Unwinder unwinder(20, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(20U, unwinder.NumFrames());
@@ -497,6 +505,7 @@
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -559,6 +568,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -607,6 +617,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -627,6 +638,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -642,6 +654,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -679,6 +692,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -745,6 +759,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -795,6 +810,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -843,6 +859,7 @@
std::vector<std::string> skip_names{"libanother.so"};
unwinder.Unwind(&skip_names);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(0U, unwinder.NumFrames());
@@ -866,6 +883,7 @@
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -925,6 +943,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -984,6 +1003,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1028,6 +1048,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1072,6 +1093,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1119,6 +1141,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -1178,6 +1201,7 @@
Unwinder unwinder(1, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1208,6 +1232,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_TRUE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1238,6 +1263,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1268,6 +1294,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1298,6 +1325,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 147db54..55eadb0 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -302,7 +302,8 @@
}
#if defined(__ANDROID__)
-int androidSetThreadPriority(pid_t tid, int pri, bool change_policy) {
+namespace {
+int androidSetThreadPriorityInternal(pid_t tid, int pri, bool change_policy) {
int rc = 0;
int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
@@ -334,6 +335,15 @@
return rc;
}
+} // namespace
+
+int androidSetThreadPriority(pid_t tid, int pri) {
+ return androidSetThreadPriorityInternal(tid, pri, true);
+}
+
+int androidSetThreadPriorityAndPolicy(pid_t tid, int pri, bool change_policy) {
+ return androidSetThreadPriorityInternal(tid, pri, change_policy);
+}
int androidGetThreadPriority(pid_t tid) {
return getpriority(PRIO_PROCESS, tid);
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index 3c30a2a..cdb5442 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -78,9 +78,12 @@
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
// in either case errno is set. Thread ID zero means current thread.
+// This is equivalent to androidSetThreadPriorityAndPolicy(tid, prio, true);
+extern int androidSetThreadPriority(pid_t tid, int prio);
+
// Parameter "change_policy" indicates if sched policy should be changed. It needs
// not be checked again if the change is done elsewhere like activity manager.
-extern int androidSetThreadPriority(pid_t tid, int prio, bool change_policy = true);
+extern int androidSetThreadPriorityAndPolicy(pid_t tid, int prio, bool change_policy);
// Get the current priority of a particular thread. Returns one of the
// ANDROID_PRIORITY constants or a negative result in case of error.
diff --git a/libutils/include/utils/Debug.h b/libutils/include/utils/Debug.h
index 96bd70e..c3b9026 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -14,27 +14,9 @@
* limitations under the License.
*/
-#ifndef ANDROID_UTILS_DEBUG_H
-#define ANDROID_UTILS_DEBUG_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
+// Note: new code should use static_assert directly.
-namespace android {
-// ---------------------------------------------------------------------------
-
-#ifdef __cplusplus
-template<bool> struct CompileTimeAssert;
-template<> struct CompileTimeAssert<true> {};
-#define COMPILE_TIME_ASSERT(_exp) \
- template class CompileTimeAssert< (_exp) >;
-#endif
-
-// DO NOT USE: Please use static_assert instead
-#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
- CompileTimeAssert<( _exp )>();
-
-// ---------------------------------------------------------------------------
-} // namespace android
-
-#endif // ANDROID_UTILS_DEBUG_H
+#define COMPILE_TIME_ASSERT static_assert
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE static_assert
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 17c5e10..8aa381a 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_UTILS_FLATTENABLE_H
-#define ANDROID_UTILS_FLATTENABLE_H
+#pragma once
// DO NOT USE: please use parcelable instead
// This code is deprecated and will not be supported via AIDL code gen. For data
@@ -25,7 +24,6 @@
#include <string.h>
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/Debug.h>
#include <type_traits>
@@ -217,5 +215,3 @@
};
} // namespace android
-
-#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 61aa938..0860000 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -1654,48 +1654,6 @@
EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, ""));
}
-
- {
- // Invent new entries because existing can not serve
- EventTagMap* map = android_openEventTagMap(nullptr);
- ASSERT_TRUE(nullptr != map);
- static const char name[] = logcat_executable ".descriptive-monotonic";
- int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
- ANDROID_LOG_UNKNOWN);
- android_closeEventTagMap(map);
- ASSERT_NE(-1, myTag);
-
- const struct tag sync = { (uint32_t)myTag, name };
-
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)7;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)62;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)3673;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)(86400 + 7200 + 180 + 58);
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
- }
- }
}
static bool reportedSecurity(const char* command) {
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
index ce3dc7b..b4d3a2f 100644
--- a/logd/ChattyLogBuffer.h
+++ b/logd/ChattyLogBuffer.h
@@ -25,7 +25,6 @@
#include <android-base/thread_annotations.h>
#include <android/log.h>
#include <private/android_filesystem_config.h>
-#include <sysutils/SocketClient.h>
#include "LogBuffer.h"
#include "LogBufferElement.h"
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b85a584..a4e52c4 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -22,8 +22,6 @@
#include "LogReaderList.h"
#include "LogReaderThread.h"
-#define LOGD_SNDTIMEO 32
-
class LogReader : public SocketListener {
public:
explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index 1855c0e..20624f2 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -27,7 +27,6 @@
#include <memory>
#include <log/log.h>
-#include <sysutils/SocketClient.h>
#include "LogBuffer.h"
#include "LogWriter.h"
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 1b7107f..6ab3b48 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -24,6 +24,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <unistd.h>
#include <string>
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index df78a50..c0f62d3 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,12 +20,13 @@
#include <sys/types.h>
#include <private/android_logger.h>
-#include <sysutils/SocketClient.h>
#include <utils/FastStrcmp.h>
// Hijack this header as a common include file used by most all sources
// to report some utilities defined here and there.
+#define LOGD_SNDTIMEO 32
+
namespace android {
// Furnished in main.cpp. Caller must own and free returned value
diff --git a/logd/logd_test.cpp b/logd/logd_test.cpp
index 202ab06..828f580 100644
--- a/logd/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -40,7 +40,7 @@
#include <selinux/selinux.h>
#endif
-#include "LogReader.h" // pickup LOGD_SNDTIMEO
+#include "LogUtils.h" // For LOGD_SNDTIMEO.
using android::base::unique_fd;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6ef3bdc..37f911a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -324,16 +324,6 @@
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
- # freezer cgroup entries
- mkdir /dev/freezer/frozen
- write /dev/freezer/frozen/freezer.state FROZEN
- chown system system /dev/freezer/cgroup.procs
- chown system system /dev/freezer/frozen
- chown system system /dev/freezer/frozen/freezer.state
- chown system system /dev/freezer/frozen/cgroup.procs
-
- chmod 0444 /dev/freezer/frozen/freezer.state
-
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
chmod 0664 /proc/pressure/memory
@@ -348,8 +338,6 @@
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
- chown root root /dev/cg2_bpf
- chmod 0600 /dev/cg2_bpf
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
# Create location for fs_mgr to store abbreviated output from filesystem