Merge "init: Introduce the function ReapAndRemove()" into main
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index ae7ed4c..cdbac00 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -34,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
@@ -58,6 +59,9 @@
constexpr char kCacheMountPoint[] = "/cache";
constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+// Mount tree to temporarily hold references to submounts.
+constexpr char kMoveMountTempDir[] = "/dev/remount";
+
constexpr char kLowerdirOption[] = "lowerdir=";
constexpr char kUpperdirOption[] = "upperdir=";
constexpr char kWorkdirOption[] = "workdir=";
@@ -284,10 +288,6 @@
if (ret) {
PERROR << "__mount(target=" << mount_point
<< ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
- // If "/system" doesn't look like a mountpoint, retry with "/".
- if (errno == EINVAL && mount_point == "/system") {
- return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
- }
return false;
}
return true;
@@ -374,24 +374,23 @@
return info;
}
-static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
- const auto mount_point = fs_mgr_mount_point(entry.mount_point);
- const auto options = fs_mgr_get_overlayfs_options(entry);
+static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
+ const auto mount_point = fs_mgr_mount_point(fstab_entry.mount_point);
+ const auto options = fs_mgr_get_overlayfs_options(fstab_entry);
if (options.empty()) return false;
- auto retval = true;
-
struct MoveEntry {
std::string mount_point;
std::string dir;
bool shared_flag;
};
-
std::vector<MoveEntry> moved_mounts;
- auto parent_private = false;
- auto parent_made_private = false;
- auto dev_private = false;
- auto dev_made_private = false;
+
+ bool retval = true;
+ bool move_dir_shared = true;
+ bool parent_shared = true;
+ bool root_shared = true;
+ bool root_made_private = false;
// There could be multiple mount entries with the same mountpoint.
// Group these entries together with stable_sort, and keep only the last entry of a group.
@@ -411,18 +410,32 @@
// mountinfo is reversed twice, so still is in lexical sorted order.
for (const auto& entry : mountinfo) {
- if ((entry.mount_point == mount_point) && !entry.shared_flag) {
- parent_private = true;
+ if (entry.mount_point == kMoveMountTempDir) {
+ move_dir_shared = entry.shared_flag;
}
- if ((entry.mount_point == "/dev") && !entry.shared_flag) {
- dev_private = true;
+ if (entry.mount_point == mount_point ||
+ (mount_point == "/system" && entry.mount_point == "/")) {
+ parent_shared = entry.shared_flag;
}
+ if (entry.mount_point == "/") {
+ root_shared = entry.shared_flag;
+ }
+ }
+
+ // Precondition is that kMoveMountTempDir is MS_PRIVATE, otherwise don't try to move any
+ // submount in to or out of it.
+ if (move_dir_shared) {
+ mountinfo.clear();
}
// Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
// This could happen if its parent mount is remounted later.
- if (!parent_private) {
- parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ if (!fs_mgr_overlayfs_set_shared_mount(mount_point, false)) {
+ // If failed to set "/system" mount type, it might be due to "/system" not being a valid
+ // mountpoint after switch root. Retry with "/" in this case.
+ if (errno == EINVAL && mount_point == "/system") {
+ root_made_private = fs_mgr_overlayfs_set_shared_mount("/", false);
+ }
}
for (const auto& entry : mountinfo) {
@@ -440,8 +453,8 @@
// mountinfo is in lexical order, so no need to worry about |entry| being a parent mount of
// entries of |moved_mounts|.
- // use as the bound directory in /dev.
- MoveEntry new_entry{entry.mount_point, "/dev/TemporaryDir-XXXXXX", entry.shared_flag};
+ MoveEntry new_entry{entry.mount_point, kMoveMountTempDir + "/TemporaryDir-XXXXXX"s,
+ entry.shared_flag};
{
AutoSetFsCreateCon createcon;
auto new_context = fs_mgr_get_context(entry.mount_point);
@@ -497,10 +510,6 @@
// Move submounts back.
for (const auto& entry : moved_mounts) {
- if (!dev_private && !dev_made_private) {
- dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
- }
-
if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
retval = false;
} else if (entry.shared_flag &&
@@ -509,12 +518,13 @@
}
rmdir(entry.dir.c_str());
}
- if (dev_made_private) {
- fs_mgr_overlayfs_set_shared_mount("/dev", true);
- }
- if (parent_made_private) {
+ // If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED.
+ if (parent_shared) {
fs_mgr_overlayfs_set_shared_mount(mount_point, true);
}
+ if (root_shared && root_made_private) {
+ fs_mgr_overlayfs_set_shared_mount("/", true);
+ }
return retval;
}
@@ -730,6 +740,25 @@
if (!OverlayfsSetupAllowed()) {
return false;
}
+
+ // Ensure kMoveMountTempDir is standalone mount tree with 'private' propagation by bind mounting
+ // to itself and set to MS_PRIVATE.
+ // Otherwise mounts moved in to it would have their propagation type changed unintentionally.
+ // Section 5d, https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
+ if (!fs_mgr_overlayfs_already_mounted(kMoveMountTempDir, false)) {
+ if (mkdir(kMoveMountTempDir, 0755) && errno != EEXIST) {
+ PERROR << "mkdir " << kMoveMountTempDir;
+ }
+ if (mount(kMoveMountTempDir, kMoveMountTempDir, nullptr, MS_BIND, nullptr)) {
+ PERROR << "bind mount " << kMoveMountTempDir;
+ }
+ }
+ fs_mgr_overlayfs_set_shared_mount(kMoveMountTempDir, false);
+ android::base::ScopeGuard umountDir([]() {
+ umount(kMoveMountTempDir);
+ rmdir(kMoveMountTempDir);
+ });
+
auto ret = true;
auto scratch_can_be_mounted = !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
@@ -742,7 +771,7 @@
scratch_can_be_mounted = false;
TryMountScratch();
}
- ret &= fs_mgr_overlayfs_mount(entry);
+ ret &= fs_mgr_overlayfs_mount_one(entry);
}
return ret;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 75467cb..5e5546d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -105,11 +105,11 @@
static constexpr uint8_t kNumResumePoints = 4;
struct CowHeaderV3 : public CowHeader {
- // Location of sequence buffer in COW.
- uint64_t sequence_buffer_offset;
- // number of currently written resume points
+ // Number of sequence data stored (each of which is a 32 byte integer)
+ uint64_t sequence_data_count;
+ // Number of currently written resume points &&
uint32_t resume_point_count;
- // Size, in bytes, of the CowResumePoint buffer.
+ // Number of max resume points that can be written
uint32_t resume_point_max;
// Number of CowOperationV3 structs in the operation buffer, currently and total
// region size.
@@ -232,19 +232,23 @@
return op.source_info & kCowOpSourceInfoDataMask;
}
-static constexpr off_t GetOpOffset(uint32_t op_index, const CowHeaderV3 header) {
- return header.prefix.header_size + header.buffer_size +
- (header.resume_point_max * sizeof(ResumePoint)) + (op_index * sizeof(CowOperationV3));
-}
-static constexpr off_t GetDataOffset(const CowHeaderV3 header) {
- return header.prefix.header_size + header.buffer_size +
- (header.resume_point_max * sizeof(ResumePoint)) +
- header.op_count_max * sizeof(CowOperation);
-}
-static constexpr off_t GetResumeOffset(const CowHeaderV3 header) {
+static constexpr off_t GetSequenceOffset(const CowHeaderV3& header) {
return header.prefix.header_size + header.buffer_size;
}
+static constexpr off_t GetResumeOffset(const CowHeaderV3& header) {
+ return GetSequenceOffset(header) + (header.sequence_data_count * sizeof(uint32_t));
+}
+
+static constexpr off_t GetOpOffset(uint32_t op_index, const CowHeaderV3& header) {
+ return GetResumeOffset(header) + (header.resume_point_max * sizeof(ResumePoint)) +
+ (op_index * sizeof(CowOperationV3));
+}
+
+static constexpr off_t GetDataOffset(const CowHeaderV3& header) {
+ return GetOpOffset(header.op_count_max, header);
+}
+
struct CowFooter {
CowFooterOperation op;
uint8_t unused[64];
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index c87b32d..bf4c79f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -16,8 +16,6 @@
#include <stdint.h>
-#include <deque>
-#include <functional>
#include <memory>
#include <optional>
#include <unordered_map>
@@ -169,6 +167,12 @@
private:
bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);
bool PrepMergeOps();
+ // sequence data is stored as an operation with actual data residing in the data offset.
+ bool GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map);
+ // v3 of the cow writes sequence data within its own separate sequence buffer.
+ bool GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map);
uint64_t FindNumCopyops();
uint8_t GetCompressionType();
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 8412879..7b5370c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -24,6 +24,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <zlib.h>
@@ -265,52 +266,31 @@
// Replace-op-4, Zero-op-9, Replace-op-5 }
//==============================================================
bool CowReader::PrepMergeOps() {
- auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>();
std::vector<int> other_ops;
- auto seq_ops_set = std::unordered_set<uint32_t>();
- auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
- size_t num_seqs = 0;
- size_t read;
+ std::vector<uint32_t> merge_op_blocks;
+ std::unordered_map<uint32_t, int> block_map;
- for (size_t i = 0; i < ops_->size(); i++) {
- auto& current_op = ops_->data()[i];
-
- if (current_op.type == kCowSequenceOp) {
- size_t seq_len = current_op.data_length / sizeof(uint32_t);
-
- merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
- if (!GetRawBytes(¤t_op, &merge_op_blocks->data()[num_seqs],
- current_op.data_length, &read)) {
- PLOG(ERROR) << "Failed to read sequence op!";
- return false;
- }
- for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
- seq_ops_set.insert(merge_op_blocks->data()[j]);
- }
- num_seqs += seq_len;
- }
-
- if (IsMetadataOp(current_op)) {
- continue;
- }
-
- // Sequence ops must be the first ops in the stream.
- if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
- merge_op_blocks->emplace_back(current_op.new_block);
- } else if (seq_ops_set.count(current_op.new_block) == 0) {
- other_ops.push_back(current_op.new_block);
- }
- block_map->insert({current_op.new_block, i});
+ switch (header_.prefix.major_version) {
+ case 1:
+ case 2:
+ GetSequenceDataV2(&merge_op_blocks, &other_ops, &block_map);
+ break;
+ case 3:
+ GetSequenceData(&merge_op_blocks, &other_ops, &block_map);
+ break;
+ default:
+ break;
}
- for (auto block : *merge_op_blocks) {
- if (block_map->count(block) == 0) {
+
+ for (auto block : merge_op_blocks) {
+ if (block_map.count(block) == 0) {
LOG(ERROR) << "Invalid Sequence Ops. Could not find Cow Op for new block " << block;
return false;
}
}
- if (merge_op_blocks->size() > header_.num_merge_ops) {
- num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
+ if (merge_op_blocks.size() > header_.num_merge_ops) {
+ num_ordered_ops_to_merge_ = merge_op_blocks.size() - header_.num_merge_ops;
} else {
num_ordered_ops_to_merge_ = 0;
}
@@ -326,9 +306,9 @@
std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());
}
- merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
+ merge_op_blocks.insert(merge_op_blocks.end(), other_ops.begin(), other_ops.end());
- num_total_data_ops_ = merge_op_blocks->size();
+ num_total_data_ops_ = merge_op_blocks.size();
if (header_.num_merge_ops > 0) {
merge_op_start_ = header_.num_merge_ops;
}
@@ -338,24 +318,94 @@
// the ops vector as required for merge operations.
auto merge_ops_buffer = std::make_shared<std::vector<CowOperation>>();
merge_ops_buffer->reserve(num_total_data_ops_);
- for (auto block : *merge_op_blocks) {
- merge_ops_buffer->emplace_back(ops_->data()[block_map->at(block)]);
+ for (auto block : merge_op_blocks) {
+ merge_ops_buffer->emplace_back(ops_->data()[block_map.at(block)]);
}
ops_->clear();
ops_ = merge_ops_buffer;
ops_->shrink_to_fit();
} else {
- for (auto block : *merge_op_blocks) {
- block_pos_index_->push_back(block_map->at(block));
+ for (auto block : merge_op_blocks) {
+ block_pos_index_->push_back(block_map.at(block));
}
}
- block_map->clear();
- merge_op_blocks->clear();
+ block_map.clear();
+ merge_op_blocks.clear();
return true;
}
+bool CowReader::GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks,
+ std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map) {
+ auto seq_ops_set = std::unordered_set<uint32_t>();
+ size_t num_seqs = 0;
+ size_t read;
+ for (size_t i = 0; i < ops_->size(); i++) {
+ auto& current_op = ops_->data()[i];
+
+ if (current_op.type == kCowSequenceOp) {
+ size_t seq_len = current_op.data_length / sizeof(uint32_t);
+
+ merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
+ if (!GetRawBytes(¤t_op, &merge_op_blocks->data()[num_seqs],
+ current_op.data_length, &read)) {
+ PLOG(ERROR) << "Failed to read sequence op!";
+ return false;
+ }
+ for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
+ seq_ops_set.insert(merge_op_blocks->at(j));
+ }
+ num_seqs += seq_len;
+ }
+
+ if (IsMetadataOp(current_op)) {
+ continue;
+ }
+
+ // Sequence ops must be the first ops in the stream.
+ if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
+ merge_op_blocks->emplace_back(current_op.new_block);
+ } else if (seq_ops_set.count(current_op.new_block) == 0) {
+ other_ops->push_back(current_op.new_block);
+ }
+ block_map->insert({current_op.new_block, i});
+ }
+ return false;
+}
+
+bool CowReader::GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map) {
+ std::unordered_set<uint32_t> seq_ops_set;
+ // read sequence ops data
+ merge_op_blocks->resize(header_.sequence_data_count);
+ if (!android::base::ReadFullyAtOffset(
+ fd_, merge_op_blocks->data(),
+ header_.sequence_data_count * sizeof(merge_op_blocks->at(0)),
+ GetSequenceOffset(header_))) {
+ PLOG(ERROR) << "failed to read sequence buffer. seq_data_count: "
+ << header_.sequence_data_count << " at offset: " << GetSequenceOffset(header_);
+ return false;
+ }
+ seq_ops_set.reserve(merge_op_blocks->size());
+ for (auto& i : *merge_op_blocks) {
+ seq_ops_set.insert(i);
+ }
+ // read ordered op data
+ for (size_t i = 0; i < ops_->size(); i++) {
+ auto& current_op = ops_->data()[i];
+ // Sequence ops must be the first ops in the stream.
+ if (seq_ops_set.empty()) {
+ merge_op_blocks->emplace_back(current_op.new_block);
+ } else if (seq_ops_set.count(current_op.new_block) == 0) {
+ other_ops->push_back(current_op.new_block);
+ }
+ block_map->insert({current_op.new_block, i});
+ }
+ return true;
+}
+
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index c41e07c..9ac1448 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -482,5 +482,144 @@
header = reader.header_v3();
ASSERT_EQ(header.op_count, 15);
}
+
+TEST_F(CowTestV3, BufferMetadataSyncTest) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ /*
+ Header metadafields
+ sequence_data_count = 0;
+ resume_point_count = 0;
+ resume_point_max = 4;
+ */
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.sequence_data_count, 0);
+ ASSERT_EQ(header.resume_point_count, 0);
+ ASSERT_EQ(header.resume_point_max, 4);
+
+ writer->AddLabel(0);
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+ ASSERT_EQ(header.sequence_data_count, 0);
+ ASSERT_EQ(header.resume_point_count, 1);
+ ASSERT_EQ(header.resume_point_max, 4);
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+
+ /*
+ Header metadafields
+ sequence_data_count = 1;
+ resume_point_count = 0;
+ resume_point_max = 4;
+ */
+}
+
+TEST_F(CowTestV3, SequenceTest) {
+ CowOptions options;
+ options.op_count_max = std::numeric_limits<uint32_t>::max();
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ // sequence data. This just an arbitrary set of integers that specify the merge order. The
+ // actual calculation is done by update_engine and passed to writer. All we care about here is
+ // writing that data correctly
+ const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ auto iter = reader.GetRevMergeOpIter();
+
+ for (int i = 0; i < seq_len; i++) {
+ ASSERT_TRUE(!iter->AtEnd());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op->new_block, seq_len - i);
+
+ iter->Next();
+ }
+ ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, MissingSeqOp) {
+ CowOptions options;
+ options.op_count_max = std::numeric_limits<uint32_t>::max();
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len - 1));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTestV3, ResumeSeqOp) {
+ CowOptions options;
+ options.op_count_max = std::numeric_limits<uint32_t>::max();
+ auto writer = std::make_unique<CowWriterV3>(options, GetCowFd());
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+ ASSERT_TRUE(writer->Initialize());
+
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
+ ASSERT_TRUE(writer->AddLabel(1));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+ auto reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+ auto itr = reader->GetRevMergeOpIter();
+ ASSERT_TRUE(itr->AtEnd());
+
+ writer = std::make_unique<CowWriterV3>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd));
+
+ auto iter = reader->GetRevMergeOpIter();
+
+ uint64_t expected_block = 10;
+ while (!iter->AtEnd() && expected_block > 0) {
+ ASSERT_FALSE(iter->AtEnd());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op->new_block, expected_block);
+
+ iter->Next();
+ expected_block--;
+ }
+ ASSERT_EQ(expected_block, 0);
+ ASSERT_TRUE(iter->AtEnd());
+}
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 6883c5e..b36c6f3 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -77,7 +77,7 @@
// v3 specific fields
// WIP: not quite sure how some of these are calculated yet, assuming buffer_size is determined
// during COW size estimation
- header_.sequence_buffer_offset = 0;
+ header_.sequence_data_count = 0;
header_.resume_point_count = 0;
header_.resume_point_max = kNumResumePoints;
header_.op_count = 0;
@@ -100,6 +100,7 @@
return false;
}
header_.compression_algorithm = *algorithm;
+ header_.op_count_max = options_.op_count_max;
if (parts.size() > 1) {
if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
@@ -163,7 +164,7 @@
return false;
}
}
- header_.op_count_max = options_.op_count_max;
+
resume_points_ = std::make_shared<std::vector<ResumePoint>>();
if (!Sync()) {
@@ -311,13 +312,18 @@
PLOG(ERROR) << "writing resume buffer failed";
return false;
}
- return Sync();
+ return Finalize();
}
bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (num_ops && data) return false;
- return false;
+ // TODO: size sequence buffer based on options
+ header_.sequence_data_count = num_ops;
+ if (!android::base::WriteFullyAtOffset(fd_, data, sizeof(data[0]) * num_ops,
+ GetSequenceOffset(header_))) {
+ PLOG(ERROR) << "writing sequence buffer failed";
+ return false;
+ }
+ return true;
}
bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 586e2cf..3f0d0e9 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -297,6 +297,10 @@
}
static UeventdConfiguration GetConfiguration() {
+ if (IsMicrodroid()) {
+ return ParseConfig({"/system/etc/ueventd.rc", "/vendor/etc/ueventd.rc"});
+ }
+
auto hardware = android::base::GetProperty("ro.hardware", "");
struct LegacyPathInfo {
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index b4482d0..76868bb 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -219,6 +219,12 @@
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
+ // If we get an error 2 'No such file or directory' , that means the
+ // cgroup is already removed, treat it as success and return 0 for
+ // idempotency.
+ if (ret < 0 && errno == ENOENT) {
+ ret = 0;
+ }
if (!ret || errno != EBUSY || !retries) break;
std::this_thread::sleep_for(5ms);
}
@@ -228,6 +234,9 @@
// so free up the kernel resources for the UID level cgroup.
const auto uid_path = ConvertUidToPath(cgroup, uid);
ret = rmdir(uid_path.c_str());
+ if (ret < 0 && errno == ENOENT) {
+ ret = 0;
+ }
}
return ret;
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 60dcc2a..3927501 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -71,6 +71,7 @@
/dev/mtp_usb 0660 root mtp
/dev/usb_accessory 0660 root usb
/dev/tun 0660 system vpn
+/dev/hidraw* 0660 system system
# CDMA radio interface MUX
/dev/ppp 0660 radio vpn