Merge "init_kill_services_test: log state"
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 844357c..3b8866e 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -459,6 +459,8 @@
{"reboot,sys_ldo_ok,pmic,main", 227},
{"reboot,sys_ldo_ok,pmic,sub", 228},
{"reboot,smpl_timeout,pmic,main", 229},
+ {"reboot,ota,.*", 230},
+ {"reboot,periodic,.*", 231},
};
// Converts a string value representing the reason the system booted to an
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 7794c4b..56cac88 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -196,6 +196,7 @@
"libfastbootshim",
"libsnapshot_cow",
"liblz4",
+ "libzstd",
"libsnapshot_nobinder",
"update_metadata-protos",
"liburing",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index cdcd036..f09616a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -95,6 +95,8 @@
using namespace std::string_literals;
using namespace std::placeholders;
+#define FASTBOOT_INFO_VERSION 1
+
static const char* serial = nullptr;
static bool g_long_listing = false;
@@ -1663,20 +1665,6 @@
return;
}
-static bool IsNumber(const std::string& s) {
- bool period = false;
- for (size_t i = 0; i < s.length(); i++) {
- if (!isdigit(s[i])) {
- if (!period && s[i] == '.' && i != 0 && i != s.length() - 1) {
- period = true;
- } else {
- return false;
- }
- }
- }
- return true;
-}
-
static bool IsIgnore(const std::vector<std::string>& command) {
if (command[0][0] == '#') {
return true;
@@ -1684,7 +1672,8 @@
return false;
}
-bool CheckFastbootInfoRequirements(const std::vector<std::string>& command) {
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command,
+ uint32_t host_tool_version) {
if (command.size() != 2) {
LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
<< android::base::Join(command, " ");
@@ -1696,18 +1685,20 @@
return false;
}
- if (!IsNumber(command[1])) {
- LOG(ERROR) << "version number contains non-numeric values in fastboot-info.txt -> "
+ uint32_t fastboot_info_version;
+ if (!android::base::ParseUint(command[1], &fastboot_info_version)) {
+ LOG(ERROR) << "version number contains non-numeric characters in fastboot-info.txt -> "
<< android::base::Join(command, " ");
return false;
}
LOG(VERBOSE) << "Checking 'fastboot-info.txt version'";
- if (command[1] < PLATFORM_TOOLS_VERSION) {
+ if (fastboot_info_version <= host_tool_version) {
return true;
}
+
LOG(ERROR) << "fasboot-info.txt version: " << command[1]
- << " not compatible with host tool version --> " << PLATFORM_TOOLS_VERSION;
+ << " not compatible with host tool version --> " << host_tool_version;
return false;
}
@@ -1721,7 +1712,7 @@
continue;
}
if (command.size() > 1 && command[0] == "version") {
- if (!CheckFastbootInfoRequirements(command)) {
+ if (!CheckFastbootInfoRequirements(command, FASTBOOT_INFO_VERSION)) {
return {};
}
continue;
@@ -1733,8 +1724,6 @@
}
auto task = ParseFastbootInfoLine(fp, command);
if (!task) {
- LOG(ERROR) << "Error when parsing fastboot-info.txt, falling back on Hardcoded list: "
- << text;
return {};
}
tasks.emplace_back(std::move(task));
@@ -1760,8 +1749,6 @@
}
std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp, std::ifstream& fs) {
- if (!fs || fs.eof()) return {};
-
std::string text;
std::vector<std::string> file;
// Get os_partitions that need to be resized
@@ -1792,13 +1779,17 @@
std::string path = find_item_given_name("fastboot-info.txt");
std::ifstream stream(path);
- std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp_, stream);
- if (tasks.empty()) {
+ if (!stream || stream.eof()) {
LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
"exist";
HardcodedFlash();
return;
}
+
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp_, stream);
+ if (tasks.empty()) {
+ LOG(FATAL) << "Invalid fastboot-info.txt file.";
+ }
LOG(VERBOSE) << "Flashing from fastboot-info.txt";
for (auto& task : tasks) {
task->Run();
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index d6afb9e..80b77a0 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -133,7 +133,8 @@
std::string get_current_slot();
// Code for Parsing fastboot-info.txt
-bool CheckFastbootInfoRequirements(const std::vector<std::string>& command);
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command,
+ uint32_t host_tool_version);
std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts);
std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 6fc2056..b4e139b 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -89,20 +89,27 @@
}
TEST_F(ParseTest, VersionCheckCorrect) {
- std::vector<std::string> correct_versions = {
- "version 1.0",
- "version 22.00",
- };
+ std::vector<std::string> correct_versions = {"version 1", "version 22", "version 5",
+ "version 17"};
- std::vector<std::string> bad_versions = {"version", "version .01", "version x1",
- "version 1.0.1", "version 1.", "s 1.0",
- "version 1.0 2.0"};
+ std::vector<std::string> bad_versions = {"version", "version .01", "version x1",
+ "version 1.0.1", "version 1.", "s 1.0",
+ "version 1.0 2.0", "version 100.00", "version 1 2"};
for (auto& version : correct_versions) {
- ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+ ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 26))
+ << version;
}
+
+ // returning False for failing version check
+ for (auto& version : correct_versions) {
+ ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 0))
+ << version;
+ }
+ // returning False for bad format
for (auto& version : bad_versions) {
- ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+ ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 100))
+ << version;
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 6349c20..ef436e5 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1083,7 +1083,7 @@
return 0;
}
- auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+ auto ideal_size = std::min(super_info.size, uint64_t(s.f_frsize * s.f_bfree * 0.85));
// Align up to the filesystem block size.
if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index efe03ab..b546995 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -38,11 +38,11 @@
bool DmTable::valid() const {
if (targets_.empty()) {
LOG(ERROR) << "Device-mapper table must have at least one target.";
- return "";
+ return false;
}
if (targets_[0]->start() != 0) {
LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
- return "";
+ return false;
}
return true;
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 3dd1f1a..d3bd904 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -163,6 +163,7 @@
"libbrotli",
"libz",
"liblz4",
+ "libzstd",
],
export_include_dirs: ["include"],
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 69079cb..c3ca00a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -28,9 +28,6 @@
static constexpr uint32_t kCowVersionManifest = 2;
-static constexpr size_t BLOCK_SZ = 4096;
-static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
-
// 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:
//
@@ -160,7 +157,8 @@
kCowCompressNone = 0,
kCowCompressGz = 1,
kCowCompressBrotli = 2,
- kCowCompressLz4 = 3
+ kCowCompressLz4 = 3,
+ kCowCompressZstd = 4,
};
static constexpr uint8_t kCowReadAheadNotStarted = 0;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 73429e1..95a1270 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -63,7 +63,9 @@
//
// Partial reads are not possible unless |buffer_size| is less than the
// operation block size.
- virtual ssize_t ReadData(const CowOperation& op, void* buffer, size_t buffer_size,
+ //
+ // The operation pointer must derive from ICowOpIter::Get().
+ virtual ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
size_t ignore_bytes = 0) = 0;
};
@@ -77,7 +79,7 @@
virtual bool AtEnd() = 0;
// Read the current operation.
- virtual const CowOperation& Get() = 0;
+ virtual const CowOperation* Get() = 0;
// Advance to the next item.
virtual void Next() = 0;
@@ -121,7 +123,7 @@
std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
- ssize_t ReadData(const CowOperation& op, void* buffer, size_t buffer_size,
+ ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
size_t ignore_bytes = 0) override;
CowHeader& GetHeader() override { return header_; }
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 24c91a8..f45d4ed 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -214,29 +214,6 @@
// Get partition size from update package metadata.
uint64_t GetSize(PartitionUpdate* partition_update);
-// Util class for test cases on low space scenario. These tests assumes image manager
-// uses /data as backup device.
-class LowSpaceUserdata {
- public:
- // Set the maximum free space allowed for this test. If /userdata has more space than the given
- // number, a file is allocated to consume space.
- AssertionResult Init(uint64_t max_free_space);
-
- uint64_t free_space() const;
- uint64_t available_space() const;
- uint64_t bsize() const;
-
- private:
- AssertionResult ReadUserdataStats();
-
- static constexpr const char* kUserDataDevice = "/data";
- std::unique_ptr<TemporaryFile> big_file_;
- bool initialized_ = false;
- uint64_t free_space_ = 0;
- uint64_t available_space_ = 0;
- uint64_t bsize_ = 0;
-};
-
bool IsVirtualAbEnabled();
#define SKIP_IF_NON_VIRTUAL_AB() \
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
index 7d1b7ba..edc9d65 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
@@ -46,7 +46,7 @@
};
// Helper to check read sizes.
-static inline bool ReadData(CowReader& reader, const CowOperation& op, void* buffer, size_t size) {
+static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
return reader.ReadData(op, buffer, size) == size;
}
@@ -80,7 +80,7 @@
size_t i = 0;
while (!iter->AtEnd()) {
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
@@ -126,7 +126,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -138,18 +138,18 @@
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ 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_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
// Note: the zero operation gets split into two blocks.
ASSERT_EQ(op->type, kCowZeroOp);
@@ -160,7 +160,7 @@
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -205,7 +205,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -217,19 +217,19 @@
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowXorOp);
ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
// Note: the zero operation gets split into two blocks.
ASSERT_EQ(op->type, kCowZeroOp);
@@ -240,7 +240,7 @@
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -274,7 +274,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
std::string sink(data.size(), '\0');
@@ -282,7 +282,7 @@
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
iter->Next();
@@ -329,14 +329,14 @@
int total_blocks = 0;
while (!iter->AtEnd()) {
- auto op = &iter->Get();
+ auto op = iter->Get();
if (op->type == kCowXorOp) {
total_blocks += 1;
std::string sink(xor_data.size(), '\0');
ASSERT_EQ(op->new_block, 50);
ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, xor_data);
}
@@ -345,19 +345,19 @@
if (op->new_block == 100) {
data.resize(options.block_size);
std::string sink(data.size(), '\0');
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink.size(), data.size());
ASSERT_EQ(sink, data);
}
if (op->new_block == 6000) {
data2.resize(options.block_size);
std::string sink(data2.size(), '\0');
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
}
if (op->new_block == 9000) {
std::string sink(data3.size(), '\0');
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data3);
}
}
@@ -403,25 +403,25 @@
int total_blocks = 0;
while (!iter->AtEnd()) {
- auto op = &iter->Get();
+ auto op = iter->Get();
if (op->type == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 50) {
data.resize(options.block_size);
std::string sink(data.size(), '\0');
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
}
if (op->new_block == 3000) {
data2.resize(options.block_size);
std::string sink(data2.size(), '\0');
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
}
if (op->new_block == 5000) {
std::string sink(data3.size(), '\0');
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data3);
}
}
@@ -519,7 +519,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
std::string sink(data.size(), '\0');
@@ -527,30 +527,30 @@
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
sink = {};
sink.resize(data2.size(), '\0');
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 41); // compressed!
ASSERT_EQ(op->new_block, 51);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
@@ -585,11 +585,11 @@
std::string sink(options.block_size, '\0');
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->new_block, 51);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
}
TEST_F(CowTest, GetSize) {
@@ -659,9 +659,9 @@
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
iter->Next();
@@ -669,16 +669,16 @@
sink.resize(data2.size(), '\0');
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 3);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
iter->Next();
@@ -722,14 +722,14 @@
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
@@ -780,7 +780,7 @@
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
@@ -831,9 +831,9 @@
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(0, options.block_size));
iter->Next();
@@ -841,33 +841,33 @@
sink.resize(options.block_size, '\0');
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 4);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
@@ -912,59 +912,59 @@
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(0, options.block_size));
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 4);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 6);
@@ -1011,22 +1011,22 @@
ASSERT_NE(iter, nullptr);
ASSERT_FALSE(iter->AtEnd());
- auto op = &iter->Get();
+ auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 50);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
- op = &iter->Get();
+ op = iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
@@ -1066,7 +1066,7 @@
return AssertionSuccess();
}
-AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
+AssertionResult CompareDataBlock(CowReader* reader, const CowOperation* op,
const std::string& data) {
const auto& header = reader->GetHeader();
@@ -1124,12 +1124,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
+ if (op->type == kCowReplaceOp) {
num_replace++;
- ASSERT_EQ(op.new_block, num_replace);
+ ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
+ } else if (op->type == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1185,12 +1185,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
+ if (op->type == kCowReplaceOp) {
num_replace++;
- ASSERT_EQ(op.new_block, num_replace);
+ ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
+ } else if (op->type == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1236,12 +1236,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
+ if (op->type == kCowReplaceOp) {
num_replace++;
- ASSERT_EQ(op.new_block, num_replace);
+ ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
+ } else if (op->type == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1278,7 +1278,7 @@
ASSERT_TRUE(!iter->AtEnd());
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, seq_len - i);
+ ASSERT_EQ(op->new_block, seq_len - i);
iter->Next();
}
@@ -1345,7 +1345,7 @@
ASSERT_FALSE(iter->AtEnd());
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, expected_block);
+ ASSERT_EQ(op->new_block, expected_block);
iter->Next();
expected_block--;
@@ -1395,7 +1395,7 @@
while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, *expected_new_block);
+ ASSERT_EQ(op->new_block, *expected_new_block);
iter->Next();
expected_new_block++;
@@ -1444,7 +1444,7 @@
while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
const auto& op = iter->Get();
- ASSERT_EQ(op.new_block, *expected_new_block);
+ ASSERT_EQ(op->new_block, *expected_new_block);
iter->Next();
expected_new_block++;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index d06c904..a4a0ad6 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -29,6 +29,7 @@
#include <libsnapshot/cow_writer.h>
#include <lz4.h>
#include <zlib.h>
+#include <zstd.h>
namespace android {
namespace snapshot {
@@ -40,6 +41,8 @@
return {kCowCompressBrotli};
} else if (name == "lz4") {
return {kCowCompressLz4};
+ } else if (name == "zstd") {
+ return {kCowCompressZstd};
} else if (name == "none" || name.empty()) {
return {kCowCompressNone};
} else {
@@ -112,6 +115,23 @@
}
return buffer;
}
+ case kCowCompressZstd: {
+ std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
+ const auto compressed_size =
+ ZSTD_compress(buffer.data(), buffer.size(), data, length, 0);
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "ZSTD compression failed " << compressed_size;
+ return {};
+ }
+ // Don't run compression if the compressed output is larger
+ if (compressed_size >= length) {
+ buffer.resize(length);
+ memcpy(buffer.data(), data, length);
+ } else {
+ buffer.resize(compressed_size);
+ }
+ return buffer;
+ }
default:
LOG(ERROR) << "unhandled compression type: " << compression;
break;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index 3d34413..da90cc0 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -17,12 +17,15 @@
#include "cow_decompress.h"
#include <array>
+#include <cstring>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
#include <brotli/decode.h>
#include <lz4.h>
#include <zlib.h>
+#include <zstd.h>
namespace android {
namespace snapshot {
@@ -336,9 +339,56 @@
}
};
+class ZstdDecompressor final : public IDecompressor {
+ public:
+ ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+ size_t ignore_bytes = 0) override {
+ if (buffer_size < decompressed_size - ignore_bytes) {
+ LOG(INFO) << "buffer size " << buffer_size
+ << " is not large enough to hold decompressed data. Decompressed size "
+ << decompressed_size << ", ignore_bytes " << ignore_bytes;
+ return -1;
+ }
+ if (ignore_bytes == 0) {
+ if (!Decompress(buffer, decompressed_size)) {
+ return -1;
+ }
+ return decompressed_size;
+ }
+ std::vector<unsigned char> ignore_buf(decompressed_size);
+ if (!Decompress(buffer, decompressed_size)) {
+ return -1;
+ }
+ memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);
+ return decompressed_size;
+ }
+ bool Decompress(void* output_buffer, const size_t output_size) {
+ std::string input_buffer;
+ input_buffer.resize(stream_->Size());
+ size_t bytes_read = stream_->Read(input_buffer.data(), input_buffer.size());
+ if (bytes_read != input_buffer.size()) {
+ LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
+ << " actual: " << bytes_read;
+ return false;
+ }
+ const auto bytes_decompressed = ZSTD_decompress(output_buffer, output_size,
+ input_buffer.data(), input_buffer.size());
+ if (bytes_decompressed != output_size) {
+ LOG(ERROR) << "Failed to decompress ZSTD block, expected output size: " << output_size
+ << ", actual: " << bytes_decompressed;
+ return false;
+ }
+ return true;
+ }
+};
+
std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
return std::make_unique<Lz4Decompressor>();
}
+std::unique_ptr<IDecompressor> IDecompressor::Zstd() {
+ return std::make_unique<ZstdDecompressor>();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
index 9e83f3c..52b3d76 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
@@ -47,6 +47,7 @@
static std::unique_ptr<IDecompressor> Gz();
static std::unique_ptr<IDecompressor> Brotli();
static std::unique_ptr<IDecompressor> Lz4();
+ static std::unique_ptr<IDecompressor> Zstd();
static std::unique_ptr<IDecompressor> FromString(std::string_view compressor);
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index 94c4109..2157d0f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -57,8 +57,6 @@
os << "data_length:" << op.data_length << ",\t";
os << "new_block:" << op.new_block << ",\t";
os << "source:" << op.source;
- if (op.type == kCowXorOp)
- os << " (block:" << op.source / BLOCK_SZ << " offset:" << op.source % BLOCK_SZ << ")";
os << ")";
return os;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 893d95f..6749cf1 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -30,6 +30,7 @@
#include <zlib.h>
#include "cow_decompress.h"
+#include "libsnapshot/cow_format.h"
namespace android {
namespace snapshot {
@@ -508,37 +509,37 @@
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
- std::unordered_map<uint64_t, CowOperation> overwritten_blocks;
+ std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;
while (!itr->AtEnd()) {
- CowOperation op = itr->Get();
+ const auto& op = itr->Get();
uint64_t block;
bool offset;
- if (op.type == kCowCopyOp) {
- block = op.source;
+ if (op->type == kCowCopyOp) {
+ block = op->source;
offset = false;
- } else if (op.type == kCowXorOp) {
- block = op.source / BLOCK_SZ;
- offset = (op.source % BLOCK_SZ) != 0;
+ } else if (op->type == kCowXorOp) {
+ block = op->source / header_.block_size;
+ offset = (op->source % header_.block_size) != 0;
} else {
itr->Next();
continue;
}
- CowOperation* overwrite = nullptr;
+ const CowOperation* overwrite = nullptr;
if (overwritten_blocks.count(block)) {
- overwrite = &overwritten_blocks[block];
+ overwrite = overwritten_blocks[block];
LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
<< op << "\noverwritten by previously merged op:\n"
<< *overwrite;
}
if (offset && overwritten_blocks.count(block + 1)) {
- overwrite = &overwritten_blocks[block + 1];
+ overwrite = overwritten_blocks[block + 1];
LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
<< op << "\noverwritten by previously merged op:\n"
<< *overwrite;
}
if (overwrite != nullptr) return false;
- overwritten_blocks[op.new_block] = op;
+ overwritten_blocks[op->new_block] = op;
itr->Next();
}
return true;
@@ -561,7 +562,7 @@
CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);
bool AtEnd() override;
- const CowOperation& Get() override;
+ const CowOperation* Get() override;
void Next() override;
void Prev() override;
@@ -595,9 +596,9 @@
op_iter_++;
}
-const CowOperation& CowOpIter::Get() {
+const CowOperation* CowOpIter::Get() {
CHECK(!AtEnd());
- return (*op_iter_);
+ return &(*op_iter_);
}
class CowRevMergeOpIter final : public ICowOpIter {
@@ -606,7 +607,7 @@
std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
bool AtEnd() override;
- const CowOperation& Get() override;
+ const CowOperation* Get() override;
void Next() override;
void Prev() override;
@@ -625,7 +626,7 @@
std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
bool AtEnd() override;
- const CowOperation& Get() override;
+ const CowOperation* Get() override;
void Next() override;
void Prev() override;
@@ -664,9 +665,9 @@
block_iter_++;
}
-const CowOperation& CowMergeOpIter::Get() {
+const CowOperation* CowMergeOpIter::Get() {
CHECK(!AtEnd());
- return ops_->data()[*block_iter_];
+ return &ops_->data()[*block_iter_];
}
CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
@@ -696,9 +697,9 @@
block_riter_++;
}
-const CowOperation& CowRevMergeOpIter::Get() {
+const CowOperation* CowRevMergeOpIter::Get() {
CHECK(!AtEnd());
- return ops_->data()[*block_riter_];
+ return &ops_->data()[*block_riter_];
}
std::unique_ptr<ICowOpIter> CowReader::GetOpIter(bool merge_progress) {
@@ -765,10 +766,10 @@
size_t remaining_;
};
-ssize_t CowReader::ReadData(const CowOperation& op, void* buffer, size_t buffer_size,
+ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
size_t ignore_bytes) {
std::unique_ptr<IDecompressor> decompressor;
- switch (op.compression) {
+ switch (op->compression) {
case kCowCompressNone:
break;
case kCowCompressGz:
@@ -777,29 +778,34 @@
case kCowCompressBrotli:
decompressor = IDecompressor::Brotli();
break;
+ case kCowCompressZstd:
+ if (header_.block_size != op->data_length) {
+ decompressor = IDecompressor::Zstd();
+ }
+ break;
case kCowCompressLz4:
- if (header_.block_size != op.data_length) {
+ if (header_.block_size != op->data_length) {
decompressor = IDecompressor::Lz4();
}
break;
default:
- LOG(ERROR) << "Unknown compression type: " << op.compression;
+ LOG(ERROR) << "Unknown compression type: " << op->compression;
return -1;
}
uint64_t offset;
- if (op.type == kCowXorOp) {
- offset = data_loc_->at(op.new_block);
+ if (op->type == kCowXorOp) {
+ offset = data_loc_->at(op->new_block);
} else {
- offset = op.source;
+ offset = op->source;
}
if (!decompressor) {
- CowDataStream stream(this, offset + ignore_bytes, op.data_length - ignore_bytes);
+ CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);
return stream.ReadFully(buffer, buffer_size);
}
- CowDataStream stream(this, offset, op.data_length);
+ CowDataStream stream(this, offset, op->data_length);
decompressor->set_stream(&stream);
return decompressor->Decompress(buffer, buffer_size, header_.block_size, ignore_bytes);
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
index cb20c6f..0e18979 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
@@ -407,7 +407,7 @@
auto iter = reader->GetOpIter();
while (!iter->AtEnd()) {
- AddOperation(iter->Get());
+ AddOperation(*iter->Get());
iter->Next();
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 4090162..917cec4 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -64,19 +64,19 @@
bool include_merged;
};
-static void ShowBad(CowReader& reader, const struct CowOperation& op) {
+static void ShowBad(CowReader& reader, const struct CowOperation* op) {
size_t count;
- auto buffer = std::make_unique<uint8_t[]>(op.data_length);
+ auto buffer = std::make_unique<uint8_t[]>(op->data_length);
- if (!reader.GetRawBytes(op.source, buffer.get(), op.data_length, &count)) {
+ if (!reader.GetRawBytes(op->source, buffer.get(), op->data_length, &count)) {
std::cerr << "Failed to read at all!\n";
} else {
std::cout << "The Block data is:\n";
- for (int i = 0; i < op.data_length; i++) {
+ for (int i = 0; i < op->data_length; i++) {
std::cout << std::hex << (int)buffer[i];
}
std::cout << std::dec << "\n\n";
- if (op.data_length >= sizeof(CowOperation)) {
+ if (op->data_length >= sizeof(CowOperation)) {
std::cout << "The start, as an op, would be " << *(CowOperation*)buffer.get() << "\n";
}
}
@@ -145,29 +145,29 @@
bool success = true;
uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
while (!iter->AtEnd()) {
- const CowOperation& op = iter->Get();
+ const CowOperation* op = iter->Get();
- if (!opt.silent && opt.show_ops) std::cout << op << "\n";
+ if (!opt.silent && opt.show_ops) std::cout << *op << "\n";
- if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
+ if (opt.decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) {
if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
- std::cerr << "Failed to decompress for :" << op << "\n";
+ std::cerr << "Failed to decompress for :" << *op << "\n";
success = false;
if (opt.show_bad) ShowBad(reader, op);
}
}
- if (op.type == kCowSequenceOp && opt.show_seq) {
+ if (op->type == kCowSequenceOp && opt.show_seq) {
size_t read;
std::vector<uint32_t> merge_op_blocks;
- size_t seq_len = op.data_length / sizeof(uint32_t);
+ size_t seq_len = op->data_length / sizeof(uint32_t);
merge_op_blocks.resize(seq_len);
- if (!reader.GetRawBytes(op.source, merge_op_blocks.data(), op.data_length, &read)) {
+ if (!reader.GetRawBytes(op->source, merge_op_blocks.data(), op->data_length, &read)) {
PLOG(ERROR) << "Failed to read sequence op!";
return false;
}
if (!opt.silent) {
- std::cout << "Sequence for " << op << " is :\n";
+ std::cout << "Sequence for " << *op << " is :\n";
for (size_t i = 0; i < seq_len; i++) {
std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << "\n";
@@ -175,13 +175,13 @@
}
}
- if (op.type == kCowCopyOp) {
+ if (op->type == kCowCopyOp) {
copy_ops++;
- } else if (op.type == kCowReplaceOp) {
+ } else if (op->type == kCowReplaceOp) {
replace_ops++;
- } else if (op.type == kCowZeroOp) {
+ } else if (op->type == kCowZeroOp) {
zero_ops++;
- } else if (op.type == kCowXorOp) {
+ } else if (op->type == kCowXorOp) {
xor_ops++;
}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 64637c2..2661482 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3133,6 +3133,7 @@
for (auto&& [name, status] : all_snapshot_status) {
sum += status.cow_file_size();
}
+ LOG(INFO) << "Calculated needed COW space: " << sum << " bytes";
return Return::NoSpace(sum);
}
@@ -3279,7 +3280,10 @@
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status);
- if (!ret.is_ok()) return ret;
+ if (!ret.is_ok()) {
+ LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string();
+ return ret;
+ }
auto exported_target_metadata = target_metadata->Export();
if (exported_target_metadata == nullptr) {
@@ -3496,7 +3500,10 @@
// Create the backing COW image if necessary.
if (snapshot_status.cow_file_size() > 0) {
auto ret = CreateCowImage(lock, name);
- if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+ if (!ret.is_ok()) {
+ LOG(ERROR) << "CreateCowImage failed: " << ret.string();
+ return AddRequiredSpace(ret, *all_snapshot_status);
+ }
}
LOG(INFO) << "Successfully created snapshot for " << name;
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 48efae0..4e75ba7 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -86,7 +86,7 @@
// Populate the operation map.
op_iter_ = cow_->GetOpIter();
while (!op_iter_->AtEnd()) {
- const CowOperation* op = &op_iter_->Get();
+ const CowOperation* op = op_iter_->Get();
if (IsMetadataOp(*op)) {
op_iter_->Next();
continue;
@@ -205,7 +205,7 @@
} else if (op->type == kCowZeroOp) {
memset(buffer, 0, bytes_to_read);
} else if (op->type == kCowReplaceOp) {
- if (cow_->ReadData(*op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
+ if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
errno = EIO;
return -1;
@@ -226,7 +226,7 @@
return -1;
}
- if (cow_->ReadData(*op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
+ if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
errno = EIO;
return -1;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 1fbfaf7..22731e7 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -19,6 +19,7 @@
#include <signal.h>
#include <sys/file.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
#include <chrono>
@@ -2371,20 +2372,44 @@
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
}
-TEST_F(SnapshotUpdateTest, LowSpace) {
- static constexpr auto kMaxFree = 10_MiB;
- auto userdata = std::make_unique<LowSpaceUserdata>();
- ASSERT_TRUE(userdata->Init(kMaxFree));
+// Get max file size and free space.
+std::pair<uint64_t, uint64_t> GetBigFileLimit() {
+ struct statvfs fs;
+ if (statvfs("/data", &fs) < 0) {
+ PLOG(ERROR) << "statfs failed";
+ return {0, 0};
+ }
- // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
- // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
+ auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
+ auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
+
+ LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
+
+ return {fs_limit, fs_free};
+}
+
+TEST_F(SnapshotUpdateTest, LowSpace) {
+ // To make the low space test more reliable, we force a large cow estimate.
+ // However legacy VAB ignores the COW estimate and uses InstallOperations
+ // to compute the exact size required for dm-snapshot. It's difficult to
+ // make this work reliably (we'd need to somehow fake an extremely large
+ // super partition, and we don't have that level of dependency injection).
+ //
+ // For now, just skip this test on legacy VAB.
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "Skipping test on legacy VAB";
+ }
+
+ auto fs = GetBigFileLimit();
+ ASSERT_NE(fs.first, 0);
+
constexpr uint64_t partition_size = 10_MiB;
SetSize(sys_, partition_size);
SetSize(vnd_, partition_size);
SetSize(prd_, partition_size);
- sys_->set_estimate_cow_size(partition_size);
- vnd_->set_estimate_cow_size(partition_size);
- prd_->set_estimate_cow_size(partition_size);
+ sys_->set_estimate_cow_size(fs.first);
+ vnd_->set_estimate_cow_size(fs.first);
+ prd_->set_estimate_cow_size(fs.first);
AddOperationForPartitions();
@@ -2393,8 +2418,12 @@
auto res = sm->CreateUpdateSnapshots(manifest_);
ASSERT_FALSE(res);
ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
- ASSERT_GE(res.required_size(), 14_MiB);
- ASSERT_LT(res.required_size(), 40_MiB);
+
+ // It's hard to predict exactly how much free space is needed, since /data
+ // is writable and the test is not the only process running. Divide by two
+ // as a rough lower bound, and adjust this in the future as necessary.
+ auto expected_delta = fs.first - fs.second;
+ ASSERT_GE(res.required_size(), expected_delta / 2);
}
TEST_F(SnapshotUpdateTest, AddPartition) {
@@ -2776,6 +2805,7 @@
void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB();
CleanUp();
+ SnapshotTest::TearDown();
}
void CleanUp() {
if (!image_manager_) {
@@ -2789,26 +2819,13 @@
};
TEST_F(ImageManagerTest, CreateImageNoSpace) {
- bool at_least_one_failure = false;
- for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
- auto userdata = std::make_unique<LowSpaceUserdata>();
- ASSERT_TRUE(userdata->Init(size));
+ auto fs = GetBigFileLimit();
+ ASSERT_NE(fs.first, 0);
- uint64_t to_allocate = userdata->free_space() + userdata->bsize();
-
- auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
- IImageManager::CREATE_IMAGE_DEFAULT);
- if (!res) {
- at_least_one_failure = true;
- } else {
- ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
- }
-
- CleanUp();
- }
-
- ASSERT_TRUE(at_least_one_failure)
- << "We should have failed to allocate at least one over-sized image";
+ auto res = image_manager_->CreateBackingImage(kImageName, fs.first,
+ IImageManager::CREATE_IMAGE_DEFAULT);
+ ASSERT_FALSE(res);
+ ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
}
bool Mkdir(const std::string& path) {
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 1e03683..9fe567a 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -112,6 +112,7 @@
"liblz4",
"libext4_utils",
"liburing",
+ "libzstd",
],
header_libs: [
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index b6e00ea..efa43b7 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -391,7 +391,7 @@
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
while (!cowop_rm_iter->AtEnd()) {
- const CowOperation* cow_op = &cowop_rm_iter->Get();
+ const CowOperation* cow_op = cowop_rm_iter->Get();
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
@@ -459,7 +459,7 @@
while (!cowop_rm_iter->AtEnd()) {
do {
- const CowOperation* cow_op = &cowop_rm_iter->Get();
+ const CowOperation* cow_op = cowop_rm_iter->Get();
// We have two cases specific cases:
//
@@ -509,10 +509,8 @@
// in the file.
//===========================================================
uint64_t block_source = cow_op->source;
- uint64_t block_offset = 0;
if (prev_id.has_value()) {
- if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
- (block_offset > 0 && source_blocks.count(block_source + 1))) {
+ if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
break;
}
}
@@ -520,9 +518,6 @@
pending_ordered_ops -= 1;
vec.push_back(cow_op);
dest_blocks.insert(block_source);
- if (block_offset > 0) {
- dest_blocks.insert(block_source + 1);
- }
source_blocks.insert(cow_op->new_block);
prev_id = cow_op->new_block;
cowop_rm_iter->Next();
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
index 01123f8..a32c2bf 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -173,16 +173,11 @@
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
uint64_t source_block = cow_op->source;
- uint64_t source_offset = 0;
- if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
- (source_offset > 0 && source_blocks_.count(source_block + 1))) {
+ if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
overlap_ = true;
}
dest_blocks_.insert(source_block);
- if (source_offset > 0) {
- dest_blocks_.insert(source_block + 1);
- }
source_blocks_.insert(cow_op->new_block);
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
index 31a80a3..922df34 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -100,7 +100,7 @@
SNAP_LOG(ERROR) << "No space in buffer sink";
return false;
}
- ssize_t rv = reader_->ReadData(*cow_op, buffer, BLOCK_SZ);
+ ssize_t rv = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
if (rv != BLOCK_SZ) {
SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block
<< ", return = " << rv;
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index c592257..46952c4 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -41,6 +41,9 @@
*/
static constexpr uint32_t SECTOR_SHIFT = 9;
+static constexpr size_t BLOCK_SZ = 4096;
+static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
+
typedef __u64 sector_t;
typedef sector_t chunk_t;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 9df8cf9..a519639 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -186,7 +186,7 @@
size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;
while (!cowop_iter->AtEnd()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ const CowOperation* cow_op = cowop_iter->Get();
if (cow_op->type == kCowCopyOp) {
copy_ops += 1;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
index 3bc02d4..7858216 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
@@ -81,7 +81,7 @@
SNAP_LOG(ERROR) << "ProcessReplaceOp failed to allocate buffer";
return false;
}
- if (!reader_->ReadData(*cow_op, buffer, BLOCK_SZ)) {
+ if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
return false;
}
@@ -139,7 +139,7 @@
<< actual;
return false;
}
- ssize_t size = reader_->ReadData(*cow_op, buffer, BLOCK_SZ);
+ ssize_t size = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
if (size != BLOCK_SZ) {
SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
<< ", return value: " << size;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index 4d91ff3..ce95b76 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -31,7 +31,7 @@
do {
if (!cowop_iter_->AtEnd() && num_ops) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ const CowOperation* cow_op = cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
break;
}
@@ -46,7 +46,7 @@
nr_consecutive = 1;
while (!cowop_iter_->AtEnd() && num_ops) {
- const CowOperation* op = &cowop_iter_->Get();
+ const CowOperation* op = cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*op)) {
break;
}
@@ -181,7 +181,7 @@
SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
while (!cowop_iter_->AtEnd()) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ const CowOperation* cow_op = cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -362,7 +362,7 @@
SNAP_LOG(INFO) << "MergeOrderedOps started....";
while (!cowop_iter_->AtEnd()) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ const CowOperation* cow_op = cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 4aad7a6..17f1f0e 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -492,7 +492,7 @@
<< xor_op->new_block;
return false;
}
- if (ssize_t rv = reader_->ReadData(*xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
+ if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
SNAP_LOG(ERROR)
<< " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
<< ", return value: " << rv;
@@ -592,7 +592,7 @@
SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
return false;
}
- if (ssize_t rv = reader_->ReadData(*xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
+ if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
SNAP_LOG(ERROR)
<< " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
<< ", return value: " << rv;
@@ -834,8 +834,7 @@
}
const CowOperation* ReadAhead::GetRAOpIter() {
- const CowOperation* cow_op = &cowop_iter_->Get();
- return cow_op;
+ return cowop_iter_->Get();
}
void ReadAhead::InitializeBuffer() {
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 9f1d676..a224f6b 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -214,68 +214,6 @@
return partition_update->mutable_new_partition_info()->size();
}
-AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
- auto res = ReadUserdataStats();
- if (!res) return res;
-
- // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
- big_file_ = std::make_unique<TemporaryFile>();
- if (big_file_->fd == -1) {
- return AssertionFailure() << strerror(errno);
- }
- if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
- return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
- << kUserDataDevice;
- }
- uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space,
- (uint64_t)std::numeric_limits<off_t>::max());
- off_t allocated = 0;
- while (next_consume > 0 && free_space_ > max_free_space) {
- int status = fallocate(big_file_->fd, 0, allocated, next_consume);
- if (status == -1 && errno == ENOSPC) {
- next_consume /= 2;
- continue;
- }
- if (status == -1) {
- return AssertionFailure() << strerror(errno);
- }
- allocated += next_consume;
-
- res = ReadUserdataStats();
- if (!res) return res;
- }
-
- LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
- initialized_ = true;
- return AssertionSuccess();
-}
-
-AssertionResult LowSpaceUserdata::ReadUserdataStats() {
- struct statvfs buf;
- if (statvfs(kUserDataDevice, &buf) == -1) {
- return AssertionFailure() << strerror(errno);
- }
- bsize_ = buf.f_bsize;
- free_space_ = bsize_ * buf.f_bfree;
- available_space_ = bsize_ * buf.f_bavail;
- return AssertionSuccess();
-}
-
-uint64_t LowSpaceUserdata::free_space() const {
- CHECK(initialized_);
- return free_space_;
-}
-
-uint64_t LowSpaceUserdata::available_space() const {
- CHECK(initialized_);
- return available_space_;
-}
-
-uint64_t LowSpaceUserdata::bsize() const {
- CHECK(initialized_);
- return bsize_;
-}
-
bool IsVirtualAbEnabled() {
return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index e5241b5..7987167 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -144,14 +144,22 @@
}
}
-uint32_t GateKeeperProxy::adjust_userId(uint32_t userId) {
+Status GateKeeperProxy::adjust_userId(uint32_t userId, uint32_t* hw_userId) {
static constexpr uint32_t kGsiOffset = 1000000;
- CHECK(userId < kGsiOffset);
- CHECK((aidl_hw_device != nullptr) || (hw_device != nullptr));
- if (is_running_gsi) {
- return userId + kGsiOffset;
+ if (userId >= kGsiOffset) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
- return userId;
+
+ if ((aidl_hw_device == nullptr) && (hw_device == nullptr)) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ if (is_running_gsi) {
+ *hw_userId = userId + kGsiOffset;
+ return Status::ok();
+ }
+ *hw_userId = userId;
+ return Status::ok();
}
#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
@@ -201,7 +209,12 @@
android::hardware::hidl_vec<uint8_t> newPwd;
newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
- uint32_t hw_userId = adjust_userId(userId);
+ uint32_t hw_userId = 0;
+ Status result = adjust_userId(userId, &hw_userId);
+ if (!result.isOk()) {
+ return result;
+ }
+
uint64_t secureUserId = 0;
if (aidl_hw_device) {
// AIDL gatekeeper service
@@ -300,7 +313,12 @@
}
}
- uint32_t hw_userId = adjust_userId(userId);
+ uint32_t hw_userId = 0;
+ Status result = adjust_userId(userId, &hw_userId);
+ if (!result.isOk()) {
+ return result;
+ }
+
android::hardware::hidl_vec<uint8_t> curPwdHandle;
curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
enrolledPasswordHandle.size());
@@ -410,7 +428,12 @@
}
clear_sid(userId);
- uint32_t hw_userId = adjust_userId(userId);
+ uint32_t hw_userId = 0;
+ Status result = adjust_userId(userId, &hw_userId);
+ if (!result.isOk()) {
+ return result;
+ }
+
if (aidl_hw_device) {
aidl_hw_device->deleteUser(hw_userId);
} else if (hw_device) {
diff --git a/gatekeeperd/gatekeeperd.h b/gatekeeperd/gatekeeperd.h
index 29873da..b1f08c6 100644
--- a/gatekeeperd/gatekeeperd.h
+++ b/gatekeeperd/gatekeeperd.h
@@ -47,7 +47,7 @@
// This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
// secure storage shared across a GSI image and a host image will not overlap.
- uint32_t adjust_userId(uint32_t userId);
+ Status adjust_userId(uint32_t userId, uint32_t* hw_userId);
#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
diff --git a/init/Android.bp b/init/Android.bp
index 7b52903..41c7a95 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -169,6 +169,7 @@
"libfsverity_init",
"liblmkd_utils",
"liblz4",
+ "libzstd",
"libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
@@ -370,6 +371,7 @@
"libprotobuf-cpp-lite",
"libsnapshot_cow",
"liblz4",
+ "libzstd",
"libsnapshot_init",
"update_metadata-protos",
"libprocinfo",
diff --git a/init/builtins.cpp b/init/builtins.cpp
index bc23972..585eca2 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -60,6 +60,8 @@
#include <cutils/android_reboot.h>
#include <fs_mgr.h>
#include <fscrypt/fscrypt.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
#include <libgsi/libgsi.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
@@ -506,29 +508,29 @@
if (android::base::StartsWith(source, "loop@")) {
int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
- unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
- if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
+ const char* file_path = source + strlen("loop@");
- for (size_t n = 0;; n++) {
- std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
- unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
- if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
-
- loop_info info;
- /* if it is a blank loop device */
- if (ioctl(loop.get(), LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
- /* if it becomes our loop device */
- if (ioctl(loop.get(), LOOP_SET_FD, fd.get()) >= 0) {
- if (mount(tmp.c_str(), target, system, flags, options) < 0) {
- ioctl(loop.get(), LOOP_CLR_FD, 0);
- return ErrnoError() << "mount() failed";
- }
- return {};
- }
- }
+ // Open source file
+ if (wait) {
+ wait_for_file(file_path, kCommandRetryTimeout);
}
- return Error() << "out of loopback devices";
+ unique_fd fd(TEMP_FAILURE_RETRY(open(file_path, mode | O_CLOEXEC)));
+ if (fd < 0) {
+ return ErrnoError() << "open(" << file_path << ", " << mode << ") failed";
+ }
+
+ // Allocate loop device and attach it to file_path.
+ android::dm::LoopControl loop_control;
+ std::string loop_device;
+ if (!loop_control.Attach(fd.get(), 5s, &loop_device)) {
+ return ErrnoError() << "loop_control.Attach " << file_path << " failed";
+ }
+
+ if (mount(loop_device.c_str(), target, system, flags, options) < 0) {
+ loop_control.Detach(loop_device);
+ return ErrnoError() << "mount() failed";
+ }
} else {
if (wait)
wait_for_file(source, kCommandRetryTimeout);
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 107e99a..bff80c5 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -58,6 +58,12 @@
namespace {
+enum class BootMode {
+ NORMAL_MODE,
+ RECOVERY_MODE,
+ CHARGER_MODE,
+};
+
void FreeRamdisk(DIR* dir, dev_t dev) {
int dfd = dirfd(dir);
@@ -149,13 +155,27 @@
}
} // namespace
-std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
- auto module_load_file = "modules.load";
- if (recovery) {
- struct stat fileStat;
- std::string recovery_load_path = dir_path + "/modules.load.recovery";
- if (!stat(recovery_load_path.c_str(), &fileStat)) {
+std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
+ std::string module_load_file;
+
+ switch (boot_mode) {
+ case BootMode::NORMAL_MODE:
+ module_load_file = "modules.load";
+ break;
+ case BootMode::RECOVERY_MODE:
module_load_file = "modules.load.recovery";
+ break;
+ case BootMode::CHARGER_MODE:
+ module_load_file = "modules.load.charger";
+ break;
+ }
+
+ if (module_load_file != "modules.load") {
+ struct stat fileStat;
+ std::string load_path = dir_path + "/" + module_load_file;
+ // Fall back to modules.load if the other files aren't accessible
+ if (stat(load_path.c_str(), &fileStat)) {
+ module_load_file = "modules.load";
}
}
@@ -163,7 +183,8 @@
}
#define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
+bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
+ int& modules_loaded) {
struct utsname uts;
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
@@ -203,7 +224,7 @@
for (const auto& module_dir : module_dirs) {
std::string dir_path = MODULE_BASE_DIR "/";
dir_path.append(module_dir);
- Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
+ Modprobe m({dir_path}, GetModuleLoadList(boot_mode, dir_path));
bool retval = m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
@@ -211,7 +232,7 @@
}
}
- Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
+ Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR));
bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
: m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
@@ -221,6 +242,21 @@
return true;
}
+static bool IsChargerMode(const std::string& cmdline, const std::string& bootconfig) {
+ return bootconfig.find("androidboot.mode = \"charger\"") != std::string::npos ||
+ cmdline.find("androidboot.mode=charger") != std::string::npos;
+}
+
+static BootMode GetBootMode(const std::string& cmdline, const std::string& bootconfig)
+{
+ if (IsChargerMode(cmdline, bootconfig))
+ return BootMode::CHARGER_MODE;
+ else if (IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig))
+ return BootMode::RECOVERY_MODE;
+
+ return BootMode::NORMAL_MODE;
+}
+
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -328,7 +364,8 @@
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
- if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
+ BootMode boot_mode = GetBootMode(cmdline, bootconfig);
+ if (!LoadKernelModules(boot_mode, want_console,
want_parallel, module_count)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
diff --git a/init/init.cpp b/init/init.cpp
index be1ebee..da63fdc 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -1043,6 +1043,12 @@
SetProperty(gsi::kGsiBootedProp, is_running);
auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
SetProperty(gsi::kGsiInstalledProp, is_installed);
+ if (android::gsi::IsGsiRunning()) {
+ std::string dsu_slot;
+ if (android::gsi::GetActiveDsu(&dsu_slot)) {
+ SetProperty(gsi::kDsuSlotProp, dsu_slot);
+ }
+ }
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 79d79dd..f90a1bc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -84,14 +84,12 @@
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
{ 00750, AID_ROOT, AID_SYSTEM, 0, "system/apex/com.android.tethering/bin/for-system" },
- { 00750, AID_ROOT, AID_SYSTEM, 0, "system/apex/com.android.tethering.inprocess/bin/for-system" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
{ 00750, AID_ROOT, AID_SHELL, 0, "system/xbin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin" },
{ 00750, AID_ROOT, AID_SYSTEM, 0, "system_ext/apex/com.android.tethering/bin/for-system" },
- { 00750, AID_ROOT, AID_SYSTEM, 0, "system_ext/apex/com.android.tethering.inprocess/bin/for-system" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
@@ -199,9 +197,7 @@
// the following files have enhanced capabilities and ARE included
// in user builds.
{ 06755, AID_CLAT, AID_CLAT, 0, "system/apex/com.android.tethering/bin/for-system/clatd" },
- { 06755, AID_CLAT, AID_CLAT, 0, "system/apex/com.android.tethering.inprocess/bin/for-system/clatd" },
{ 06755, AID_CLAT, AID_CLAT, 0, "system_ext/apex/com.android.tethering/bin/for-system/clatd" },
- { 06755, AID_CLAT, AID_CLAT, 0, "system_ext/apex/com.android.tethering.inprocess/bin/for-system/clatd" },
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
"system/bin/inputflinger" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 04f5d79..fe8c1f3 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -137,26 +137,36 @@
path: "binder",
}
-cc_fuzz {
- name: "storaged_service_fuzzer",
+cc_defaults {
+ name: "storaged_service_fuzzer_defaults",
defaults: [
"storaged_defaults",
"service_fuzzer_defaults",
+ "fuzzer_disable_leaks",
],
- srcs: ["tests/fuzzers/storaged_service_fuzzer.cpp"],
static_libs: [
"libstoraged",
],
+ fuzz_config: {
+ cc: [
+ "dvander@google.com",
+ ],
+ triage_assignee: "waghpawan@google.com",
+ },
+}
+
+cc_fuzz {
+ name: "storaged_service_fuzzer",
+ defaults: [
+ "storaged_service_fuzzer_defaults",
+ ],
+ srcs: ["tests/fuzzers/storaged_service_fuzzer.cpp"],
}
cc_fuzz {
name: "storaged_private_service_fuzzer",
defaults: [
- "storaged_defaults",
- "service_fuzzer_defaults",
+ "storaged_service_fuzzer_defaults",
],
srcs: ["tests/fuzzers/storaged_private_service_fuzzer.cpp"],
- static_libs: [
- "libstoraged",
- ],
-}
\ No newline at end of file
+}
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index 4780943..96804bd 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -26,7 +26,8 @@
"-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
],
fuzz_config: {
- cc: ["trong@google.com"],
+ cc: ["mikemcternan@google.com"],
+ componentid: 1290237,
},
}
@@ -40,7 +41,8 @@
"libdmabufheap",
],
fuzz_config: {
- cc: ["trong@google.com"],
+ cc: ["mikemcternan@google.com"],
+ componentid: 1290237,
},
// The initial corpus for this fuzzer was derived by dumping messages from/to
diff --git a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
index e944167..6b8f90f 100644
--- a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
+++ b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
@@ -17,8 +17,10 @@
#include <getopt.h>
#include <string>
+#include <vector>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
namespace {
@@ -34,14 +36,66 @@
{"model", required_argument, nullptr, 'm'},
{"imei", required_argument, nullptr, 'i'},
{"meid", required_argument, nullptr, 'c'},
+ {"imei2", required_argument, nullptr, '2'},
{0, 0, 0, 0},
};
+std::string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei ";
+
+// Run a shell command and collect the output of it. If any error, set an empty string as the
+// output.
+std::string exec_command(const std::string& command) {
+ char buffer[128];
+ std::string result = "";
+
+ FILE* pipe = popen(command.c_str(), "r");
+ if (!pipe) {
+ fprintf(stderr, "popen('%s') failed\n", command.c_str());
+ return result;
+ }
+
+ while (!feof(pipe)) {
+ if (fgets(buffer, 128, pipe) != NULL) {
+ result += buffer;
+ }
+ }
+
+ pclose(pipe);
+ return result;
+}
+
+// Get IMEI using Telephony service shell command. If any error while executing the command
+// then empty string will be returned as output.
+std::string get_imei(int slot) {
+ std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot);
+ std::string output = exec_command(cmd);
+
+ if (output.empty()) {
+ fprintf(stderr, "Retrieve IMEI command ('%s') failed\n", cmd.c_str());
+ return "";
+ }
+
+ std::vector<std::string> out =
+ ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:");
+
+ if (out.size() != 1) {
+ fprintf(stderr, "Error parsing command ('%s') output '%s'\n", cmd.c_str(), output.c_str());
+ return "";
+ }
+
+ std::string imei = ::android::base::Trim(out[0]);
+ if (imei.compare("null") == 0) {
+ fprintf(stderr, "IMEI value from command ('%s') is null, skipping", cmd.c_str());
+ return "";
+ }
+ return imei;
+}
+
std::string buf2string(const keymaster::Buffer& buf) {
return std::string(reinterpret_cast<const char*>(buf.peek_read()), buf.available_read());
}
-void print_usage(const char* prog, const keymaster::SetAttestationIdsRequest& req) {
+void print_usage(const char* prog, const keymaster::SetAttestationIdsKM3Request& req) {
fprintf(stderr,
"Usage: %s [options]\n"
"\n"
@@ -55,34 +109,53 @@
" -m, --model <val> set model (default '%s')\n"
" -i, --imei <val> set IMEI (default '%s')\n"
" -c, --meid <val> set MEID (default '%s')\n"
+ " -2, --imei2 <val> set second IMEI (default '%s')\n"
"\n",
- prog, buf2string(req.brand).c_str(), buf2string(req.device).c_str(),
- buf2string(req.product).c_str(), buf2string(req.serial).c_str(),
- buf2string(req.manufacturer).c_str(), buf2string(req.model).c_str(),
- buf2string(req.imei).c_str(), buf2string(req.meid).c_str());
+ prog, buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
+ buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
+ buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
+ buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
+ buf2string(req.second_imei).c_str());
+}
+
+void set_to(keymaster::Buffer* buf, const std::string& value) {
+ if (!value.empty()) {
+ buf->Reinitialize(value.data(), value.size());
+ }
}
void set_from_prop(keymaster::Buffer* buf, const std::string& prop) {
std::string prop_value = ::android::base::GetProperty(prop, /* default_value = */ "");
- if (!prop_value.empty()) {
- buf->Reinitialize(prop_value.data(), prop_value.size());
- }
+ set_to(buf, prop_value);
}
-void populate_ids(keymaster::SetAttestationIdsRequest* req) {
+void populate_base_ids(keymaster::SetAttestationIdsRequest* req) {
set_from_prop(&req->brand, "ro.product.brand");
set_from_prop(&req->device, "ro.product.device");
set_from_prop(&req->product, "ro.product.name");
set_from_prop(&req->serial, "ro.serialno");
set_from_prop(&req->manufacturer, "ro.product.manufacturer");
set_from_prop(&req->model, "ro.product.model");
+ std::string imei = get_imei(0);
+ set_to(&req->imei, imei);
+}
+
+void populate_ids(keymaster::SetAttestationIdsKM3Request* req) {
+ populate_base_ids(&req->base);
+
+ // - "What about IMEI?"
+ // - "You've already had it."
+ // - "We've had one, yes. What about second IMEI?"
+ // - "I don't think he knows about second IMEI, Pip."
+ std::string imei2 = get_imei(1);
+ set_to(&req->second_imei, imei2);
}
} // namespace
int main(int argc, char** argv) {
// By default, set attestation IDs to the values in userspace properties.
- keymaster::SetAttestationIdsRequest req(/* ver = */ 4);
+ keymaster::SetAttestationIdsKM3Request req(/* ver = */ 4);
populate_ids(&req);
while (true) {
@@ -94,28 +167,31 @@
switch (c) {
case 'b':
- req.brand.Reinitialize(optarg, strlen(optarg));
+ req.base.brand.Reinitialize(optarg, strlen(optarg));
break;
case 'd':
- req.device.Reinitialize(optarg, strlen(optarg));
+ req.base.device.Reinitialize(optarg, strlen(optarg));
break;
case 'p':
- req.product.Reinitialize(optarg, strlen(optarg));
+ req.base.product.Reinitialize(optarg, strlen(optarg));
break;
case 's':
- req.serial.Reinitialize(optarg, strlen(optarg));
+ req.base.serial.Reinitialize(optarg, strlen(optarg));
break;
case 'M':
- req.manufacturer.Reinitialize(optarg, strlen(optarg));
+ req.base.manufacturer.Reinitialize(optarg, strlen(optarg));
break;
case 'm':
- req.model.Reinitialize(optarg, strlen(optarg));
+ req.base.model.Reinitialize(optarg, strlen(optarg));
break;
case 'i':
- req.imei.Reinitialize(optarg, strlen(optarg));
+ req.base.imei.Reinitialize(optarg, strlen(optarg));
break;
case 'c':
- req.meid.Reinitialize(optarg, strlen(optarg));
+ req.base.meid.Reinitialize(optarg, strlen(optarg));
+ break;
+ case '2':
+ req.second_imei.Reinitialize(optarg, strlen(optarg));
break;
case 'h':
print_usage(argv[0], req);
@@ -144,19 +220,33 @@
" manufacturer: %s\n"
" model: %s\n"
" IMEI: %s\n"
- " MEID: %s\n",
- buf2string(req.brand).c_str(), buf2string(req.device).c_str(),
- buf2string(req.product).c_str(), buf2string(req.serial).c_str(),
- buf2string(req.manufacturer).c_str(), buf2string(req.model).c_str(),
- buf2string(req.imei).c_str(), buf2string(req.meid).c_str());
+ " MEID: %s\n"
+ " SECOND_IMEI: %s\n\n",
+ buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
+ buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
+ buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
+ buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
+ buf2string(req.second_imei).c_str());
+ fflush(stdout);
keymaster::EmptyKeymasterResponse rsp(/* ver = */ 4);
- ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
- if (ret) {
- fprintf(stderr, "SET_ATTESTATION_IDS failed: %d\n", ret);
- trusty_keymaster_disconnect();
- return EXIT_FAILURE;
+ const char* msg;
+ if (req.second_imei.available_read() == 0) {
+ // No SECOND_IMEI set, use base command.
+ ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req.base, &rsp);
+ msg = "SET_ATTESTATION_IDS";
+ } else {
+ // SECOND_IMEI is set, use updated command.
+ ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS_KM3, req, &rsp);
+ msg = "SET_ATTESTATION_IDS_KM3";
}
+ trusty_keymaster_disconnect();
- return EXIT_SUCCESS;
+ if (ret) {
+ fprintf(stderr, "%s failed: %d\n", msg, ret);
+ return EXIT_FAILURE;
+ } else {
+ printf("done\n");
+ return EXIT_SUCCESS;
+ }
}