Merge changes I138f3ace,I19f7fc51
* changes:
init: V devices need to specify user
init.usb.rc: specify user manually
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index db27cf0..d357e45 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -24,9 +24,8 @@
{
"name": "vab_legacy_tests"
},
- {
- "name": "vabc_legacy_tests"
- },
+ // TODO: b/279009697
+ //{"name": "vabc_legacy_tests"},
{
"name": "cow_api_test"
}
@@ -43,9 +42,8 @@
},
{
"name": "vab_legacy_tests"
- },
- {
- "name": "vabc_legacy_tests"
}
+ // TODO: b/279009697
+ //{"name": "vabc_legacy_tests"}
]
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 8e61a21..73429e1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -29,42 +29,12 @@
class ICowOpIter;
-// A ByteSink object handles requests for a buffer of a specific size. It
-// always owns the underlying buffer. It's designed to minimize potential
-// copying as we parse or decompress the COW.
-class IByteSink {
- public:
- virtual ~IByteSink() {}
-
- // Called when the reader has data. The size of the request is given. The
- // sink must return a valid pointer (or null on failure), and return the
- // maximum number of bytes that can be written to the returned buffer.
- //
- // The returned buffer is owned by IByteSink, but must remain valid until
- // the read operation has completed (or the entire buffer has been
- // covered by calls to ReturnData).
- //
- // After calling GetBuffer(), all previous buffers returned are no longer
- // valid.
- //
- // GetBuffer() is intended to be sequential. A returned size of N indicates
- // that the output stream will advance by N bytes, and the ReturnData call
- // indicates that those bytes have been fulfilled. Therefore, it is
- // possible to have ReturnBuffer do nothing, if the implementation doesn't
- // care about incremental writes.
- virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
-
- // Called when a section returned by |GetBuffer| has been filled with data.
- virtual bool ReturnData(void* buffer, size_t length) = 0;
-};
-
// Interface for reading from a snapuserd COW.
class ICowReader {
public:
virtual ~ICowReader() {}
// Return the file header.
- virtual bool GetHeader(CowHeader* header) = 0;
virtual CowHeader& GetHeader() = 0;
// Return the file footer.
@@ -84,10 +54,6 @@
virtual std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress) = 0;
// Get decoded bytes from the data section, handling any decompression.
- // All retrieved data is passed to the sink.
- virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
-
- // Get decoded bytes from the data section, handling any decompression.
//
// If ignore_bytes is non-zero, it specifies the initial number of bytes
// to skip writing to |buffer|.
@@ -101,13 +67,14 @@
size_t ignore_bytes = 0) = 0;
};
-// Iterate over a sequence of COW operations.
+// Iterate over a sequence of COW operations. The iterator is bidirectional.
class ICowOpIter {
public:
virtual ~ICowOpIter() {}
- // True if there are no more items to read forward, false otherwise.
- virtual bool Done() = 0;
+ // Returns true if the iterator is at the end of the operation list.
+ // If true, Get() and Next() must not be called.
+ virtual bool AtEnd() = 0;
// Read the current operation.
virtual const CowOperation& Get() = 0;
@@ -115,11 +82,13 @@
// Advance to the next item.
virtual void Next() = 0;
+ // Returns true if the iterator is at the beginning of the operation list.
+ // If true, Prev() must not be called; Get() however will be valid if
+ // AtEnd() is not true.
+ virtual bool AtBegin() = 0;
+
// Advance to the previous item.
virtual void Prev() = 0;
-
- // True if there are no more items to read backwards, false otherwise
- virtual bool RDone() = 0;
};
class CowReader final : public ICowReader {
@@ -140,7 +109,6 @@
bool InitForMerge(android::base::unique_fd&& fd);
bool VerifyMergeOps() override;
- bool GetHeader(CowHeader* header) override;
bool GetFooter(CowFooter* footer) override;
bool GetLastLabel(uint64_t* label) override;
@@ -153,7 +121,6 @@
std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
- bool ReadData(const CowOperation& op, IByteSink* sink) override;
ssize_t ReadData(const CowOperation& op, void* buffer, size_t buffer_size,
size_t ignore_bytes = 0) override;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
index f05aeb2..7d1b7ba 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
@@ -62,23 +62,24 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
- CowHeader header;
- CowFooter footer;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
+
+ const auto& header = reader.GetHeader();
ASSERT_EQ(header.magic, kCowMagicNumber);
ASSERT_EQ(header.major_version, kCowVersionMajor);
ASSERT_EQ(header.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
ASSERT_EQ(footer.op.num_ops, 100);
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
size_t i = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
ASSERT_EQ(op->compression, kCowCompressNone);
@@ -110,20 +111,21 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
- CowHeader header;
- CowFooter footer;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
+
+ const auto& header = reader.GetHeader();
ASSERT_EQ(header.magic, kCowMagicNumber);
ASSERT_EQ(header.major_version, kCowVersionMajor);
ASSERT_EQ(header.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
ASSERT_EQ(footer.op.num_ops, 4);
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
@@ -135,7 +137,7 @@
std::string sink(data.size(), '\0');
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
@@ -146,7 +148,7 @@
ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
// Note: the zero operation gets split into two blocks.
@@ -157,7 +159,7 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
@@ -167,7 +169,7 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, ReadWriteXor) {
@@ -188,20 +190,21 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
CowReader reader;
- CowHeader header;
- CowFooter footer;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
+
+ const auto& header = reader.GetHeader();
ASSERT_EQ(header.magic, kCowMagicNumber);
ASSERT_EQ(header.major_version, kCowVersionMajor);
ASSERT_EQ(header.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
ASSERT_EQ(footer.op.num_ops, 4);
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
@@ -213,7 +216,7 @@
std::string sink(data.size(), '\0');
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowXorOp);
@@ -225,7 +228,7 @@
ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
// Note: the zero operation gets split into two blocks.
@@ -236,7 +239,7 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
@@ -246,7 +249,7 @@
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, CompressGz) {
@@ -270,7 +273,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
std::string sink(data.size(), '\0');
@@ -283,7 +286,7 @@
ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
@@ -325,7 +328,7 @@
ASSERT_NE(iter, nullptr);
int total_blocks = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
auto op = &iter->Get();
if (op->type == kCowXorOp) {
@@ -399,7 +402,7 @@
ASSERT_NE(iter, nullptr);
int total_blocks = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
auto op = &iter->Get();
if (op->type == kCowReplaceOp) {
@@ -515,7 +518,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
std::string sink(data.size(), '\0');
@@ -528,13 +531,13 @@
ASSERT_EQ(sink, data);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
sink = {};
@@ -546,13 +549,13 @@
ASSERT_EQ(sink, data2);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, CompressTwoBlocks) {
@@ -576,9 +579,9 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
std::string sink(options.block_size, '\0');
@@ -655,7 +658,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
@@ -665,21 +668,21 @@
sink = {};
sink.resize(data2.size(), '\0');
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 3);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendLabelMissing) {
@@ -718,20 +721,20 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 0);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendExtendedCorrupted) {
@@ -776,13 +779,13 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendbyLabel) {
@@ -827,7 +830,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
@@ -837,7 +840,7 @@
sink = {};
sink.resize(options.block_size, '\0');
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
@@ -845,32 +848,32 @@
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 4);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, ClusterTest) {
@@ -908,7 +911,7 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
@@ -916,58 +919,58 @@
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 4);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 5);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 6);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, ClusterAppendTest) {
@@ -1007,14 +1010,14 @@
auto iter = reader.GetOpIter();
ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
auto op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 50);
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, *op, sink.data(), sink.size()));
@@ -1022,13 +1025,13 @@
iter->Next();
- ASSERT_FALSE(iter->Done());
+ ASSERT_FALSE(iter->AtEnd());
op = &iter->Get();
ASSERT_EQ(op->type, kCowClusterOp);
iter->Next();
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, AppendAfterFinalize) {
@@ -1065,8 +1068,7 @@
AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
const std::string& data) {
- CowHeader header;
- reader->GetHeader(&header);
+ const auto& header = reader->GetHeader();
std::string cmp = data;
cmp.resize(header.block_size, '\0');
@@ -1116,7 +1118,7 @@
size_t max_in_cluster = 0;
size_t num_in_cluster = 0;
size_t num_clusters = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const auto& op = iter->Get();
num_in_cluster++;
@@ -1177,7 +1179,7 @@
size_t max_in_cluster = 0;
size_t num_in_cluster = 0;
size_t num_clusters = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const auto& op = iter->Get();
num_in_cluster++;
@@ -1229,7 +1231,7 @@
size_t max_in_cluster = 0;
size_t num_in_cluster = 0;
size_t num_clusters = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const auto& op = iter->Get();
num_in_cluster++;
@@ -1273,14 +1275,14 @@
auto iter = reader.GetRevMergeOpIter();
for (int i = 0; i < seq_len; i++) {
- ASSERT_TRUE(!iter->Done());
+ ASSERT_TRUE(!iter->AtEnd());
const auto& op = iter->Get();
ASSERT_EQ(op.new_block, seq_len - i);
iter->Next();
}
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, MissingSeqOp) {
@@ -1324,7 +1326,7 @@
auto reader = std::make_unique<CowReader>();
ASSERT_TRUE(reader->Parse(cow_->fd, 1));
auto itr = reader->GetRevMergeOpIter();
- ASSERT_TRUE(itr->Done());
+ ASSERT_TRUE(itr->AtEnd());
writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
@@ -1339,8 +1341,8 @@
auto iter = reader->GetRevMergeOpIter();
uint64_t expected_block = 10;
- while (!iter->Done() && expected_block > 0) {
- ASSERT_FALSE(iter->Done());
+ while (!iter->AtEnd() && expected_block > 0) {
+ ASSERT_FALSE(iter->AtEnd());
const auto& op = iter->Get();
ASSERT_EQ(op.new_block, expected_block);
@@ -1349,7 +1351,7 @@
expected_block--;
}
ASSERT_EQ(expected_block, 0);
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, RevMergeOpItrTest) {
@@ -1390,7 +1392,7 @@
auto iter = reader.GetRevMergeOpIter();
auto expected_new_block = revMergeOpSequence.begin();
- while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
+ while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
const auto& op = iter->Get();
ASSERT_EQ(op.new_block, *expected_new_block);
@@ -1399,7 +1401,7 @@
expected_new_block++;
}
ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, LegacyRevMergeOpItrTest) {
@@ -1439,7 +1441,7 @@
auto iter = reader.GetRevMergeOpIter();
auto expected_new_block = revMergeOpSequence.begin();
- while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
+ while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
const auto& op = iter->Get();
ASSERT_EQ(op.new_block, *expected_new_block);
@@ -1448,7 +1450,7 @@
expected_new_block++;
}
ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
- ASSERT_TRUE(iter->Done());
+ ASSERT_TRUE(iter->AtEnd());
}
TEST_F(CowTest, InvalidMergeOrderTest) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index 483d559..3d34413 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -64,69 +64,17 @@
}
}
-class NoDecompressor final : public IDecompressor {
- public:
- bool Decompress(size_t) override;
- ssize_t Decompress(void*, size_t, size_t, size_t) override {
- LOG(ERROR) << "Not supported";
- return -1;
- }
-};
-
-bool NoDecompressor::Decompress(size_t) {
- size_t stream_remaining = stream_->Size();
- while (stream_remaining) {
- size_t buffer_size = stream_remaining;
- uint8_t* buffer = reinterpret_cast<uint8_t*>(sink_->GetBuffer(buffer_size, &buffer_size));
- if (!buffer) {
- LOG(ERROR) << "Could not acquire buffer from sink";
- return false;
- }
-
- // Read until we can fill the buffer.
- uint8_t* buffer_pos = buffer;
- size_t bytes_to_read = std::min(buffer_size, stream_remaining);
- while (bytes_to_read) {
- ssize_t read = stream_->Read(buffer_pos, bytes_to_read);
- if (read < 0) {
- return false;
- }
- if (!read) {
- LOG(ERROR) << "Stream ended prematurely";
- return false;
- }
- if (!sink_->ReturnData(buffer_pos, read)) {
- LOG(ERROR) << "Could not return buffer to sink";
- return false;
- }
- buffer_pos += read;
- bytes_to_read -= read;
- stream_remaining -= read;
- }
- }
- return true;
-}
-
-std::unique_ptr<IDecompressor> IDecompressor::Uncompressed() {
- return std::unique_ptr<IDecompressor>(new NoDecompressor());
-}
-
// Read chunks of the COW and incrementally stream them to the decoder.
class StreamDecompressor : public IDecompressor {
public:
- bool Decompress(size_t output_bytes) override;
ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
size_t ignore_bytes) override;
virtual bool Init() = 0;
- virtual bool DecompressInput(const uint8_t* data, size_t length) = 0;
virtual bool PartialDecompress(const uint8_t* data, size_t length) = 0;
bool OutputFull() const { return !ignore_bytes_ && !output_buffer_remaining_; }
protected:
- bool GetFreshBuffer();
-
- size_t output_bytes_;
size_t stream_remaining_;
uint8_t* output_buffer_ = nullptr;
size_t output_buffer_remaining_ = 0;
@@ -136,43 +84,6 @@
static constexpr size_t kChunkSize = 4096;
-bool StreamDecompressor::Decompress(size_t output_bytes) {
- if (!Init()) {
- return false;
- }
-
- stream_remaining_ = stream_->Size();
- output_bytes_ = output_bytes;
-
- uint8_t chunk[kChunkSize];
- while (stream_remaining_) {
- size_t max_read = std::min(stream_remaining_, sizeof(chunk));
- ssize_t read = stream_->Read(chunk, max_read);
- if (read < 0) {
- return false;
- }
- if (!read) {
- LOG(ERROR) << "Stream ended prematurely";
- return false;
- }
- if (!DecompressInput(chunk, read)) {
- return false;
- }
-
- stream_remaining_ -= read;
-
- if (stream_remaining_ && decompressor_ended_) {
- LOG(ERROR) << "Decompressor terminated early";
- return false;
- }
- }
- if (!decompressor_ended_) {
- LOG(ERROR) << "Decompressor expected more bytes";
- return false;
- }
- return true;
-}
-
ssize_t StreamDecompressor::Decompress(void* buffer, size_t buffer_size, size_t,
size_t ignore_bytes) {
if (!Init()) {
@@ -220,23 +131,11 @@
return buffer_size - output_buffer_remaining_;
}
-bool StreamDecompressor::GetFreshBuffer() {
- size_t request_size = std::min(output_bytes_, kChunkSize);
- output_buffer_ =
- reinterpret_cast<uint8_t*>(sink_->GetBuffer(request_size, &output_buffer_remaining_));
- if (!output_buffer_) {
- LOG(ERROR) << "Could not acquire buffer from sink";
- return false;
- }
- return true;
-}
-
class GzDecompressor final : public StreamDecompressor {
public:
~GzDecompressor();
bool Init() override;
- bool DecompressInput(const uint8_t* data, size_t length) override;
bool PartialDecompress(const uint8_t* data, size_t length) override;
private:
@@ -255,50 +154,6 @@
inflateEnd(&z_);
}
-bool GzDecompressor::DecompressInput(const uint8_t* data, size_t length) {
- z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));
- z_.avail_in = length;
-
- while (z_.avail_in) {
- // If no more output buffer, grab a new buffer.
- if (z_.avail_out == 0) {
- if (!GetFreshBuffer()) {
- return false;
- }
- z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
- z_.avail_out = output_buffer_remaining_;
- }
-
- // Remember the position of the output buffer so we can call ReturnData.
- auto avail_out = z_.avail_out;
-
- // Decompress.
- int rv = inflate(&z_, Z_NO_FLUSH);
- if (rv != Z_OK && rv != Z_STREAM_END) {
- LOG(ERROR) << "inflate returned error code " << rv;
- return false;
- }
-
- size_t returned = avail_out - z_.avail_out;
- if (!sink_->ReturnData(output_buffer_, returned)) {
- LOG(ERROR) << "Could not return buffer to sink";
- return false;
- }
- output_buffer_ += returned;
- output_buffer_remaining_ -= returned;
-
- if (rv == Z_STREAM_END) {
- if (z_.avail_in) {
- LOG(ERROR) << "Gz stream ended prematurely";
- return false;
- }
- decompressor_ended_ = true;
- return true;
- }
- }
- return true;
-}
-
bool GzDecompressor::PartialDecompress(const uint8_t* data, size_t length) {
z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));
z_.avail_in = length;
@@ -362,7 +217,6 @@
~BrotliDecompressor();
bool Init() override;
- bool DecompressInput(const uint8_t* data, size_t length) override;
bool PartialDecompress(const uint8_t* data, size_t length) override;
private:
@@ -380,31 +234,6 @@
}
}
-bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
- size_t available_in = length;
- const uint8_t* next_in = data;
-
- bool needs_more_output = false;
- while (available_in || needs_more_output) {
- if (!output_buffer_remaining_ && !GetFreshBuffer()) {
- return false;
- }
-
- auto output_buffer = output_buffer_;
- auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
- &output_buffer_remaining_, &output_buffer_, nullptr);
- if (r == BROTLI_DECODER_RESULT_ERROR) {
- LOG(ERROR) << "brotli decode failed";
- return false;
- }
- if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
- return false;
- }
- needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
- }
- return true;
-}
-
bool BrotliDecompressor::PartialDecompress(const uint8_t* data, size_t length) {
size_t available_in = length;
const uint8_t* next_in = data;
@@ -451,45 +280,6 @@
public:
~Lz4Decompressor() override = default;
- bool Decompress(const size_t output_size) override {
- size_t actual_buffer_size = 0;
- auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size);
- if (actual_buffer_size != output_size) {
- LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got "
- << actual_buffer_size << " bytes";
- return false;
- }
- // If input size is same as output size, then input is uncompressed.
- if (stream_->Size() == output_size) {
- ssize_t bytes_read = stream_->ReadFully(output_buffer, output_size);
- if (bytes_read != output_size) {
- LOG(ERROR) << "Failed to read all input at once. Expected: " << output_size
- << " actual: " << bytes_read;
- return false;
- }
- sink_->ReturnData(output_buffer, output_size);
- return true;
- }
- std::string input_buffer;
- input_buffer.resize(stream_->Size());
- ssize_t bytes_read = stream_->ReadFully(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 int bytes_decompressed =
- LZ4_decompress_safe(input_buffer.data(), static_cast<char*>(output_buffer),
- input_buffer.size(), output_size);
- if (bytes_decompressed != output_size) {
- LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
- << ", actual: " << bytes_decompressed;
- return false;
- }
- sink_->ReturnData(output_buffer, output_size);
- return true;
- }
-
ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
size_t ignore_bytes) override {
std::string input_buffer(stream_->Size(), '\0');
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
index 09164d3..9e83f3c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
@@ -50,9 +50,6 @@
static std::unique_ptr<IDecompressor> FromString(std::string_view compressor);
- // |output_bytes| is the expected total number of bytes to sink.
- virtual bool Decompress(size_t output_bytes) = 0;
-
// Decompress at most |buffer_size| bytes, ignoring the first |ignore_bytes|
// of the decoded stream. |buffer_size| must be at least one byte.
// |decompressed_size| is the expected total size if the entire stream were
@@ -64,11 +61,9 @@
size_t ignore_bytes = 0) = 0;
void set_stream(IByteStream* stream) { stream_ = stream; }
- void set_sink(IByteSink* sink) { sink_ = sink; }
protected:
IByteStream* stream_ = nullptr;
- IByteSink* sink_ = nullptr;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index e583ff0..893d95f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -509,7 +509,7 @@
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
std::unordered_map<uint64_t, CowOperation> overwritten_blocks;
- while (!itr->Done()) {
+ while (!itr->AtEnd()) {
CowOperation op = itr->Get();
uint64_t block;
bool offset;
@@ -544,11 +544,6 @@
return true;
}
-bool CowReader::GetHeader(CowHeader* header) {
- *header = header_;
- return true;
-}
-
bool CowReader::GetFooter(CowFooter* footer) {
if (!footer_) return false;
*footer = footer_.value();
@@ -565,12 +560,12 @@
public:
CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);
- bool Done() override;
+ bool AtEnd() override;
const CowOperation& Get() override;
void Next() override;
void Prev() override;
- bool RDone() override;
+ bool AtBegin() override;
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -582,26 +577,26 @@
op_iter_ = ops_->begin() + start;
}
-bool CowOpIter::RDone() {
+bool CowOpIter::AtBegin() {
return op_iter_ == ops_->begin();
}
void CowOpIter::Prev() {
- CHECK(!RDone());
+ CHECK(!AtBegin());
op_iter_--;
}
-bool CowOpIter::Done() {
+bool CowOpIter::AtEnd() {
return op_iter_ == ops_->end();
}
void CowOpIter::Next() {
- CHECK(!Done());
+ CHECK(!AtEnd());
op_iter_++;
}
const CowOperation& CowOpIter::Get() {
- CHECK(!Done());
+ CHECK(!AtEnd());
return (*op_iter_);
}
@@ -610,12 +605,12 @@
explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
- bool Done() override;
+ bool AtEnd() override;
const CowOperation& Get() override;
void Next() override;
void Prev() override;
- bool RDone() override;
+ bool AtBegin() override;
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -629,12 +624,12 @@
explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
- bool Done() override;
+ bool AtEnd() override;
const CowOperation& Get() override;
void Next() override;
void Prev() override;
- bool RDone() override;
+ bool AtBegin() override;
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -651,26 +646,26 @@
block_iter_ = cow_op_index_vec_->begin() + start;
}
-bool CowMergeOpIter::RDone() {
+bool CowMergeOpIter::AtBegin() {
return block_iter_ == cow_op_index_vec_->begin();
}
void CowMergeOpIter::Prev() {
- CHECK(!RDone());
+ CHECK(!AtBegin());
block_iter_--;
}
-bool CowMergeOpIter::Done() {
+bool CowMergeOpIter::AtEnd() {
return block_iter_ == cow_op_index_vec_->end();
}
void CowMergeOpIter::Next() {
- CHECK(!Done());
+ CHECK(!AtEnd());
block_iter_++;
}
const CowOperation& CowMergeOpIter::Get() {
- CHECK(!Done());
+ CHECK(!AtEnd());
return ops_->data()[*block_iter_];
}
@@ -683,26 +678,26 @@
block_riter_ = cow_op_index_vec_->rbegin();
}
-bool CowRevMergeOpIter::RDone() {
+bool CowRevMergeOpIter::AtBegin() {
return block_riter_ == cow_op_index_vec_->rbegin();
}
void CowRevMergeOpIter::Prev() {
- CHECK(!RDone());
+ CHECK(!AtBegin());
block_riter_--;
}
-bool CowRevMergeOpIter::Done() {
+bool CowRevMergeOpIter::AtEnd() {
return block_riter_ == cow_op_index_vec_->rend() - start_;
}
void CowRevMergeOpIter::Next() {
- CHECK(!Done());
+ CHECK(!AtEnd());
block_riter_++;
}
const CowOperation& CowRevMergeOpIter::Get() {
- CHECK(!Done());
+ CHECK(!AtEnd());
return ops_->data()[*block_riter_];
}
@@ -770,38 +765,6 @@
size_t remaining_;
};
-bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
- std::unique_ptr<IDecompressor> decompressor;
- switch (op.compression) {
- case kCowCompressNone:
- decompressor = IDecompressor::Uncompressed();
- break;
- case kCowCompressGz:
- decompressor = IDecompressor::Gz();
- break;
- case kCowCompressBrotli:
- decompressor = IDecompressor::Brotli();
- break;
- case kCowCompressLz4:
- decompressor = IDecompressor::Lz4();
- break;
- default:
- LOG(ERROR) << "Unknown compression type: " << op.compression;
- return false;
- }
-
- uint64_t offset;
- if (op.type == kCowXorOp) {
- offset = data_loc_->at(op.new_block);
- } else {
- offset = op.source;
- }
- CowDataStream stream(this, offset, op.data_length);
- decompressor->set_stream(&stream);
- decompressor->set_sink(sink);
- return decompressor->Decompress(header_.block_size);
-}
-
ssize_t CowReader::ReadData(const CowOperation& op, void* buffer, size_t buffer_size,
size_t ignore_bytes) {
std::unique_ptr<IDecompressor> decompressor;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
index 042ffb4..cb20c6f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
@@ -392,10 +392,11 @@
auto reader = std::make_unique<CowReader>();
std::queue<CowOperation> toAdd;
- if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
+ if (!reader->Parse(fd_, {label})) {
return false;
}
+ header_ = reader->GetHeader();
options_.block_size = header_.block_size;
options_.cluster_ops = header_.cluster_ops;
@@ -405,7 +406,7 @@
auto iter = reader->GetOpIter();
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
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 2716156..4090162 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -16,6 +16,7 @@
#include <stdio.h>
#include <unistd.h>
+#include <chrono>
#include <iomanip>
#include <iostream>
#include <string>
@@ -38,16 +39,16 @@
}
static void usage(void) {
- LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
- LOG(ERROR) << "\t -s Run Silent";
- LOG(ERROR) << "\t -d Attempt to decompress";
- LOG(ERROR) << "\t -b Show data for failed decompress";
- LOG(ERROR) << "\t -l Show ops";
- LOG(ERROR) << "\t -m Show ops in reverse merge order";
- LOG(ERROR) << "\t -n Show ops in merge order";
- LOG(ERROR) << "\t -a Include merged ops in any merge order listing";
- LOG(ERROR) << "\t -o Shows sequence op block order";
- LOG(ERROR) << "\t -v Verifies merge order has no conflicts\n";
+ std::cerr << "Usage: inspect_cow [-sd] <COW_FILE>\n";
+ std::cerr << "\t -s Run Silent\n";
+ std::cerr << "\t -d Attempt to decompress\n";
+ std::cerr << "\t -b Show data for failed decompress\n";
+ std::cerr << "\t -l Show ops\n";
+ std::cerr << "\t -m Show ops in reverse merge order\n";
+ std::cerr << "\t -n Show ops in merge order\n";
+ std::cerr << "\t -a Include merged ops in any merge order listing\n";
+ std::cerr << "\t -o Shows sequence op block order\n";
+ std::cerr << "\t -v Verifies merge order has no conflicts\n";
}
enum OpIter { Normal, RevMerge, Merge };
@@ -89,37 +90,40 @@
}
CowReader reader;
+
+ auto start_time = std::chrono::steady_clock::now();
if (!reader.Parse(fd)) {
LOG(ERROR) << "parse failed: " << path;
return false;
}
+ std::chrono::duration<double> parse_time = std::chrono::steady_clock::now() - start_time;
- CowHeader header;
- if (!reader.GetHeader(&header)) {
- LOG(ERROR) << "could not get header: " << path;
- return false;
- }
+ const CowHeader& header = reader.GetHeader();
CowFooter footer;
bool has_footer = false;
if (reader.GetFooter(&footer)) has_footer = true;
if (!opt.silent) {
- std::cout << "Major version: " << header.major_version << "\n";
- std::cout << "Minor version: " << header.minor_version << "\n";
+ std::cout << "Version: " << header.major_version << "." << header.minor_version << "\n";
std::cout << "Header size: " << header.header_size << "\n";
std::cout << "Footer size: " << header.footer_size << "\n";
std::cout << "Block size: " << header.block_size << "\n";
- std::cout << "Num merge ops: " << header.num_merge_ops << "\n";
- std::cout << "RA buffer size: " << header.buffer_size << "\n";
- std::cout << "\n";
+ std::cout << "Merge ops: " << header.num_merge_ops << "\n";
+ std::cout << "Readahead buffer: " << header.buffer_size << " bytes\n";
if (has_footer) {
- std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
- std::cout << "Number of Ops: " << footer.op.num_ops << "\n";
- std::cout << "\n";
+ std::cout << "Footer: ops usage: " << footer.op.ops_size << " bytes\n";
+ std::cout << "Footer: op count: " << footer.op.num_ops << "\n";
+ } else {
+ std::cout << "Footer: none\n";
}
}
+ if (!opt.silent) {
+ std::cout << "Parse time: " << (parse_time.count() * 1000) << "ms\n";
+ }
+
if (opt.verify_sequence) {
+ std::cout << "\n";
if (reader.VerifyMergeOps()) {
std::cout << "\nMerge sequence is consistent.\n";
} else {
@@ -140,7 +144,7 @@
bool success = true;
uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
- while (!iter->Done()) {
+ while (!iter->AtEnd()) {
const CowOperation& op = iter->Get();
if (!opt.silent && opt.show_ops) std::cout << op << "\n";
@@ -186,9 +190,11 @@
if (!opt.silent) {
auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
- std::cout << "Total-data-ops: " << total_ops << "Replace-ops: " << replace_ops
- << " Zero-ops: " << zero_ops << " Copy-ops: " << copy_ops
- << " Xor_ops: " << xor_ops << std::endl;
+ std::cout << "Data ops: " << total_ops << "\n";
+ std::cout << "Replace ops: " << replace_ops << "\n";
+ std::cout << "Zero ops: " << zero_ops << "\n";
+ std::cout << "Copy ops: " << copy_ops << "\n";
+ std::cout << "Xor ops: " << xor_ops << "\n";
}
return success;
@@ -237,15 +243,17 @@
break;
default:
android::snapshot::usage();
+ return 1;
}
}
- android::base::InitLogging(argv, android::snapshot::MyLogger);
if (argc < optind + 1) {
android::snapshot::usage();
return 1;
}
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+
if (!android::snapshot::Inspect(argv[optind], opt)) {
return 1;
}
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 54e2436..48efae0 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -80,15 +80,12 @@
bool CompressedSnapshotReader::SetCow(std::unique_ptr<CowReader>&& cow) {
cow_ = std::move(cow);
- CowHeader header;
- if (!cow_->GetHeader(&header)) {
- return false;
- }
+ const auto& header = cow_->GetHeader();
block_size_ = header.block_size;
// Populate the operation map.
op_iter_ = cow_->GetOpIter();
- while (!op_iter_->Done()) {
+ while (!op_iter_->AtEnd()) {
const CowOperation* op = &op_iter_->Get();
if (IsMetadataOp(*op)) {
op_iter_->Next();
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index f472bab..1fbfaf7 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2646,6 +2646,11 @@
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
+ if (ShouldSkipLegacyMerging()) {
+ LOG(INFO) << "Skipping legacy merge in test";
+ return;
+ }
+
// Simulate a reboot that tries the merge again, with the non-failing dm.
ASSERT_TRUE(UnmapAll());
init = NewManagerForFirstStageMount("_b");
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 8926030..b6e00ea 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -347,7 +347,6 @@
*/
bool Snapuserd::ReadMetadata() {
reader_ = std::make_unique<CowReader>();
- CowHeader header;
CowOptions options;
bool metadata_found = false;
int replace_ops = 0, zero_ops = 0, copy_ops = 0;
@@ -359,11 +358,7 @@
return false;
}
- if (!reader_->GetHeader(&header)) {
- SNAP_LOG(ERROR) << "Failed to get header";
- return false;
- }
-
+ const auto& header = reader_->GetHeader();
if (!(header.block_size == BLOCK_SZ)) {
SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
return false;
@@ -395,7 +390,7 @@
// this memset will ensure that metadata read is completed.
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- while (!cowop_rm_iter->Done()) {
+ while (!cowop_rm_iter->AtEnd()) {
const CowOperation* cow_op = &cowop_rm_iter->Get();
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
@@ -442,7 +437,7 @@
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- if (cowop_rm_iter->Done()) {
+ if (cowop_rm_iter->AtEnd()) {
vec_.push_back(std::move(de_ptr));
}
}
@@ -462,7 +457,7 @@
<< " Number of replace/zero ops completed in this area: " << num_ops
<< " Pending copy ops for this area: " << pending_ordered_ops;
- while (!cowop_rm_iter->Done()) {
+ while (!cowop_rm_iter->AtEnd()) {
do {
const CowOperation* cow_op = &cowop_rm_iter->Get();
@@ -531,7 +526,7 @@
source_blocks.insert(cow_op->new_block);
prev_id = cow_op->new_block;
cowop_rm_iter->Next();
- } while (!cowop_rm_iter->Done() && pending_ordered_ops);
+ } while (!cowop_rm_iter->AtEnd() && pending_ordered_ops);
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
@@ -574,7 +569,7 @@
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- if (cowop_rm_iter->Done()) {
+ if (cowop_rm_iter->AtEnd()) {
vec_.push_back(std::move(de_ptr));
SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
}
@@ -636,8 +631,7 @@
}
bool Snapuserd::MmapMetadata() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
if (header.major_version >= 2 && header.buffer_size > 0) {
total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
@@ -832,8 +826,7 @@
}
uint64_t Snapuserd::GetBufferMetadataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t size = header.header_size + sizeof(BufferState);
return size;
@@ -848,16 +841,14 @@
*
*/
size_t Snapuserd::GetBufferMetadataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t metadata_bytes = (header.buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ;
return metadata_bytes;
}
size_t Snapuserd::GetBufferDataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
return (header.header_size + GetBufferMetadataSize());
}
@@ -866,16 +857,14 @@
* (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
*/
size_t Snapuserd::GetBufferDataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t size = header.buffer_size - GetBufferMetadataSize();
return size;
}
struct BufferState* Snapuserd::GetBufferState() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
struct BufferState* ra_state =
reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
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 965af40..31a80a3 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -95,11 +95,17 @@
// internal COW format and if the block is compressed,
// it will be de-compressed.
bool WorkerThread::ProcessReplaceOp(const CowOperation* cow_op) {
- if (!reader_->ReadData(*cow_op, &bufsink_)) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "No space in buffer sink";
return false;
}
-
+ 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;
+ return false;
+ }
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index 2e4cac6..a6b6a7f 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -25,16 +25,15 @@
namespace android {
namespace snapshot {
-class BufferSink : public IByteSink {
+class BufferSink final {
public:
void Initialize(size_t size);
void* GetBufPtr() { return buffer_.get(); }
void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
void* GetPayloadBuffer(size_t size);
- void* GetBuffer(size_t requested, size_t* actual) override;
+ void* GetBuffer(size_t requested, size_t* actual);
void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
struct dm_user_header* GetHeaderPtr();
- bool ReturnData(void*, size_t) override { return true; }
void ResetBufferOffset() { buffer_offset_ = 0; }
void* GetPayloadBufPtr();
@@ -44,12 +43,12 @@
size_t buffer_size_;
};
-class XorSink : public IByteSink {
+class XorSink final {
public:
void Initialize(BufferSink* sink, size_t size);
void Reset();
- void* GetBuffer(size_t requested, size_t* actual) override;
- bool ReturnData(void* buffer, size_t len) override;
+ void* GetBuffer(size_t requested, size_t* actual);
+ bool ReturnData(void* buffer, size_t len);
private:
BufferSink* bufsink_;
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 25ce0ae..9df8cf9 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -98,8 +98,7 @@
}
} else {
reader_->UpdateMergeOpsCompleted(num_merge_ops);
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
if (lseek(cow_fd_.get(), 0, SEEK_SET) < 0) {
SNAP_PLOG(ERROR) << "lseek failed";
@@ -154,7 +153,6 @@
bool SnapshotHandler::ReadMetadata() {
reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE, true);
- CowHeader header;
CowOptions options;
SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
@@ -164,11 +162,7 @@
return false;
}
- if (!reader_->GetHeader(&header)) {
- SNAP_LOG(ERROR) << "Failed to get header";
- return false;
- }
-
+ const auto& header = reader_->GetHeader();
if (!(header.block_size == BLOCK_SZ)) {
SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
return false;
@@ -191,7 +185,7 @@
size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;
- while (!cowop_iter->Done()) {
+ while (!cowop_iter->AtEnd()) {
const CowOperation* cow_op = &cowop_iter->Get();
if (cow_op->type == kCowCopyOp) {
@@ -244,8 +238,7 @@
}
bool SnapshotHandler::MmapMetadata() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
@@ -367,8 +360,7 @@
}
uint64_t SnapshotHandler::GetBufferMetadataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
return (header.header_size + sizeof(BufferState));
}
@@ -383,8 +375,7 @@
*
*/
size_t SnapshotHandler::GetBufferMetadataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t buffer_size = header.buffer_size;
// If there is no scratch space, then just use the
@@ -397,8 +388,7 @@
}
size_t SnapshotHandler::GetBufferDataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
return (header.header_size + GetBufferMetadataSize());
}
@@ -407,8 +397,7 @@
* (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
*/
size_t SnapshotHandler::GetBufferDataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
size_t buffer_size = header.buffer_size;
// If there is no scratch space, then just use the
@@ -421,8 +410,7 @@
}
struct BufferState* SnapshotHandler::GetBufferState() {
- CowHeader header;
- reader_->GetHeader(&header);
+ const auto& header = reader_->GetHeader();
struct BufferState* ra_state =
reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
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 0d0f711..3bc02d4 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
@@ -76,11 +76,15 @@
// internal COW format and if the block is compressed,
// it will be de-compressed.
bool Worker::ProcessReplaceOp(const CowOperation* cow_op) {
- if (!reader_->ReadData(*cow_op, &bufsink_)) {
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ProcessReplaceOp failed to allocate buffer";
+ return false;
+ }
+ if (!reader_->ReadData(*cow_op, buffer, BLOCK_SZ)) {
SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
return false;
}
-
return true;
}
@@ -125,12 +129,26 @@
if (!ReadFromSourceDevice(cow_op)) {
return false;
}
+
xorsink_.Reset();
- if (!reader_->ReadData(*cow_op, &xorsink_)) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
+
+ size_t actual = 0;
+ void* buffer = xorsink_.GetBuffer(BLOCK_SZ, &actual);
+ if (!buffer || actual < BLOCK_SZ) {
+ SNAP_LOG(ERROR) << "ProcessXorOp failed to get buffer of " << BLOCK_SZ << " size, got "
+ << actual;
return false;
}
-
+ 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;
+ return false;
+ }
+ if (!xorsink_.ReturnData(buffer, size)) {
+ SNAP_LOG(ERROR) << "ProcessXorOp failed to return data";
+ return false;
+ }
return true;
}
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 d57f434..4d91ff3 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -30,7 +30,7 @@
bool checkOrderedOp = (replace_zero_vec == nullptr);
do {
- if (!cowop_iter_->Done() && num_ops) {
+ if (!cowop_iter_->AtEnd() && num_ops) {
const CowOperation* cow_op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
break;
@@ -45,7 +45,7 @@
num_ops -= 1;
nr_consecutive = 1;
- while (!cowop_iter_->Done() && num_ops) {
+ while (!cowop_iter_->AtEnd() && num_ops) {
const CowOperation* op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*op)) {
break;
@@ -85,7 +85,7 @@
SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
- while (!cowop_iter_->Done()) {
+ while (!cowop_iter_->AtEnd()) {
int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
std::vector<const CowOperation*> replace_zero_vec;
uint64_t source_offset;
@@ -93,7 +93,7 @@
int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
if (linear_blocks == 0) {
// Merge complete
- CHECK(cowop_iter_->Done());
+ CHECK(cowop_iter_->AtEnd());
break;
}
@@ -180,7 +180,7 @@
SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
- while (!cowop_iter_->Done()) {
+ while (!cowop_iter_->AtEnd()) {
const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
@@ -361,7 +361,7 @@
SNAP_LOG(INFO) << "MergeOrderedOps started....";
- while (!cowop_iter_->Done()) {
+ while (!cowop_iter_->AtEnd()) {
const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
@@ -443,7 +443,7 @@
if (!MergeOrderedOpsAsync()) {
SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
// Reset the iter so that we retry the merge
- while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
+ while (blocks_merged_in_group_ && !cowop_iter_->AtBegin()) {
cowop_iter_->Prev();
blocks_merged_in_group_ -= 1;
}
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 fbe57d2..4aad7a6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -114,6 +114,24 @@
return nr_consecutive;
}
+class [[nodiscard]] AutoNotifyReadAheadFailed {
+ public:
+ AutoNotifyReadAheadFailed(std::shared_ptr<SnapshotHandler> snapuserd) : snapuserd_(snapuserd) {}
+
+ ~AutoNotifyReadAheadFailed() {
+ if (cancelled_) {
+ return;
+ }
+ snapuserd_->ReadAheadIOFailed();
+ }
+
+ void Cancel() { cancelled_ = true; }
+
+ private:
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+ bool cancelled_ = false;
+};
+
bool ReadAhead::ReconstructDataFromCow() {
std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
loff_t metadata_offset = 0;
@@ -145,6 +163,8 @@
metadata_offset += sizeof(struct ScratchMetadata);
}
+ AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);
+
// We are done re-constructing the mapping; however, we need to make sure
// all the COW operations to-be merged are present in the re-constructed
// mapping.
@@ -162,7 +182,6 @@
if (!(num_ops == 0)) {
SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
<< " Pending ops: " << num_ops;
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -175,11 +194,11 @@
if (!snapuserd_->ReadAheadIOCompleted(true)) {
SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
- snapuserd_->ReadAheadIOFailed();
return false;
}
SNAP_LOG(INFO) << "ReconstructDataFromCow success";
+ notify_read_ahead_failed.Cancel();
return true;
}
@@ -467,9 +486,16 @@
if (xor_op_index < xor_op_vec.size()) {
const CowOperation* xor_op = xor_op_vec[xor_op_index];
if (xor_op->new_block == new_block) {
- if (!reader_->ReadData(*xor_op, &bufsink_)) {
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer for block: "
+ << xor_op->new_block;
+ return false;
+ }
+ 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;
+ << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
+ << ", return value: " << rv;
return false;
}
@@ -492,6 +518,8 @@
blocks_.clear();
std::vector<const CowOperation*> xor_op_vec;
+ AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);
+
bufsink_.ResetBufferOffset();
// Number of ops to be merged in this window. This is a fixed size
@@ -518,8 +546,6 @@
<< " offset :" << source_offset % BLOCK_SZ
<< " buffer_offset : " << buffer_offset << " io_size : " << io_size
<< " buf-addr : " << read_ahead_buffer_;
-
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -530,6 +556,7 @@
// Done with merging ordered ops
if (RAIterDone() && total_blocks_merged_ == 0) {
+ notify_read_ahead_failed.Cancel();
return true;
}
@@ -560,20 +587,25 @@
// Check if this block is an XOR op
if (xor_op->new_block == new_block) {
// Read the xor'ed data from COW
- if (!reader_->ReadData(*xor_op, &bufsink)) {
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
+ return false;
+ }
+ 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;
- snapuserd_->ReadAheadIOFailed();
+ << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
+ << ", return value: " << rv;
return false;
}
// Pointer to the data read from base device
- uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
+ uint8_t* read_buffer = reinterpret_cast<uint8_t*>(bufptr);
// Get the xor'ed data read from COW device
uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink.GetPayloadBufPtr());
// Retrieve the original data
for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
- buffer[byte_offset] ^= xor_data[byte_offset];
+ read_buffer[byte_offset] ^= xor_data[byte_offset];
}
// Move to next XOR op
@@ -604,6 +636,7 @@
bm->new_block = 0;
bm->file_offset = 0;
+ notify_read_ahead_failed.Cancel();
return true;
}
@@ -776,7 +809,7 @@
}
bool ReadAhead::RAIterDone() {
- if (cowop_iter_->Done()) {
+ if (cowop_iter_->AtEnd()) {
return true;
}
@@ -794,7 +827,7 @@
}
void ReadAhead::RAResetIter(uint64_t num_blocks) {
- while (num_blocks && !cowop_iter_->RDone()) {
+ while (num_blocks && !cowop_iter_->AtBegin()) {
cowop_iter_->Prev();
num_blocks -= 1;
}
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 48bc0b7..dbaeb93 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -96,6 +96,10 @@
// Returns false in case of error, true in case of success
bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
+// Check if a profile can be applied without failing.
+// Returns true if it can be applied without failing, false otherwise
+bool isProfileValidForProcess(const std::string& profile_name, int uid, int pid);
+
#endif // __ANDROID_VNDK__
__END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index e294260..1f29040 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -656,3 +656,13 @@
bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
return CgroupGetAttributePathForTask(attr_name, tid, path);
}
+
+bool isProfileValidForProcess(const std::string& profile_name, int uid, int pid) {
+ const TaskProfile* tp = TaskProfiles::GetInstance().GetProfile(profile_name);
+
+ if (tp == nullptr) {
+ return false;
+ }
+
+ return tp->IsValidForProcess(uid, pid);
+}
\ No newline at end of file
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 1731828..44dba2a 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -259,6 +259,31 @@
return true;
}
+bool SetAttributeAction::IsValidForProcess(uid_t, pid_t pid) const {
+ return IsValidForTask(pid);
+}
+
+bool SetAttributeAction::IsValidForTask(int tid) const {
+ std::string path;
+
+ if (!attribute_->GetPathForTask(tid, &path)) {
+ return false;
+ }
+
+ if (!access(path.c_str(), W_OK)) {
+ // operation will succeed
+ return true;
+ }
+
+ if (!access(path.c_str(), F_OK)) {
+ // file exists but not writable
+ return false;
+ }
+
+ // file does not exist, ignore if optional
+ return optional_;
+}
+
SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
: controller_(c), path_(p) {
FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
@@ -396,6 +421,39 @@
FdCacheHelper::Drop(fd_[cache_type]);
}
+bool SetCgroupAction::IsValidForProcess(uid_t uid, pid_t pid) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
+ return true;
+ }
+
+ if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
+ return false;
+ }
+
+ std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
+ return access(procs_path.c_str(), W_OK) == 0;
+}
+
+bool SetCgroupAction::IsValidForTask(int) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
+ return true;
+ }
+
+ if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
+ return false;
+ }
+
+ if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
+ // application-dependent path can't be used with tid
+ return false;
+ }
+
+ std::string tasks_path = controller()->GetTasksFilePath(path_);
+ return access(tasks_path.c_str(), W_OK) == 0;
+}
+
WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
const std::string& value, bool logfailures)
: task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
@@ -532,6 +590,37 @@
FdCacheHelper::Drop(fd_[cache_type]);
}
+bool WriteFileAction::IsValidForProcess(uid_t, pid_t) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
+ return true;
+ }
+
+ if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
+ return false;
+ }
+
+ return access(proc_path_.empty() ? task_path_.c_str() : proc_path_.c_str(), W_OK) == 0;
+}
+
+bool WriteFileAction::IsValidForTask(int) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
+ return true;
+ }
+
+ if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
+ return false;
+ }
+
+ if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
+ // application-dependent path can't be used with tid
+ return false;
+ }
+
+ return access(task_path_.c_str(), W_OK) == 0;
+}
+
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
profile->ExecuteForProcess(uid, pid);
@@ -558,6 +647,24 @@
}
}
+bool ApplyProfileAction::IsValidForProcess(uid_t uid, pid_t pid) const {
+ for (const auto& profile : profiles_) {
+ if (!profile->IsValidForProcess(uid, pid)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ApplyProfileAction::IsValidForTask(int tid) const {
+ for (const auto& profile : profiles_) {
+ if (!profile->IsValidForTask(tid)) {
+ return false;
+ }
+ }
+ return true;
+}
+
void TaskProfile::MoveTo(TaskProfile* profile) {
profile->elements_ = std::move(elements_);
profile->res_cached_ = res_cached_;
@@ -620,6 +727,20 @@
res_cached_ = false;
}
+bool TaskProfile::IsValidForProcess(uid_t uid, pid_t pid) const {
+ for (const auto& element : elements_) {
+ if (!element->IsValidForProcess(uid, pid)) return false;
+ }
+ return true;
+}
+
+bool TaskProfile::IsValidForTask(int tid) const {
+ for (const auto& element : elements_) {
+ if (!element->IsValidForTask(tid)) return false;
+ }
+ return true;
+}
+
void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
for (auto& iter : profiles_) {
iter.second->DropResourceCaching(cache_type);
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index a8ecb87..a62c5b0 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -72,12 +72,14 @@
virtual const char* Name() const = 0;
// Default implementations will fail
- virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
- virtual bool ExecuteForTask(int) const { return false; };
- virtual bool ExecuteForUID(uid_t) const { return false; };
+ virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; }
+ virtual bool ExecuteForTask(int) const { return false; }
+ virtual bool ExecuteForUID(uid_t) const { return false; }
virtual void EnableResourceCaching(ResourceCacheType) {}
virtual void DropResourceCaching(ResourceCacheType) {}
+ virtual bool IsValidForProcess(uid_t uid, pid_t pid) const { return false; }
+ virtual bool IsValidForTask(int tid) const { return false; }
protected:
enum CacheUseResult { SUCCESS, FAIL, UNUSED };
@@ -103,6 +105,8 @@
const char* Name() const override { return "SetTimerSlack"; }
bool ExecuteForTask(int tid) const override;
+ bool IsValidForProcess(uid_t uid, pid_t pid) const override { return true; }
+ bool IsValidForTask(int tid) const override { return true; }
private:
unsigned long slack_;
@@ -120,6 +124,8 @@
bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
bool ExecuteForTask(int tid) const override;
bool ExecuteForUID(uid_t uid) const override;
+ bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+ bool IsValidForTask(int tid) const override;
private:
const IProfileAttribute* attribute_;
@@ -137,6 +143,8 @@
bool ExecuteForTask(int tid) const override;
void EnableResourceCaching(ResourceCacheType cache_type) override;
void DropResourceCaching(ResourceCacheType cache_type) override;
+ bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+ bool IsValidForTask(int tid) const override;
const CgroupController* controller() const { return &controller_; }
@@ -161,6 +169,8 @@
bool ExecuteForTask(int tid) const override;
void EnableResourceCaching(ResourceCacheType cache_type) override;
void DropResourceCaching(ResourceCacheType cache_type) override;
+ bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+ bool IsValidForTask(int tid) const override;
private:
std::string task_path_, proc_path_, value_;
@@ -186,6 +196,8 @@
bool ExecuteForUID(uid_t uid) const;
void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
+ bool IsValidForProcess(uid_t uid, pid_t pid) const;
+ bool IsValidForTask(int tid) const;
private:
const std::string name_;
@@ -204,6 +216,8 @@
bool ExecuteForTask(int tid) const override;
void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+ bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+ bool IsValidForTask(int tid) const override;
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index 6a5b48b..eadbe76 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -175,6 +175,32 @@
}
}
+class TaskProfileFixture : public TestWithParam<TestParam> {
+ public:
+ ~TaskProfileFixture() = default;
+};
+
+TEST_P(TaskProfileFixture, TaskProfile) {
+ // Treehugger runs host tests inside a container without cgroupv2 support.
+ if (!IsCgroupV2MountedRw()) {
+ GTEST_SKIP();
+ return;
+ }
+ const TestParam params = GetParam();
+ ProfileAttributeMock pa(params.attr_name);
+ // Test simple profile with one action
+ std::shared_ptr<TaskProfile> tp = std::make_shared<TaskProfile>("test_profile");
+ tp->Add(std::make_unique<SetAttributeAction>(&pa, params.attr_value, params.optional_attr));
+ EXPECT_EQ(tp->IsValidForProcess(getuid(), getpid()), params.result);
+ EXPECT_EQ(tp->IsValidForTask(getpid()), params.result);
+ // Test aggregate profile
+ TaskProfile tp2("meta_profile");
+ std::vector<std::shared_ptr<TaskProfile>> profiles = {tp};
+ tp2.Add(std::make_unique<ApplyProfileAction>(profiles));
+ EXPECT_EQ(tp2.IsValidForProcess(getuid(), getpid()), params.result);
+ EXPECT_EQ(tp2.IsValidForTask(getpid()), params.result);
+}
+
// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
// exists }.
INSTANTIATE_TEST_SUITE_P(
@@ -215,4 +241,28 @@
.log_prefix = "Failed to write",
.log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
+// Test TaskProfile IsValid calls.
+INSTANTIATE_TEST_SUITE_P(
+ TaskProfileTestSuite, TaskProfileFixture,
+ Values(
+ // Test operating on non-existing cgroup attribute fails.
+ TestParam{.attr_name = "no-such-attribute",
+ .attr_value = ".",
+ .optional_attr = false,
+ .result = false},
+ // Test operating on optional non-existing cgroup attribute succeeds.
+ TestParam{.attr_name = "no-such-attribute",
+ .attr_value = ".",
+ .optional_attr = true,
+ .result = true},
+ // Test operating on existing cgroup attribute succeeds.
+ TestParam{.attr_name = "cgroup.procs",
+ .attr_value = ".",
+ .optional_attr = false,
+ .result = true},
+ // Test operating on optional existing cgroup attribute succeeds.
+ TestParam{.attr_name = "cgroup.procs",
+ .attr_value = ".",
+ .optional_attr = true,
+ .result = true}));
} // namespace