Merge "init: Start snapuserd_proxy after early-init" into main
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 296987a..cacd29d 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -685,7 +685,7 @@
     } else {
         offset = GetCowOpSourceInfoData(*op);
     }
-    if (!decompressor) {
+    if (!decompressor || op->data_length == header_.block_size) {
         CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);
         return stream.ReadFully(buffer, buffer_size);
     }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index d39c4a7..d0af0f9 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -339,15 +339,16 @@
     ASSERT_EQ(i, 5);
 }
 
-TEST_F(CowTestV3, AllOps) {
+TEST_F(CowTestV3, AllOpsWithCompression) {
     CowOptions options;
+    options.compression = "gz";
     options.op_count_max = 100;
     auto writer = CreateCowWriter(3, options, GetCowFd());
 
     std::string data;
     data.resize(options.block_size * 5);
     for (int i = 0; i < data.size(); i++) {
-        data[i] = char(rand() % 256);
+        data[i] = char(rand() % 4);
     }
 
     ASSERT_TRUE(writer->AddZeroBlocks(10, 5));
@@ -397,7 +398,6 @@
     while (i < 5) {
         auto op = iter->Get();
         ASSERT_EQ(op->type, kCowReplaceOp);
-        ASSERT_EQ(op->data_length, options.block_size);
         ASSERT_EQ(op->new_block, 18 + i);
         ASSERT_TRUE(
                 ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
@@ -411,7 +411,6 @@
     while (i < 5) {
         auto op = iter->Get();
         ASSERT_EQ(op->type, kCowXorOp);
-        ASSERT_EQ(op->data_length, 4096);
         ASSERT_EQ(op->new_block, 50 + i);
         ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314 + (i * options.block_size));  // 4096 * 24 + 10
         ASSERT_TRUE(
@@ -421,5 +420,42 @@
     }
     ASSERT_EQ(sink, data);
 }
+
+TEST_F(CowTestV3, GzCompression) {
+    CowOptions options;
+    options.op_count_max = 100;
+    options.compression = "gz";
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto header = reader.header_v3();
+    ASSERT_EQ(header.compression_algorithm, kCowCompressGz);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+
+    std::string sink(data.size(), '\0');
+
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->data_length, 56);  // compressed!
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    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 13b6157..86dd9f7 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -97,6 +97,8 @@
         LOG(ERROR) << "unrecognized compression: " << options_.compression;
         return false;
     }
+    header_.compression_algorithm = *algorithm;
+
     if (parts.size() > 1) {
         if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
             LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
@@ -108,6 +110,7 @@
     }
 
     compression_.algorithm = *algorithm;
+    compressor_ = ICompressor::Create(compression_, header_.block_size);
     return true;
 }
 
@@ -191,27 +194,41 @@
 
 bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
                              uint64_t old_block, uint16_t offset, uint8_t type) {
-    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
     const size_t num_blocks = (size / header_.block_size);
-
     for (size_t i = 0; i < num_blocks; i++) {
+        const uint8_t* const iter =
+                reinterpret_cast<const uint8_t*>(data) + (header_.block_size * i);
+
         CowOperation op = {};
         op.new_block = new_block_start + i;
 
         op.type = type;
-        op.data_length = static_cast<uint16_t>(header_.block_size);
-
         if (type == kCowXorOp) {
             op.source_info = (old_block + i) * header_.block_size + offset;
         } else {
             op.source_info = next_data_pos_;
         }
-        if (!WriteOperation(op, iter, header_.block_size)) {
-            LOG(ERROR) << "AddRawBlocks: write failed";
+        std::basic_string<uint8_t> compressed_data;
+        const void* out_data = iter;
+
+        op.data_length = header_.block_size;
+
+        if (compression_.algorithm) {
+            if (!compressor_) {
+                PLOG(ERROR) << "Compressor not initialized";
+                return false;
+            }
+            compressed_data = compressor_->Compress(out_data, header_.block_size);
+            if (compressed_data.size() < op.data_length) {
+                out_data = compressed_data.data();
+                op.data_length = compressed_data.size();
+            }
+        }
+        if (!WriteOperation(op, out_data, op.data_length)) {
+            PLOG(ERROR) << "AddRawBlocks with compression: write failed. new block: "
+                        << new_block_start << " compression: " << compression_.algorithm;
             return false;
         }
-
-        iter += header_.block_size;
     }
 
     return true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index af71a03..2347e91 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -46,6 +46,7 @@
     bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0);
     bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
                     uint16_t offset, uint8_t type);
+    bool CompressBlocks(size_t num_blocks, const void* data);
 
     off_t GetOpOffset(uint32_t op_index) const {
         CHECK_LT(op_index, header_.op_count_max);
@@ -56,9 +57,14 @@
   private:
     CowHeaderV3 header_{};
     CowCompression compression_;
+    // in the case that we are using one thread for compression, we can store and re-use the same
+    // compressor
+    std::unique_ptr<ICompressor> compressor_;
+    std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
 
     uint64_t next_op_pos_ = 0;
     uint64_t next_data_pos_ = 0;
+    std::vector<std::basic_string<uint8_t>> compressed_buf_;
 
     // in the case that we are using one thread for compression, we can store and re-use the same
     // compressor
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index cc92c68..9ebe799 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -191,14 +191,13 @@
   }
 
   // Retrieve package information from system, switching egid so we can read the file.
+  pkg_info info = {.name = pkgname};
   gid_t old_egid = getegid();
   if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed");
-  pkg_info info;
-  memset(&info, 0, sizeof(info));
-  info.name = pkgname;
   if (!packagelist_parse(packagelist_parse_callback, &info)) {
     error(1, errno, "packagelist_parse failed");
   }
+  if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   // Handle a multi-user data path
   if (userId > 0) {
@@ -211,7 +210,6 @@
   if (info.uid == 0) {
     error(1, 0, "unknown package: %s", pkgname);
   }
-  if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   // Verify that user id is not too big.
   if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {