Merge "Use target device mapper given mapper target" into main
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index c3e1302..a6d8226 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -94,7 +94,7 @@
if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
err(1, "sigaction failed"); \
} \
- alarm(seconds); \
+ alarm(seconds * android::base::HwTimeoutMultiplier()); \
auto value = expr; \
int saved_errno = errno; \
if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
@@ -2825,7 +2825,8 @@
}
prev_file = match[1];
- unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release());
+ auto elf_memory = unwindstack::Memory::CreateFileMemory(prev_file, 0);
+ unwindstack::Elf elf(elf_memory);
if (!elf.Init() || !elf.valid()) {
// Skipping invalid elf files.
continue;
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d71fc6c..15f09b3 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,8 +382,10 @@
return "SEGV_MTEAERR";
case SEGV_MTESERR:
return "SEGV_MTESERR";
+ case SEGV_CPERR:
+ return "SEGV_CPERR";
}
- static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code");
+ static_assert(NSIGSEGV == SEGV_CPERR, "missing SEGV_* si_code");
break;
case SIGSYS:
switch (si->si_code) {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 56cac88..f85d1de 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -446,3 +446,14 @@
host_supported: true,
export_include_dirs: ["."],
}
+
+python_test_host {
+ name: "fastboot_integration_test",
+ main: "test_fastboot.py",
+ srcs: ["test_fastboot.py"],
+ data: [":fastboot"],
+ test_config: "fastboot_integration_test.xml",
+ test_options: {
+ unit_test: false,
+ },
+}
diff --git a/fastboot/fastboot_integration_test.xml b/fastboot/fastboot_integration_test.xml
new file mode 100644
index 0000000..ad14cab
--- /dev/null
+++ b/fastboot/fastboot_integration_test.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config to run fastboot integration tests">
+ <option name="test-suite-tag" value="fastboot_unittests" />
+ <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+ <option name="par-file-name" value="fastboot_integration_test" />
+ <option name="test-timeout" value="1m" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/fastboot/test_fastboot.py b/fastboot/test_fastboot.py
new file mode 100644
index 0000000..adcaec7
--- /dev/null
+++ b/fastboot/test_fastboot.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Tests for the fastboot command line utility.
+"""
+
+import re
+import subprocess
+import unittest
+
+
+class DevicesTest(unittest.TestCase):
+ """Tests for `fastboot devices`."""
+
+
+ def test_devices(self):
+ """Ensure that the format of `fastboot devices` does not change.
+
+ `fastboot devices` should alternate between a line containing the
+ device's serial number and fastboot state and an empty line
+ """
+ output = subprocess.check_output(["fastboot", "devices"])
+
+ previous_line_was_empty = True
+ for line in output.decode().splitlines():
+ if previous_line_was_empty:
+ if not re.match(r"[a-zA-Z\d]+\s+(bootloader|fastbootd)", line):
+ self.fail("%s does not match the expected format <serial no>\\s+(bootloader|fastbootd)" % line)
+ previous_line_was_empty = False
+ else:
+ if line:
+ self.fail("Expected an empty line. Received '%s'" % line)
+ previous_line_was_empty = True
+
+ if len(output) == 0:
+ self.fail("Output is empty. Are any devices connected?")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index bacfa4b..6b742e5 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -112,6 +112,7 @@
// properties are loaded.
static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
if (vndk_version.empty()) {
+ // Vendor overlay is disabled from VNDK deprecated devices.
LINFO << "vendor overlay: vndk version not defined";
return false;
}
diff --git a/fs_mgr/include_fstab b/fs_mgr/include_fstab
deleted file mode 120000
index 728737f..0000000
--- a/fs_mgr/include_fstab
+++ /dev/null
@@ -1 +0,0 @@
-libfstab/include
\ No newline at end of file
diff --git a/fs_mgr/liblp/fuzzer/README.md b/fs_mgr/liblp/fuzzer/README.md
index f831e2e..015c59b 100644
--- a/fs_mgr/liblp/fuzzer/README.md
+++ b/fs_mgr/liblp/fuzzer/README.md
@@ -18,15 +18,14 @@
9. alignmentOffset (parameter name: "alignment_offset")
10. logicalBlockSize (parameter name: "logical_block_size")
11. maxMetadataSize (parameter name: "max_metadata_size")
-12. numSlots (parameter name: "metadata_slot_count")
-13. deviceIndex (parameter name: "device_index")
-14. start (parameter name: "start")
-15. end (parameter name: "end")
-16. addedGroupName (parameter name: "group_name")
-17. partitionGroupName (parameter name: "partition_name")
-18. numSectors (parameter name: "num_sectors")
-19. physicalSector (parameter name: "physical_sector")
-20. resizedPartitionSize (parameter name: "requested_size")
+12. deviceIndex (parameter name: "device_index")
+13. start (parameter name: "start")
+14. end (parameter name: "end")
+15. addedGroupName (parameter name: "group_name")
+16. partitionGroupName (parameter name: "partition_name")
+17. numSectors (parameter name: "num_sectors")
+18. physicalSector (parameter name: "physical_sector")
+19. resizedPartitionSize (parameter name: "requested_size")
| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
@@ -41,7 +40,6 @@
|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|
|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
|`maxMetadataSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
-|`numSlots`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
|`deviceIndex`| Integer |Value obtained from FuzzedDataProvider|
|`start`| Integer |Value obtained from FuzzedDataProvider|
|`end`| Integer |Value obtained from FuzzedDataProvider|
@@ -67,17 +65,15 @@
1. kAttributeTypes (parameter name: "attribute")
2. blockDevSize (parameter name: "blockdev_size")
3. metadataMaxSize (parameter name: "metadata_max_size")
-4. metadataSlotCount (parameter name: "metadata_slot_count")
-5. partitionName (parameter name: "partition_name")
-6. data (parameter name: "data")
-7. imageName (parameter name: "image_name")
+4. partitionName (parameter name: "partition_name")
+5. data (parameter name: "data")
+6. imageName (parameter name: "image_name")
| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|
|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|
|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
-|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
|`partitionName`| String |Value obtained from FuzzedDataProvider|
|`data`| String |Value obtained from FuzzedDataProvider|
|`imageName`| String |Value obtained from FuzzedDataProvider|
@@ -102,12 +98,11 @@
4. logicalBlockSize (parameter name: "logical_block_size")
5. blockDevSize (parameter name: "blockdev_size")
6. metadataMaxSize (parameter name: "metadata_max_size")
-7. metadataSlotCount (parameter name: "metadata_slot_count")
-8. blockDeviceInfoName (parameter name: "block_device_info_name")
-9. numSectors (parameter name: "num_sectors")
-10. physicalSector (parameter name: "physical_sector")
-11. sparsify (parameter name: "sparsify")
-12. buffer (parameter name: "data")
+7. blockDeviceInfoName (parameter name: "block_device_info_name")
+8. numSectors (parameter name: "num_sectors")
+9. physicalSector (parameter name: "physical_sector")
+10. sparsify (parameter name: "sparsify")
+11. buffer (parameter name: "data")
| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
@@ -117,7 +112,6 @@
|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
|`blockDevSize`| Integer value in multiples of `LP_SECTOR_SIZE`|Value obtained from FuzzedDataProvider|
|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
-|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
|`blockDeviceInfoName`| String |Value obtained from FuzzedDataProvider|
|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
diff --git a/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
index a15bc89..cb192ea 100644
--- a/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
@@ -39,8 +39,6 @@
static constexpr uint32_t kValidLogicalBlockSize = 4096;
static constexpr uint32_t kMinMetadataSize = 0;
static constexpr uint32_t kMaxMetadataSize = 10000;
-static constexpr uint32_t kMinSlot = 0;
-static constexpr uint32_t kMaxSlot = 10;
static constexpr uint32_t kMinFactor = 0;
static constexpr uint32_t kMaxFactor = 10;
static constexpr uint32_t kMetadataGeometrySize = 4096;
@@ -108,7 +106,7 @@
uint32_t randomMetadataMaxSize =
mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataSize, kMaxMetadataSize);
uint32_t metadataMaxSize = mFdp.ConsumeBool() ? kMetadataSize : randomMetadataMaxSize;
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
if (mBuilder.get()) {
@@ -128,14 +126,15 @@
Partition* super = mBuilder->AddPartition(mSuperPartitionName, LP_PARTITION_ATTR_READONLY);
mBuilder->AddPartition(mPartitionName, LP_PARTITION_ATTR_READONLY);
- int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
- kMinSectorValue, kMaxSectorValue)
- : kValidNumSectors;
- int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ if (super) {
+ int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
kMinSectorValue, kMaxSectorValue)
- : kValidPhysicalSector;
-
- mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);
+ : kValidNumSectors;
+ int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinSectorValue, kMaxSectorValue)
+ : kValidPhysicalSector;
+ mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);
+ }
}
}
diff --git a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
index e5fbe27..7f09ac8 100644
--- a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
@@ -48,8 +48,6 @@
static constexpr uint32_t kZeroAlignment = 0;
static constexpr uint32_t kZeroAlignmentOffset = 0;
static constexpr uint32_t kMaxBytes = 20;
-static constexpr uint32_t kMinSlot = 0;
-static constexpr uint32_t kMaxSlot = 10;
static constexpr uint32_t kMinBuilder = 0;
static constexpr uint32_t kMaxBuilder = 4;
@@ -108,9 +106,7 @@
switch (randomBuilder) {
case 0: {
uint32_t maxMetadataSize = getParamValue(kValidMaxMetadataSize);
- uint32_t numSlots = mFdp.ConsumeBool()
- ? kMaxSlot
- : mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t numSlots = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(mBlockDevices, superBlockDeviceName, maxMetadataSize,
numSlots);
break;
@@ -120,14 +116,14 @@
mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
uint32_t metadataMaxSize =
mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
break;
}
case 2: {
uint64_t blockDevSize = getParamValue(kValidBlockSize);
uint32_t metadataSize = getParamValue(kValidMetadataSize);
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(blockDevSize, metadataSize, metadataSlotCount);
break;
}
@@ -213,13 +209,13 @@
void BuilderFuzzer::callVerifyExtentsAgainstSourceMetadata() {
uint64_t sourceBlockDevSize = getParamValue(kValidBlockSize);
uint32_t sourceMetadataMaxSize = getParamValue(kValidMetadataSize);
- uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 2);
+ uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
auto sourceBuilder =
MetadataBuilder::New(sourceBlockDevSize, sourceMetadataMaxSize, sourceSlotCount);
uint64_t targetBlockDevSize = getParamValue(kValidBlockSize);
uint32_t targetMetadataMaxSize = getParamValue(kValidMetadataSize);
- uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 2);
+ uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
auto targetBuilder =
MetadataBuilder::New(targetBlockDevSize, targetMetadataMaxSize, targetSlotCount);
@@ -292,17 +288,20 @@
int64_t numExtents =
mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
- bool extentAdded = false;
- for (int64_t i = 0; i <= numExtents; ++i) {
- extentAdded = mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,
- numSectors, physicalSector);
- }
+ if (mFuzzPartition) {
+ bool extentAdded = false;
+ for (int64_t i = 0; i <= numExtents; ++i) {
+ extentAdded =
+ mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,
+ numSectors, physicalSector);
+ }
- if (extentAdded) {
- unique_ptr<LpMetadata> metadata = mBuilder->Export();
- uint64_t alignedSize =
- mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
- mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);
+ if (extentAdded) {
+ unique_ptr<LpMetadata> metadata = mBuilder->Export();
+ uint64_t alignedSize =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+ mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);
+ }
}
},
[&]() { callVerifyExtentsAgainstSourceMetadata(); },
diff --git a/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
index 887093c..a6642d7 100644
--- a/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
@@ -38,8 +38,6 @@
static constexpr uint32_t kMinMetadataValue = 0;
static constexpr uint32_t kMaxMetadataValue = 10000;
static constexpr uint32_t kMaxBytes = 20;
-static constexpr uint32_t kMinSlot = 0;
-static constexpr uint32_t kMaxSlot = 10;
static constexpr uint32_t kMinOpen = 0;
static constexpr uint32_t kMaxOpen = 2;
@@ -58,14 +56,14 @@
void invokeSuperLayoutBuilderAPIs();
void callRandomOpen(int32_t open);
void addMultiplePartitions(int32_t numPartitions);
- void setupSuperLayoutBuilder(string fuzzPartitionName);
+ void setupSuperLayoutBuilder();
SuperLayoutBuilder mSuperLayoutBuilder;
unique_ptr<MetadataBuilder> mSuperBuilder;
unique_ptr<LpMetadata> mMetadata;
bool mOpenSuccess = false;
};
-void SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder(string fuzzPartitionName) {
+void SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder() {
uint64_t randomBlockDevSize =
mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
uint64_t blockDevSize = mFdp.ConsumeBool() ? kSuperLayoutValidBlockDevSize : randomBlockDevSize;
@@ -73,7 +71,7 @@
mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
uint32_t metadataMaxSize =
mFdp.ConsumeBool() ? kSuperLayoutValidMetadataSize : randomMetadataMaxSize;
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mSuperBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
if (mSuperBuilder.get()) {
@@ -85,10 +83,6 @@
uint32_t randomOpen = mFdp.ConsumeIntegralInRange<uint32_t>(kMinOpen, kMaxOpen);
callRandomOpen(randomOpen);
-
- if (!fuzzPartitionName.size()) {
- fuzzPartitionName = "builder_partition";
- }
}
}
@@ -125,7 +119,10 @@
string imageName = mFdp.ConsumeRandomLengthString(kMaxBytes);
string fuzzPartitionName =
mFdp.ConsumeBool() ? "builder_partition" : mFdp.ConsumeRandomLengthString(kMaxBytes);
- setupSuperLayoutBuilder(fuzzPartitionName);
+ if (!fuzzPartitionName.size()) {
+ fuzzPartitionName = "builder_partition";
+ }
+ setupSuperLayoutBuilder();
if (mOpenSuccess) {
while (mFdp.remaining_bytes()) {
auto invokeSuperAPIs = mFdp.PickValueInArray<const function<void()>>({
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6fad662..a8a7716 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -198,6 +198,7 @@
"libsnapshot_cow/cow_format.cpp",
"libsnapshot_cow/cow_reader.cpp",
"libsnapshot_cow/parser_v2.cpp",
+ "libsnapshot_cow/parser_v3.cpp",
"libsnapshot_cow/snapshot_reader.cpp",
"libsnapshot_cow/writer_base.cpp",
"libsnapshot_cow/writer_v2.cpp",
@@ -419,6 +420,7 @@
},
data: [
"tools/testdata/cow_v2",
+ "tools/testdata/incompressible_block",
],
auto_gen_config: true,
require_root: false,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index c9777a3..75467cb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -94,13 +94,27 @@
} __attribute__((packed));
+// Resume point structure used for resume buffer
+struct ResumePoint {
+ // monotonically increasing value used by update_engine
+ uint64_t label;
+ // Index of last CowOperation guaranteed to be resumable
+ uint32_t op_index;
+} __attribute__((packed));
+
+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
+ uint32_t resume_point_count;
// Size, in bytes, of the CowResumePoint buffer.
- uint32_t resume_buffer_size;
- // Size, in bytes, of the CowOperation buffer.
- uint32_t op_buffer_size;
+ uint32_t resume_point_max;
+ // Number of CowOperationV3 structs in the operation buffer, currently and total
+ // region size.
+ uint32_t op_count;
+ uint32_t op_count_max;
// Compression Algorithm
uint32_t compression_algorithm;
} __attribute__((packed));
@@ -218,6 +232,19 @@
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) {
+ return header.prefix.header_size + header.buffer_size;
+}
+
struct CowFooter {
CowFooterOperation op;
uint8_t unused[64];
@@ -243,6 +270,8 @@
std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
+std::ostream& operator<<(std::ostream& os, ResumePoint const& arg);
+
int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index debaf36..c87b32d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -16,6 +16,7 @@
#include <stdint.h>
+#include <deque>
#include <functional>
#include <memory>
#include <optional>
@@ -145,6 +146,7 @@
size_t ignore_bytes = 0) override;
CowHeader& GetHeader() override { return header_; }
+ const CowHeaderV3& header_v3() const { return header_; }
bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read);
bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
@@ -182,16 +184,14 @@
uint64_t num_total_data_ops_{};
uint64_t num_ordered_ops_to_merge_{};
bool has_seq_ops_{};
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_;
ReaderFlags reader_flag_;
bool is_merge_{};
- uint8_t compression_type_ = kCowCompressNone;
};
// Though this function takes in a CowHeaderV3, the struct could be populated as a v1/v2 CowHeader.
// The extra fields will just be filled as 0. V3 header is strictly a superset of v1/v2 header and
// contains all of the latter's field
bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header);
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 3016e93..5b1e56c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -54,6 +54,9 @@
// Batch write cluster ops
bool batch_write = false;
+
+ // Size of the cow operation buffer; used in v3 only.
+ uint32_t op_count_max = 0;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index b905291..937065d 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -100,6 +100,11 @@
return os;
}
+std::ostream& operator<<(std::ostream& os, ResumePoint const& resume_point) {
+ os << "ResumePoint(" << resume_point.label << " , " << resume_point.op_index << ")";
+ return os;
+}
+
int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 3b84c95..8412879 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -28,8 +28,8 @@
#include <zlib.h>
#include "cow_decompress.h"
-#include "libsnapshot/cow_format.h"
#include "parser_v2.h"
+#include "parser_v3.h"
namespace android {
namespace snapshot {
@@ -82,10 +82,9 @@
cow->merge_op_start_ = merge_op_start_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
- cow->data_loc_ = data_loc_;
+ cow->xor_data_loc_ = xor_data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
- cow->compression_type_ = compression_type_;
return cow;
}
@@ -104,11 +103,14 @@
PLOG(ERROR) << "lseek header failed";
return false;
}
- if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+
+ CHECK_GE(header_.prefix.header_size, sizeof(CowHeader));
+ CHECK_LE(header_.prefix.header_size, sizeof(header_));
+
+ if (!android::base::ReadFully(fd_, &header_, header_.prefix.header_size)) {
PLOG(ERROR) << "read header failed";
return false;
}
-
return true;
}
@@ -124,52 +126,35 @@
return false;
}
- CowParserV2 parser;
- if (!parser.Parse(fd, header_, label)) {
+ std::unique_ptr<CowParserBase> parser;
+ switch (header_.prefix.major_version) {
+ case 1:
+ case 2:
+ parser = std::make_unique<CowParserV2>();
+ break;
+ case 3:
+ parser = std::make_unique<CowParserV3>();
+ break;
+ default:
+ LOG(ERROR) << "Unknown version: " << header_.prefix.major_version;
+ return false;
+ }
+ if (!parser->Parse(fd, header_, label)) {
return false;
}
- footer_ = parser.footer();
- fd_size_ = parser.fd_size();
- last_label_ = parser.last_label();
- data_loc_ = parser.data_loc();
- ops_ = std::make_shared<std::vector<CowOperation>>(parser.ops()->size());
-
- // Translate the operation buffer from on disk to in memory
- for (size_t i = 0; i < parser.ops()->size(); i++) {
- const auto& v2_op = parser.ops()->at(i);
-
- auto& new_op = ops_->at(i);
- new_op.type = v2_op.type;
- new_op.data_length = v2_op.data_length;
-
- if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
- LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
- return false;
- }
- new_op.new_block = v2_op.new_block;
-
- uint64_t source_info = v2_op.source;
- if (new_op.type != kCowLabelOp) {
- source_info &= kCowOpSourceInfoDataMask;
- if (source_info != v2_op.source) {
- LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
- return false;
- }
- }
- if (v2_op.compression != kCowCompressNone) {
- if (compression_type_ == kCowCompressNone) {
- compression_type_ = v2_op.compression;
- } else if (compression_type_ != v2_op.compression) {
- LOG(ERROR) << "COW has mixed compression types which is not supported;"
- << " previously saw " << compression_type_ << ", got "
- << v2_op.compression << ", op: " << v2_op;
- return false;
- }
- }
- new_op.source_info = source_info;
+ TranslatedCowOps ops_info;
+ if (!parser->Translate(&ops_info)) {
+ return false;
}
+ header_ = ops_info.header;
+ ops_ = std::move(ops_info.ops);
+ footer_ = parser->footer();
+ fd_size_ = parser->fd_size();
+ last_label_ = parser->last_label();
+ xor_data_loc_ = parser->xor_data_loc();
+
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
return PrepMergeOps();
@@ -615,8 +600,8 @@
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
- len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) {
+ if (offset < header_.prefix.header_size || offset >= fd_size_ || offset + len > fd_size_ ||
+ len >= fd_size_) {
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
return false;
}
@@ -664,7 +649,7 @@
};
uint8_t CowReader::GetCompressionType() {
- return compression_type_;
+ return header_.compression_algorithm;
}
ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
@@ -696,12 +681,12 @@
uint64_t offset;
if (op->type == kCowXorOp) {
- offset = data_loc_->at(op->new_block);
+ offset = xor_data_loc_->at(op->new_block);
} else {
offset = GetCowOpSourceInfoData(*op);
}
-
- if (!decompressor) {
+ if (!decompressor ||
+ ((op->data_length == header_.block_size) && (header_.prefix.major_version == 3))) {
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/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index a291469..993630b 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -74,15 +74,16 @@
}
}
-static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeader& header) {
+static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeaderV3& header) {
CowParserV2 parser;
if (!parser.Parse(fd, header)) {
LOG(ERROR) << "v2 parser failed";
return false;
}
- for (const auto& op : *parser.ops()) {
+ for (const auto& op : *parser.get_v2ops()) {
std::cout << op << "\n";
- if (auto iter = parser.data_loc()->find(op.new_block); iter != parser.data_loc()->end()) {
+ if (auto iter = parser.xor_data_loc()->find(op.new_block);
+ iter != parser.xor_data_loc()->end()) {
std::cout << " data loc: " << iter->second << "\n";
}
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
new file mode 100644
index 0000000..837b33e
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <optional>
+#include <unordered_map>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct TranslatedCowOps {
+ CowHeaderV3 header;
+ std::shared_ptr<std::vector<CowOperationV3>> ops;
+};
+
+class CowParserBase {
+ public:
+ virtual ~CowParserBase() = default;
+
+ virtual bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+ std::optional<uint64_t> label = {}) = 0;
+ virtual bool Translate(TranslatedCowOps* out) = 0;
+ virtual std::optional<CowFooter> footer() const { return std::nullopt; }
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc() {
+ return xor_data_loc_;
+ };
+
+ uint64_t fd_size() const { return fd_size_; }
+ const std::optional<uint64_t>& last_label() const { return last_label_; }
+
+ protected:
+ CowHeaderV3 header_ = {};
+ uint64_t fd_size_;
+ std::optional<uint64_t> last_label_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_ = {};
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index 8f20317..08a43a4 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -18,12 +18,14 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <libsnapshot/cow_format.h>
+
namespace android {
namespace snapshot {
using android::base::borrowed_fd;
-bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
+bool CowParserV2::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
auto pos = lseek(fd.get(), 0, SEEK_END);
if (pos < 0) {
PLOG(ERROR) << "lseek end failed";
@@ -47,8 +49,7 @@
return false;
}
- if ((header_.prefix.major_version > kCowVersionMajor) ||
- (header_.prefix.minor_version != kCowVersionMinor)) {
+ if (header_.prefix.major_version > 2 || header_.prefix.minor_version != 0) {
LOG(ERROR) << "Header version mismatch, "
<< "major version: " << header_.prefix.major_version
<< ", expected: " << kCowVersionMajor
@@ -62,7 +63,7 @@
bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
uint64_t pos;
- auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+ auto xor_data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
// Skip the scratch space
if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
@@ -110,7 +111,7 @@
auto& current_op = ops_buffer->data()[current_op_num];
current_op_num++;
if (current_op.type == kCowXorOp) {
- data_loc->insert({current_op.new_block, data_pos});
+ xor_data_loc->insert({current_op.new_block, data_pos});
}
pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
@@ -190,9 +191,51 @@
}
}
- ops_ = ops_buffer;
- ops_->shrink_to_fit();
- data_loc_ = data_loc;
+ v2_ops_ = ops_buffer;
+ v2_ops_->shrink_to_fit();
+ xor_data_loc_ = xor_data_loc;
+ return true;
+}
+
+bool CowParserV2::Translate(TranslatedCowOps* out) {
+ out->ops = std::make_shared<std::vector<CowOperationV3>>(v2_ops_->size());
+
+ // Translate the operation buffer from on disk to in memory
+ for (size_t i = 0; i < out->ops->size(); i++) {
+ const auto& v2_op = v2_ops_->at(i);
+
+ auto& new_op = out->ops->at(i);
+ new_op.type = v2_op.type;
+ new_op.data_length = v2_op.data_length;
+
+ if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
+ return false;
+ }
+ new_op.new_block = v2_op.new_block;
+
+ uint64_t source_info = v2_op.source;
+ if (new_op.type != kCowLabelOp) {
+ source_info &= kCowOpSourceInfoDataMask;
+ if (source_info != v2_op.source) {
+ LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
+ return false;
+ }
+ }
+ if (v2_op.compression != kCowCompressNone) {
+ if (header_.compression_algorithm == kCowCompressNone) {
+ header_.compression_algorithm = v2_op.compression;
+ } else if (header_.compression_algorithm != v2_op.compression) {
+ LOG(ERROR) << "COW has mixed compression types which is not supported;"
+ << " previously saw " << header_.compression_algorithm << ", got "
+ << v2_op.compression << ", op: " << v2_op;
+ return false;
+ }
+ }
+ new_op.source_info = source_info;
+ }
+
+ out->header = header_;
return true;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
index f51ff88..f9ee2e5 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -17,36 +17,29 @@
#include <memory>
#include <optional>
-#include <unordered_map>
#include <vector>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
+#include <libsnapshot_cow/parser_base.h>
namespace android {
namespace snapshot {
-class CowParserV2 {
+class CowParserV2 final : public CowParserBase {
public:
- bool Parse(android::base::borrowed_fd fd, const CowHeader& header,
- std::optional<uint64_t> label = {});
+ bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+ std::optional<uint64_t> label = {}) override;
+ bool Translate(TranslatedCowOps* out) override;
+ std::optional<CowFooter> footer() const override { return footer_; }
const CowHeader& header() const { return header_; }
- const std::optional<CowFooter>& footer() const { return footer_; }
- std::shared_ptr<std::vector<CowOperationV2>> ops() { return ops_; }
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
- uint64_t fd_size() const { return fd_size_; }
- const std::optional<uint64_t>& last_label() const { return last_label_; }
+ std::shared_ptr<std::vector<CowOperationV2>> get_v2ops() { return v2_ops_; }
private:
bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
-
- CowHeader header_ = {};
+ std::shared_ptr<std::vector<CowOperationV2>> v2_ops_;
std::optional<CowFooter> footer_;
- std::shared_ptr<std::vector<CowOperationV2>> ops_;
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
- uint64_t fd_size_;
- std::optional<uint64_t> last_label_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
new file mode 100644
index 0000000..52c6348
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "parser_v3.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+bool CowParserV3::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
+ auto pos = lseek(fd.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+ header_ = header;
+
+ if (header_.footer_size != 0) {
+ LOG(ERROR) << "Footer size isn't 0, read " << header_.footer_size;
+ return false;
+ }
+
+ if (header_.op_size != sizeof(CowOperationV3)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperationV3);
+ return false;
+ }
+ if (header_.cluster_ops != 0) {
+ LOG(ERROR) << "Cluster ops not supported in v3";
+ return false;
+ }
+
+ if (header_.prefix.major_version != 3 || header_.prefix.minor_version != 0) {
+ LOG(ERROR) << "Header version mismatch, "
+ << "major version: " << header_.prefix.major_version
+ << ", expected: " << kCowVersionMajor
+ << ", minor version: " << header_.prefix.minor_version
+ << ", expected: " << kCowVersionMinor;
+ return false;
+ }
+
+ std::optional<uint32_t> op_index = header_.op_count;
+ if (label) {
+ if (!ReadResumeBuffer(fd)) {
+ PLOG(ERROR) << "Failed to read resume buffer";
+ return false;
+ }
+ op_index = FindResumeOp(label.value());
+ if (op_index == std::nullopt) {
+ LOG(ERROR) << "failed to get op index from given label: " << label.value();
+ return false;
+ }
+ }
+
+ return ParseOps(fd, op_index.value());
+}
+
+bool CowParserV3::ReadResumeBuffer(borrowed_fd fd) {
+ resume_points_ = std::make_shared<std::vector<ResumePoint>>(header_.resume_point_count);
+
+ return android::base::ReadFullyAtOffset(fd, resume_points_->data(),
+ header_.resume_point_count * sizeof(ResumePoint),
+ header_.prefix.header_size + header_.buffer_size);
+}
+
+std::optional<uint32_t> CowParserV3::FindResumeOp(const uint64_t label) {
+ for (auto& resume_point : *resume_points_) {
+ if (resume_point.label == label) {
+ return resume_point.op_index;
+ }
+ }
+ LOG(ERROR) << "failed to find label: " << label << "from following labels";
+ LOG(ERROR) << android::base::Join(*resume_points_, " ");
+
+ return std::nullopt;
+}
+
+bool CowParserV3::ParseOps(borrowed_fd fd, const uint32_t op_index) {
+ ops_ = std::make_shared<std::vector<CowOperationV3>>();
+ ops_->resize(op_index);
+
+ // read beginning of operation buffer -> so op_index = 0
+ const off_t offset = GetOpOffset(0, header_);
+ if (!android::base::ReadFullyAtOffset(fd, ops_->data(), ops_->size() * sizeof(CowOperationV3),
+ offset)) {
+ PLOG(ERROR) << "read ops failed";
+ return false;
+ }
+
+ // fill out mapping of XOR op data location
+ uint64_t data_pos = GetDataOffset(header_);
+
+ xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+ for (auto op : *ops_) {
+ if (op.type == kCowXorOp) {
+ xor_data_loc_->insert({op.new_block, data_pos});
+ }
+ data_pos += op.data_length;
+ }
+ // :TODO: sequence buffer & resume buffer follow
+ // Once we implement labels, we'll have to discard unused ops and adjust
+ // the header as needed.
+
+ ops_->shrink_to_fit();
+
+ return true;
+}
+
+bool CowParserV3::Translate(TranslatedCowOps* out) {
+ out->ops = ops_;
+ out->header = header_;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
new file mode 100644
index 0000000..afc01af
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot_cow/parser_base.h>
+
+namespace android {
+namespace snapshot {
+
+class CowParserV3 final : public CowParserBase {
+ public:
+ bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+ std::optional<uint64_t> label = {}) override;
+ bool Translate(TranslatedCowOps* out) override;
+ std::shared_ptr<std::vector<ResumePoint>> resume_points() const { return resume_points_; }
+
+ private:
+ bool ParseOps(android::base::borrowed_fd fd, const uint32_t op_index);
+ std::optional<uint32_t> FindResumeOp(const uint64_t label);
+ CowHeaderV3 header_ = {};
+ std::shared_ptr<std::vector<CowOperationV3>> ops_;
+ bool ReadResumeBuffer(android::base::borrowed_fd fd);
+ std::shared_ptr<std::vector<ResumePoint>> resume_points_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 35d74ba..2709059 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -1522,6 +1522,37 @@
ASSERT_TRUE(reader.GetFooter(&footer));
}
+TEST_F(CowTest, DecompressIncompressibleBlock) {
+ auto fd = OpenTestFile("incompressible_block", O_RDONLY);
+ ASSERT_GE(fd, 0);
+
+ std::string original;
+ ASSERT_TRUE(android::base::ReadFdToString(fd, &original)) << strerror(errno);
+ ASSERT_EQ(original.size(), 4096);
+
+ CowOptions options;
+ options.compression = "gz";
+ auto writer = CreateCowWriter(2, options, GetCowFd());
+ ASSERT_NE(writer, nullptr);
+ ASSERT_TRUE(writer->AddRawBlocks(0, original.data(), original.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ std::string block(original.size(), '\0');
+ ASSERT_EQ(iter->Get()->data_length, 4096);
+ ASSERT_TRUE(ReadData(reader, iter->Get(), block.data(), block.size()));
+
+ for (size_t i = 0; i < block.size(); i++) {
+ ASSERT_EQ(block[i], original[i]) << "mismatch at byte " << i;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index cc8dd83..c41e07c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -37,7 +37,7 @@
namespace android {
namespace snapshot {
-class CowOperationV3Test : public ::testing::Test {
+class CowTestV3 : public ::testing::Test {
protected:
virtual void SetUp() override {
cow_ = std::make_unique<TemporaryFile>();
@@ -51,7 +51,12 @@
std::unique_ptr<TemporaryFile> cow_;
};
-TEST_F(CowOperationV3Test, CowHeaderV2Test) {
+// Helper to check read sizes.
+static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
+ return reader.ReadData(op, buffer, size) == size;
+}
+
+TEST_F(CowTestV3, CowHeaderV2Test) {
CowOptions options;
options.cluster_ops = 5;
options.num_merge_ops = 1;
@@ -67,11 +72,415 @@
const auto& header = reader.GetHeader();
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
- ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
- ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.prefix.major_version, 2);
+ ASSERT_EQ(header.prefix.minor_version, 0);
ASSERT_EQ(header.block_size, options.block_size);
ASSERT_EQ(header.cluster_ops, options.cluster_ops);
}
+TEST_F(CowTestV3, Header) {
+ CowOptions options;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, 0);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.cluster_ops, 0);
+}
+
+TEST_F(CowTestV3, MaxOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_FALSE(writer->AddZeroBlocks(1, 21));
+ ASSERT_FALSE(writer->AddZeroBlocks(1, 1));
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));
+
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_EQ(reader.header_v3().op_count, 20);
+}
+
+TEST_F(CowTestV3, ZeroOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_TRUE(writer->AddZeroBlocks(1, 2));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_EQ(reader.header_v3().op_count, 2);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 1);
+ ASSERT_EQ(op->source_info, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 2);
+ ASSERT_EQ(op->source_info, 0);
+}
+
+TEST_F(CowTestV3, ReplaceOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ options.scratch_space = false;
+ 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(5, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.op_count, 1);
+
+ 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, 4096);
+ ASSERT_EQ(op->new_block, 5);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveReplaceOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ options.scratch_space = false;
+ 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);
+ }
+
+ ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.op_count, 5);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+ std::string sink(data.size(), '\0');
+
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->data_length, options.block_size);
+ ASSERT_EQ(op->new_block, 5 + i);
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ ASSERT_EQ(i, 5);
+}
+
+TEST_F(CowTestV3, CopyOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ ASSERT_TRUE(writer->AddCopy(10, 1000, 100));
+ ASSERT_TRUE(writer->Finalize());
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+ iter->Next();
+ i += 1;
+ }
+
+ ASSERT_EQ(i, 100);
+}
+
+TEST_F(CowTestV3, XorOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data = "This is test data-1. Testing xor";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 1);
+
+ 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, kCowXorOp);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveXorOp) {
+ CowOptions options;
+ 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);
+ }
+
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 5);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ std::string sink(data.size(), '\0');
+ size_t i = 0;
+
+ while (!iter->AtEnd()) {
+ 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(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ ASSERT_EQ(i, 5);
+}
+
+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() % 4);
+ }
+
+ ASSERT_TRUE(writer->AddZeroBlocks(10, 5));
+ ASSERT_TRUE(writer->AddCopy(15, 3, 5));
+ ASSERT_TRUE(writer->AddRawBlocks(18, data.data(), data.size()));
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.buffer_size, BUFFER_REGION_DEFAULT_SIZE);
+ ASSERT_EQ(header.op_count, 20);
+ ASSERT_EQ(header.op_count_max, 100);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->new_block, 10 + i);
+ iter->Next();
+ i++;
+ }
+ i = 0;
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->new_block, 15 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 3 + i);
+ iter->Next();
+ i++;
+ }
+ i = 0;
+ std::string sink(data.size(), '\0');
+
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->new_block, 18 + i);
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ i = 0;
+ std::fill(sink.begin(), sink.end(), '\0');
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->new_block, 50 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314 + (i * options.block_size)); // 4096 * 24 + 10
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ 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());
+}
+
+TEST_F(CowTestV3, ResumePointTest) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 15));
+ ASSERT_TRUE(writer->AddLabel(0));
+ ASSERT_TRUE(writer->AddZeroBlocks(15, 15));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 30);
+
+ CowWriterV3 second_writer(options, GetCowFd());
+ ASSERT_TRUE(second_writer.Initialize(0));
+ ASSERT_TRUE(second_writer.Finalize());
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 15);
+}
} // namespace snapshot
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
index 709b248..5274456 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -62,6 +62,8 @@
bool InitFd();
bool ValidateNewBlock(uint64_t new_block);
+ bool IsEstimating() const { return is_dev_null_; }
+
CowOptions options_;
android::base::unique_fd fd_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 83a9b1b..37324c7 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -273,10 +273,11 @@
if (!ReadCowHeader(fd_, &header_v3)) {
return false;
}
+
header_ = header_v3;
CowParserV2 parser;
- if (!parser.Parse(fd_, header_, {label})) {
+ if (!parser.Parse(fd_, header_v3, {label})) {
return false;
}
if (header_.prefix.major_version > 2) {
@@ -292,7 +293,7 @@
footer_.op.num_ops = 0;
InitPos();
- for (const auto& op : *parser.ops()) {
+ for (const auto& op : *parser.get_v2ops()) {
AddOperation(op);
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 5ae5f19..6883c5e 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -34,6 +34,7 @@
#include <zlib.h>
#include <fcntl.h>
+#include <libsnapshot_cow/parser_v3.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -77,8 +78,10 @@
// 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_.resume_buffer_size = 0;
- header_.op_buffer_size = 0;
+ header_.resume_point_count = 0;
+ header_.resume_point_max = kNumResumePoints;
+ header_.op_count = 0;
+ header_.op_count_max = 0;
header_.compression_algorithm = kCowCompressNone;
return;
}
@@ -96,6 +99,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];
@@ -107,6 +112,7 @@
}
compression_.algorithm = *algorithm;
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
return true;
}
@@ -116,11 +122,14 @@
if (!InitFd() || !ParseOptions()) {
return false;
}
-
- CHECK(!label.has_value());
-
- if (!OpenForWrite()) {
- return false;
+ if (!label) {
+ if (!OpenForWrite()) {
+ return false;
+ }
+ } else {
+ if (!OpenForAppend(*label)) {
+ return false;
+ }
}
return true;
@@ -154,48 +163,155 @@
return false;
}
}
+ header_.op_count_max = options_.op_count_max;
+ resume_points_ = std::make_shared<std::vector<ResumePoint>>();
if (!Sync()) {
LOG(ERROR) << "Header sync failed";
return false;
}
+ next_data_pos_ = GetDataOffset(header_);
+ return true;
+}
- next_op_pos_ = 0;
- next_data_pos_ = 0;
+bool CowWriterV3::OpenForAppend(uint64_t label) {
+ CowHeaderV3 header_v3;
+ if (!ReadCowHeader(fd_, &header_v3)) {
+ LOG(ERROR) << "Couldn't read Cow Header";
+ return false;
+ }
+
+ header_ = header_v3;
+
+ CHECK(label >= 0);
+ CowParserV3 parser;
+ if (!parser.Parse(fd_, header_, label)) {
+ PLOG(ERROR) << "unable to parse with given label: " << label;
+ return false;
+ }
+
+ resume_points_ = parser.resume_points();
+ options_.block_size = header_.block_size;
+ next_data_pos_ = GetDataOffset(header_);
+
+ TranslatedCowOps ops;
+ parser.Translate(&ops);
+ header_.op_count = ops.ops->size();
+
+ for (const auto& op : *ops.ops) {
+ next_data_pos_ += op.data_length;
+ }
return true;
}
bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (new_block || old_block || num_blocks) return false;
- return false;
+ for (size_t i = 0; i < num_blocks; i++) {
+ CowOperationV3 op = {};
+ op.type = kCowCopyOp;
+ op.new_block = new_block + i;
+ op.source_info = old_block + i;
+ if (!WriteOperation(op)) {
+ return false;
+ }
+ }
+
+ return true;
}
bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
-
- if (new_block_start || data || size) return false;
- return false;
+ return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
}
bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
uint32_t old_block, uint16_t offset) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (new_block_start || old_block || offset || data || size) return false;
- return false;
+ return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+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 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;
+ if (type == kCowXorOp) {
+ op.source_info = (old_block + i) * header_.block_size + offset;
+ } else {
+ op.source_info = next_data_pos_;
+ }
+ 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;
+ }
+ }
+
+ return true;
}
bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (new_block_start && num_blocks) return false;
- return false;
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ CowOperationV3 op;
+ op.type = kCowZeroOp;
+ op.data_length = 0;
+ op.new_block = new_block_start + i;
+ op.source_info = 0;
+ if (!WriteOperation(op)) {
+ return false;
+ }
+ }
+ return true;
}
bool CowWriterV3::EmitLabel(uint64_t label) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (label) return false;
- return false;
+ // remove all labels greater than this current one. we want to avoid the situation of adding
+ // in
+ // duplicate labels with differing op values
+ auto remove_if_callback = [&](const auto& resume_point) -> bool {
+ if (resume_point.label >= label) return true;
+ return false;
+ };
+ resume_points_->erase(
+ std::remove_if(resume_points_->begin(), resume_points_->end(), remove_if_callback),
+ resume_points_->end());
+
+ resume_points_->push_back({label, header_.op_count});
+ header_.resume_point_count++;
+ // remove the oldest resume point if resume_buffer is full
+ while (resume_points_->size() > header_.resume_point_max) {
+ resume_points_->erase(resume_points_->begin());
+ }
+
+ CHECK_LE(resume_points_->size(), header_.resume_point_max);
+
+ if (!android::base::WriteFullyAtOffset(fd_, resume_points_->data(),
+ resume_points_->size() * sizeof(ResumePoint),
+ GetResumeOffset(header_))) {
+ PLOG(ERROR) << "writing resume buffer failed";
+ return false;
+ }
+ return Sync();
}
bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
@@ -204,15 +320,49 @@
return false;
}
+bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
+ if (IsEstimating()) {
+ header_.op_count++;
+ header_.op_count_max++;
+ next_data_pos_ += op.data_length;
+ return true;
+ }
+
+ if (header_.op_count + 1 > header_.op_count_max) {
+ LOG(ERROR) << "Maximum number of ops reached: " << header_.op_count_max;
+ return false;
+ }
+
+ const off_t offset = GetOpOffset(header_.op_count, header_);
+ if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) {
+ PLOG(ERROR) << "write failed for " << op << " at " << offset;
+ return false;
+ }
+ if (data && size > 0) {
+ if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
+ PLOG(ERROR) << "write failed for data of size: " << size
+ << " at offset: " << next_data_pos_;
+ return false;
+ }
+ }
+ header_.op_count++;
+ next_data_pos_ += op.data_length;
+ next_op_pos_ += sizeof(CowOperationV3);
+
+ return true;
+}
+
bool CowWriterV3::Finalize() {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- return false;
+ CHECK_GE(header_.prefix.header_size, sizeof(CowHeaderV3));
+ CHECK_LE(header_.prefix.header_size, sizeof(header_));
+ if (!android::base::WriteFullyAtOffset(fd_, &header_, header_.prefix.header_size, 0)) {
+ return false;
+ }
+ return Sync();
}
uint64_t CowWriterV3::GetCowSize() {
- LOG(ERROR) << __LINE__ << " " << __FILE__
- << " <- Get Cow Size function here should never be called";
- return 0;
+ return next_data_pos_;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index 2a88a12..3dfc33c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -14,7 +14,8 @@
#pragma once
-#include <future>
+#include <android-base/logging.h>
+
#include "writer_base.h"
namespace android {
@@ -42,16 +43,28 @@
void SetupHeaders();
bool ParseOptions();
bool OpenForWrite();
+ bool OpenForAppend(uint64_t label);
+ 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);
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_;
+ // Resume points contain a laebl + cow_op_index.
+ std::shared_ptr<std::vector<ResumePoint>> resume_points_;
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
int num_compress_threads_ = 1;
};
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index eb4beb7..e91e3b7 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3513,6 +3513,11 @@
return Return::Error();
}
+ if (!android::fs_mgr::WaitForFile(cow_path, 6s)) {
+ LOG(ERROR) << "Timed out waiting for device to appear: " << cow_path;
+ return Return::Error();
+ }
+
if (it->second.using_snapuserd()) {
unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
@@ -4334,32 +4339,30 @@
}
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
- auto slot = GetCurrentSlot();
- if (slot == Slot::Target) {
- // Merge in-progress
- if (IsSnapuserdRequired()) {
+ // We cannot grab /metadata/ota lock here as this
+ // is in reboot path. See b/308900853
+ //
+ // Check if any of the partitions are mounted
+ // off dm-user block device. If so, then we are certain
+ // that OTA update in progress.
+ auto current_suffix = device_->GetSlotSuffix();
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ LOG(ERROR) << "No dm-enabled block device is found.";
+ return false;
+ }
+ for (auto& partition : dm_block_devices) {
+ std::string partition_name = partition.first + current_suffix;
+ DeviceMapper::TargetInfo snap_target;
+ if (!GetSingleTarget(partition_name, TableQuery::Status, &snap_target)) {
+ return false;
+ }
+ auto type = DeviceMapper::GetTargetType(snap_target.spec);
+ if (type == "user") {
return true;
}
}
-
- // Let's check more deeper to see if snapshots are mounted
- auto lock = LockExclusive();
- if (!lock) {
- return false;
- }
-
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock.get(), &snapshots)) {
- return false;
- }
-
- for (const auto& snapshot : snapshots) {
- // Active snapshot and daemon is alive
- if (IsSnapshotDevice(snapshot) && EnsureSnapuserdConnected(2s)) {
- return true;
- }
- }
-
return false;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
index d979e20..ffd7a4b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -51,11 +51,10 @@
std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
const std::string& misc_name, const std::string& cow_device_path,
const std::string& backing_device, const std::string& base_path_merge,
- std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
- bool perform_verification) {
+ std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring) {
auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
base_path_merge, opener, num_worker_threads,
- use_iouring, perform_verification);
+ use_iouring, perform_verification_);
if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
return nullptr;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
index b1605f0..ff6ee8f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -57,8 +57,7 @@
const std::string& backing_device,
const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener,
- int num_worker_threads, bool use_iouring,
- bool perform_verification) = 0;
+ int num_worker_threads, bool use_iouring) = 0;
// Start serving requests on a snapshot handler.
virtual bool StartHandler(const std::string& misc_name) = 0;
@@ -84,6 +83,9 @@
// Returns whether all snapshots have verified.
virtual bool GetVerificationStatus() = 0;
+
+ // Disable partition verification
+ virtual void DisableVerification() = 0;
};
class SnapshotHandlerManager final : public ISnapshotHandlerManager {
@@ -94,8 +96,7 @@
const std::string& backing_device,
const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener,
- int num_worker_threads, bool use_iouring,
- bool perform_verification) override;
+ int num_worker_threads, bool use_iouring) override;
bool StartHandler(const std::string& misc_name) override;
bool DeleteHandler(const std::string& misc_name) override;
bool InitiateMerge(const std::string& misc_name) override;
@@ -104,6 +105,7 @@
void TerminateMergeThreads() override;
double GetMergePercentage() override;
bool GetVerificationStatus() override;
+ void DisableVerification() override { perform_verification_ = false; }
private:
bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
@@ -128,6 +130,7 @@
int num_partitions_merge_complete_ = 0;
std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
android::base::unique_fd monitor_merge_event_fd_;
+ bool perform_verification_ = true;
};
} // namespace snapshot
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 8208d67..950d771 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -112,7 +112,7 @@
return false;
}
- if (!android::base::WriteFully(cow_fd_, &header, sizeof(CowHeader))) {
+ if (!android::base::WriteFully(cow_fd_, &header, header.prefix.header_size)) {
SNAP_PLOG(ERROR) << "Write to header failed";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 13b9a00..6eee357 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -360,16 +360,15 @@
num_worker_threads = 1;
}
- bool perform_verification = true;
- if (android::base::EndsWith(misc_name, "-init") || is_socket_present_) {
- perform_verification = false;
+ if (android::base::EndsWith(misc_name, "-init") || is_socket_present_ ||
+ (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {
+ handlers_->DisableVerification();
}
auto opener = block_server_factory_->CreateOpener(misc_name);
return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
- opener, num_worker_threads, io_uring_enabled_,
- perform_verification);
+ opener, num_worker_threads, io_uring_enabled_);
}
bool UserSnapshotServer::WaitForSocket() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index be28541..9926071 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -40,6 +40,8 @@
static constexpr uint32_t kMaxPacketSize = 512;
static constexpr uint8_t kMaxMergeThreads = 2;
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+ "/metadata/ota/snapshot-boot-without-slot-switch";
class UserSnapshotServer {
private:
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index bed71cf..73c3cbf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -627,9 +627,10 @@
void SnapuserdTest::InitCowDevice() {
auto factory = harness_->GetBlockServerFactory();
auto opener = factory->CreateOpener(system_device_ctrl_name_);
+ handlers_->DisableVerification();
auto handler =
handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
- base_dev_->GetPath(), opener, 1, GetParam(), false);
+ base_dev_->GetPath(), opener, 1, GetParam());
ASSERT_NE(handler, nullptr);
ASSERT_NE(handler->snapuserd(), nullptr);
#ifdef __ANDROID__
diff --git a/fs_mgr/libsnapshot/tools/testdata/incompressible_block b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
new file mode 100644
index 0000000..cc45cd0
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
Binary files differ
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
index f08cab2..91f235c 100644
--- a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -21,10 +21,8 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
-
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
@@ -38,9 +36,15 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public class VendorOverlayHostTest extends BaseHostJUnit4Test {
boolean wasRoot = false;
+ String vndkVersion = null;
@Before
public void setup() throws DeviceNotAvailableException {
+ vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+ Assume.assumeTrue(
+ "Vendor Overlay is disabled for VNDK deprecated devices",
+ vndkVersion != null && !vndkVersion.trim().isEmpty());
+
wasRoot = getDevice().isAdbRoot();
if (!wasRoot) {
Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
@@ -74,8 +78,6 @@
*/
@Test
public void testVendorOverlay() throws DeviceNotAvailableException {
- String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
-
// Create files and modify policy
CommandResult result = getDevice().executeShellV2Command(
"echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index f070776..662185c 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -108,9 +108,9 @@
static passwd static_passwd = {
.pw_name = static_name,
.pw_dir = static_dir,
- .pw_shell = static_shell,
.pw_uid = 0,
.pw_gid = 0,
+ .pw_shell = static_shell,
};
for (size_t n = 0; n < android_id_count; ++n) {
diff --git a/init/init.cpp b/init/init.cpp
index 40e2169..83cb78b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -108,6 +108,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::base::unique_fd;
using android::fs_mgr::AvbHandle;
using android::snapshot::SnapshotManager;
@@ -116,7 +117,8 @@
static int property_triggers_enabled = 0;
-static int signal_fd = -1;
+int sigchld_fd = -1;
+static int sigterm_fd = -1;
static int property_fd = -1;
struct PendingControlMessage {
@@ -713,8 +715,9 @@
HandlePowerctlMessage("shutdown,container");
}
-static void HandleSignalFd() {
+static void HandleSignalFd(int signal) {
signalfd_siginfo siginfo;
+ const int signal_fd = signal == SIGCHLD ? sigchld_fd : sigterm_fd;
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
if (bytes_read != sizeof(siginfo)) {
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
@@ -748,10 +751,28 @@
}
}
+static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, signal);
+ unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+ if (signal_fd == -1) {
+ return ErrnoError() << "failed to create signalfd for signal " << signal;
+ }
+
+ auto result = epoll->RegisterHandler(
+ signal_fd.get(), [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
+ if (!result.ok()) {
+ return result.error();
+ }
+
+ return signal_fd.release();
+}
+
static void InstallSignalFdHandler(Epoll* epoll) {
// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
// SIGCHLD when a child process stops or continues (b/77867680#comment9).
- const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
+ const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };
sigaction(SIGCHLD, &act, nullptr);
sigset_t mask;
@@ -774,14 +795,19 @@
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
- signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
- if (signal_fd == -1) {
- PLOG(FATAL) << "failed to create signalfd";
+ Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGCHLD);
+ if (!cs_result.ok()) {
+ PLOG(FATAL) << cs_result.error();
}
+ sigchld_fd = cs_result.value();
+ Service::SetSigchldFd(sigchld_fd);
- constexpr int flags = EPOLLIN | EPOLLPRI;
- if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
- LOG(FATAL) << result.error();
+ if (sigismember(&mask, SIGTERM)) {
+ Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
+ if (!cs_result.ok()) {
+ PLOG(FATAL) << cs_result.error();
+ }
+ sigterm_fd = cs_result.value();
}
}
@@ -1059,8 +1085,8 @@
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
- am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
+ am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
diff --git a/init/init.h b/init/init.h
index 9c7e918..b781167 100644
--- a/init/init.h
+++ b/init/init.h
@@ -28,6 +28,8 @@
namespace android {
namespace init {
+extern int sigchld_fd;
+
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index e89244f..6f8a4de 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -46,6 +46,13 @@
constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
+void AddPersistentProperty(const std::string& name, const std::string& value,
+ PersistentProperties* persistent_properties) {
+ auto persistent_property_record = persistent_properties->add_properties();
+ persistent_property_record->set_name(name);
+ persistent_property_record->set_value(value);
+}
+
Result<PersistentProperties> LoadLegacyPersistentProperties() {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
if (!dir) {
@@ -146,13 +153,6 @@
} // namespace
-void AddPersistentProperty(const std::string& name, const std::string& value,
- PersistentProperties* persistent_properties) {
- auto persistent_property_record = persistent_properties->add_properties();
- persistent_property_record->set_name(name);
- persistent_property_record->set_value(value);
-}
-
Result<PersistentProperties> LoadPersistentPropertyFile() {
auto file_contents = ReadPersistentPropertyFile();
if (!file_contents.ok()) return file_contents.error();
@@ -266,8 +266,57 @@
}
}
- return *persistent_properties;
+ // loop over to find all staged props
+ auto const staged_prefix = std::string_view("next_boot.");
+ auto staged_props = std::unordered_map<std::string, std::string>();
+ for (const auto& property_record : persistent_properties->properties()) {
+ auto const& prop_name = property_record.name();
+ auto const& prop_value = property_record.value();
+ if (StartsWith(prop_name, staged_prefix)) {
+ auto actual_prop_name = prop_name.substr(staged_prefix.size());
+ staged_props[actual_prop_name] = prop_value;
+ }
+ }
+
+ if (staged_props.empty()) {
+ return *persistent_properties;
+ }
+
+ // if has staging, apply staging and perserve the original prop order
+ PersistentProperties updated_persistent_properties;
+ for (const auto& property_record : persistent_properties->properties()) {
+ auto const& prop_name = property_record.name();
+ auto const& prop_value = property_record.value();
+
+ // don't include staged props anymore
+ if (StartsWith(prop_name, staged_prefix)) {
+ continue;
+ }
+
+ auto iter = staged_props.find(prop_name);
+ if (iter != staged_props.end()) {
+ AddPersistentProperty(prop_name, iter->second, &updated_persistent_properties);
+ staged_props.erase(iter);
+ } else {
+ AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
+ }
+ }
+
+ // add any additional staged props
+ for (auto const& [prop_name, prop_value] : staged_props) {
+ AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
+ }
+
+ // write current updated persist prop file
+ auto result = WritePersistentPropertyFile(updated_persistent_properties);
+ if (!result.ok()) {
+ LOG(ERROR) << "Could not store persistent property: " << result.error();
+ }
+
+ return updated_persistent_properties;
}
+
+
} // namespace init
} // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
index 7e9d438..11083da 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -25,8 +25,6 @@
namespace android {
namespace init {
-void AddPersistentProperty(const std::string& name, const std::string& value,
- PersistentProperties* persistent_properties);
PersistentProperties LoadPersistentProperties();
void WritePersistentProperty(const std::string& name, const std::string& value);
PersistentProperties LoadPersistentPropertiesFromMemory();
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index e5d26db..5763050 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -178,5 +178,37 @@
EXPECT_FALSE(it == read_back_properties.properties().end());
}
+TEST(persistent_properties, StagedPersistProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"next_boot.persist.test.numbers", "54321"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.test.numbers", "12345"},
+ {"next_boot.persist.test.extra", "abc"},
+ };
+
+ ASSERT_RESULT_OK(
+ WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+ std::vector<std::pair<std::string, std::string>> expected_persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.test.numbers", "54321"},
+ {"persist.test.extra", "abc"},
+ };
+
+ // lock down that staged props are applied
+ auto first_read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(expected_persistent_properties, first_read_back_properties);
+
+ // lock down that other props are not overwritten
+ auto second_read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(expected_persistent_properties, second_read_back_properties);
+}
+
} // namespace init
} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index b08a904..bd74358 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -76,6 +76,10 @@
#include "system/core/init/property_service.pb.h"
#include "util.h"
+static constexpr char APPCOMPAT_OVERRIDE_PROP_FOLDERNAME[] =
+ "/dev/__properties__/appcompat_override";
+static constexpr char APPCOMPAT_OVERRIDE_PROP_TREE_FILE[] =
+ "/dev/__properties__/appcompat_override/property_info";
using namespace std::literals;
using android::base::ErrnoError;
@@ -1279,11 +1283,17 @@
return;
}
- constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
- if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
+ if (!WriteStringToFile(serialized_contexts, PROP_TREE_FILE, 0444, 0, 0, false)) {
PLOG(ERROR) << "Unable to write serialized property infos to file";
}
- selinux_android_restorecon(kPropertyInfosPath, 0);
+ selinux_android_restorecon(PROP_TREE_FILE, 0);
+
+ mkdir(APPCOMPAT_OVERRIDE_PROP_FOLDERNAME, S_IRWXU | S_IXGRP | S_IXOTH);
+ if (!WriteStringToFile(serialized_contexts, APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0444, 0, 0,
+ false)) {
+ PLOG(ERROR) << "Unable to write vendor overrides to file";
+ }
+ selinux_android_restorecon(APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0);
}
static void ExportKernelBootProps() {
@@ -1396,33 +1406,12 @@
switch (init_message.msg_case()) {
case InitMessage::kLoadPersistentProperties: {
load_override_properties();
- // Read persistent properties after all default values have been loaded.
- // Apply staged and persistent properties
- bool has_staged_prop = false;
- auto const staged_prefix = std::string_view("next_boot.");
auto persistent_properties = LoadPersistentProperties();
for (const auto& property_record : persistent_properties.properties()) {
auto const& prop_name = property_record.name();
auto const& prop_value = property_record.value();
-
- if (StartsWith(prop_name, staged_prefix)) {
- has_staged_prop = true;
- auto actual_prop_name = prop_name.substr(staged_prefix.size());
- InitPropertySet(actual_prop_name, prop_value);
- } else {
- InitPropertySet(prop_name, prop_value);
- }
- }
-
- // Update persist prop file if there are staged props
- if (has_staged_prop) {
- PersistentProperties props = LoadPersistentPropertiesFromMemory();
- // write current updated persist prop file
- auto result = WritePersistentPropertyFile(props);
- if (!result.ok()) {
- LOG(ERROR) << "Could not store persistent property: " << result.error();
- }
+ InitPropertySet(prop_name, prop_value);
}
// Apply debug ramdisk special settings after persistent properties are loaded.
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 3351c4c..5757922 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -563,7 +563,7 @@
}
}
if (timeout > 0ms) {
- WaitToBeReaped(pids, timeout);
+ WaitToBeReaped(sigchld_fd, pids, timeout);
} else {
// Even if we don't to wait for services to stop, we still optimistically reap zombies.
ReapAnyOutstandingChildren();
diff --git a/init/service.cpp b/init/service.cpp
index 5e900ee..d351a8f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -136,6 +136,7 @@
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
+int Service::sigchld_fd_ = -1;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::string& filename, const std::vector<std::string>& args)
@@ -194,28 +195,20 @@
}
}
-void Service::KillProcessGroup(int signal, bool report_oneshot) {
- // If we've already seen a successful result from killProcessGroup*(), then we have removed
- // the cgroup already and calling these functions a second time will simply result in an error.
- // This is true regardless of which signal was sent.
- // These functions handle their own logging, so no additional logging is needed.
- if (!process_cgroup_empty_) {
+void Service::KillProcessGroup(int signal) {
+ // Always attempt the process kill if process is still running.
+ // Cgroup clean up routines are idempotent. It's safe to call
+ // killProcessGroup repeatedly. During shutdown, `init` will
+ // call this function to send SIGTERM/SIGKILL to all processes.
+ // These signals must be sent for a successful shutdown.
+ if (!process_cgroup_empty_ || IsRunning()) {
LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
<< ") process group...";
- int max_processes = 0;
int r;
if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid(), pid_, signal, &max_processes);
+ r = killProcessGroupOnce(uid(), pid_, signal);
} else {
- r = killProcessGroup(uid(), pid_, signal, &max_processes);
- }
-
- if (report_oneshot && max_processes > 0) {
- LOG(WARNING)
- << "Killed " << max_processes
- << " additional processes from a oneshot process group for service '" << name_
- << "'. This is new behavior, previously child processes would not be killed in "
- "this case.";
+ r = killProcessGroup(uid(), pid_, signal);
}
if (r == 0) process_cgroup_empty_ = true;
@@ -265,7 +258,7 @@
void Service::Reap(const siginfo_t& siginfo) {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
- KillProcessGroup(SIGKILL, false);
+ KillProcessGroup(SIGKILL);
} else {
// Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
// kill the process group in this case.
@@ -273,7 +266,7 @@
// The new behavior in Android R is to kill these process groups in all cases. The
// 'true' parameter instructions KillProcessGroup() to report a warning message where it
// detects a difference in behavior has occurred.
- KillProcessGroup(SIGKILL, true);
+ KillProcessGroup(SIGKILL);
}
}
diff --git a/init/service.h b/init/service.h
index 9f09cef..13c8b5f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -156,11 +156,12 @@
const Subcontext* subcontext() const { return subcontext_; }
const std::string& filename() const { return filename_; }
void set_filename(const std::string& name) { filename_ = name; }
+ static void SetSigchldFd(int sigchld_fd) { sigchld_fd_ = sigchld_fd; }
private:
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
- void KillProcessGroup(int signal, bool report_oneshot = false);
+ void KillProcessGroup(int signal);
void SetProcessAttributesAndCaps(InterprocessFifo setsid_finished);
void ResetFlagsForStart();
Result<void> CheckConsole();
@@ -168,8 +169,10 @@
void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
InterprocessFifo setsid_finished);
void SetMountNamespace();
+
static unsigned long next_start_order_;
static bool is_exec_service_running_;
+ static int sigchld_fd_;
const std::string name_;
std::set<std::string> classnames_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index a1b2cc5..92e350b 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -179,8 +179,9 @@
if (!ParseInt(args[1], &service_->proc_attr_.priority,
static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
- ANDROID_PRIORITY_LOWEST);
+ return Errorf("process priority value must be range {} - {}",
+ static_cast<int>(ANDROID_PRIORITY_HIGHEST),
+ static_cast<int>(ANDROID_PRIORITY_LOWEST));
}
return {};
}
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index f8c501f..9d4c7c8 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -18,6 +18,7 @@
#include <signal.h>
#include <string.h>
+#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -31,6 +32,7 @@
#include <thread>
+#include "epoll.h"
#include "init.h"
#include "service.h"
#include "service_list.h"
@@ -121,8 +123,23 @@
}
}
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout) {
+static void DiscardSiginfo(int signal_fd) {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ LOG(WARNING) << "Unexpected: " << __func__ << " read " << bytes_read << " bytes instead of "
+ << sizeof(siginfo);
+ }
+}
+
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+ std::chrono::milliseconds timeout) {
Timer t;
+ Epoll epoll;
+ // The init process passes a valid sigchld_fd argument but unit tests do not.
+ if (sigchld_fd >= 0) {
+ epoll.RegisterHandler(sigchld_fd, [sigchld_fd]() { DiscardSiginfo(sigchld_fd); });
+ }
std::vector<pid_t> alive_pids(pids.begin(), pids.end());
while (!alive_pids.empty() && t.duration() < timeout) {
pid_t pid;
@@ -135,14 +152,18 @@
if (alive_pids.empty()) {
break;
}
- std::this_thread::sleep_for(50ms);
+ if (sigchld_fd >= 0) {
+ epoll.Wait(std::max(timeout - t.duration(), 0ms));
+ } else {
+ std::this_thread::sleep_for(50ms);
+ }
}
LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
<< alive_pids.size() << " of them still running";
- for (pid_t pid : pids) {
+ for (pid_t pid : alive_pids) {
std::string status = "(no-such-pid)";
ReadFileToString(StringPrintf("/proc/%d/status", pid), &status);
- LOG(INFO) << "Still running: " << pid << ' ' << status;
+ LOG(INFO) << "Still running: " << pid << '\n' << status;
}
}
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index fac1020..e07a7d6 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -25,7 +25,8 @@
void ReapAnyOutstandingChildren();
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout);
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+ std::chrono::milliseconds timeout);
} // namespace init
} // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index dbaeb93..a319c63 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,8 @@
__BEGIN_DECLS
-static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
+static constexpr const char* CGROUPV2_HIERARCHY_NAME = "cgroup2";
+[[deprecated]] static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
bool CgroupsAvailable();
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
@@ -67,14 +68,11 @@
// Return 0 and removes the cgroup if there are no longer any processes in it.
// Returns -1 in the case of an error occurring or if there are processes still running
// even after retrying for up to 200ms.
-// If max_processes is not nullptr, it returns the maximum number of processes seen in the cgroup
-// during the killing process. Note that this can be 0 if all processes from the process group have
-// already been terminated.
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroup(uid_t uid, int initialPid, int signal);
// Returns the same as killProcessGroup(), however it does not retry, which means
// that it only returns 0 in the case that the cgroup exists and it contains no processes.
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
// Sends the provided signal to all members of a process group, but does not wait for processes to
// exit, or for the cgroup to be removed. Callers should also ensure that killProcessGroup is called
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index cc2565f..d352f0e 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -129,7 +129,7 @@
}
if (!attr->GetPathForTask(tid, path)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return false;
}
@@ -213,7 +213,7 @@
return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
}
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
+static int RemoveCgroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
int ret = 0;
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
@@ -233,7 +233,7 @@
return ret;
}
-static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
+static bool RemoveUidCgroups(const std::string& uid_path, bool empty_only) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
bool empty = true;
if (uid != NULL) {
@@ -275,11 +275,11 @@
return empty;
}
-void removeAllProcessGroupsInternal(bool empty_only) {
+static void removeAllProcessGroupsInternal(bool empty_only) {
std::vector<std::string> cgroups;
std::string path, memcg_apps_path;
- if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
+ if (CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &path)) {
cgroups.push_back(path);
}
if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
@@ -302,7 +302,7 @@
}
auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- if (!RemoveUidProcessGroups(path, empty_only)) {
+ if (!RemoveUidCgroups(path, empty_only)) {
LOG(VERBOSE) << "Skip removing " << path;
continue;
}
@@ -455,29 +455,21 @@
return (!fd || feof(fd.get())) ? processes : -1;
}
-static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
- int* max_processes) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
CHECK_GE(uid, 0);
CHECK_GT(initialPid, 0);
std::string hierarchy_root_path;
if (CgroupsAvailable()) {
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
}
const char* cgroup = hierarchy_root_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- if (max_processes != nullptr) {
- *max_processes = 0;
- }
-
int retry = retries;
int processes;
while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
- if (max_processes != nullptr && processes > *max_processes) {
- *max_processes = processes;
- }
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (!CgroupsAvailable()) {
// makes no sense to retry, because there are no cgroup_procs file
@@ -519,12 +511,12 @@
}
// 400 retries correspond to 2 secs max timeout
- int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
+ int err = RemoveCgroup(cgroup, uid, initialPid, 400);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
std::string memcg_apps_path;
if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
- RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
+ RemoveCgroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
return -1;
}
}
@@ -540,18 +532,18 @@
}
}
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes) {
- return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/, max_processes);
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+ return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes) {
- return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
int sendSignalToProcessGroup(uid_t uid, int initialPid, int signal) {
std::string hierarchy_root_path;
if (CgroupsAvailable()) {
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
}
const char* cgroup = hierarchy_root_path.c_str();
return DoKillProcessGroupOnce(cgroup, uid, initialPid, signal);
@@ -609,7 +601,7 @@
CHECK_GT(initialPid, 0);
if (memControl && !UsePerAppMemcg()) {
- PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
+ LOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
return -EINVAL;
}
@@ -625,19 +617,19 @@
}
std::string cgroup;
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cgroup);
return createProcessGroupInternal(uid, initialPid, cgroup, true);
}
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
if (!isMemoryCgroupSupported()) {
- PLOG(ERROR) << "Memcg is not mounted.";
+ LOG(ERROR) << "Memcg is not mounted.";
return false;
}
std::string path;
if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
- PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+ LOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
return false;
}
@@ -672,4 +664,4 @@
}
return tp->IsValidForProcess(uid, pid);
-}
\ No newline at end of file
+}
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index fbeedf9..4e44c91 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -212,7 +212,7 @@
if (root.isMember("Cgroups2")) {
const Json::Value& cgroups2 = root["Cgroups2"];
std::string root_path = cgroups2["Path"].asString();
- MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_CONTROLLER_NAME, "", 2);
+ MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME, "", 2);
const Json::Value& childGroups = cgroups2["Controllers"];
for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {
@@ -358,7 +358,7 @@
const format::CgroupController* controller = descriptor.controller();
if (controller->version() == 2) {
- if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
+ if (!strcmp(controller->name(), CGROUPV2_HIERARCHY_NAME)) {
return MountV2CgroupController(descriptor);
} else {
return ActivateV2CgroupController(descriptor);
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index 99d819a..b17e695 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -116,7 +116,7 @@
}
bool GetPathForTask(int tid, std::string* path) const override {
#ifdef __ANDROID__
- CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+ CHECK(CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, path));
CHECK_GT(path->length(), 0);
if (path->rbegin()[0] != '/') {
*path += "/";
diff --git a/libutils/binder/RefBase.cpp b/libutils/binder/RefBase.cpp
index 4b0cc16..2d2e40b 100644
--- a/libutils/binder/RefBase.cpp
+++ b/libutils/binder/RefBase.cpp
@@ -55,14 +55,16 @@
// log all reference counting operations
#define PRINT_REFS 0
+#if !defined(ANDROID_UTILS_CALLSTACK_ENABLED)
#if defined(__linux__)
// CallStack is only supported on linux type platforms.
-#define CALLSTACK_ENABLED 1
+#define ANDROID_UTILS_CALLSTACK_ENABLED 1
#else
-#define CALLSTACK_ENABLED 0
-#endif
+#define ANDROID_UTILS_CALLSTACK_ENABLED 0
+#endif // defined(__linux__)
+#endif // !defined(ANDROID_UTILS_CALLSTACK_ENABLED)
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
#include "../../include/utils/CallStack.h"
#endif
@@ -230,7 +232,7 @@
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
@@ -244,7 +246,7 @@
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
@@ -252,7 +254,7 @@
}
if (dumpStack) {
ALOGE("above errors at:");
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
}
@@ -341,7 +343,7 @@
{
ref_entry* next;
const void* id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::CallStackUPtr stack;
#endif
int32_t ref;
@@ -358,7 +360,7 @@
// decrement the reference count.
ref->ref = mRef;
ref->id = id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
ref->stack = CallStack::getCurrent(2);
#endif
ref->next = *refs;
@@ -394,7 +396,7 @@
ref = ref->next;
}
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
}
@@ -422,7 +424,7 @@
snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
inc, refs->id, refs->ref);
out->append(buf);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
out->append(CallStack::stackToString("\t\t", refs->stack.get()));
#else
out->append("\t\t(call stacks disabled)");
@@ -788,7 +790,7 @@
"object.",
mRefs->mWeak.load(), this);
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
} else if (strongs != 0) {
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
index 0548021..65705ac 100644
--- a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -20,6 +20,8 @@
#include <stdint.h>
#include <stdlib.h>
+static constexpr char PROP_TREE_FILE[] = "/dev/__properties__/property_info";
+
namespace android {
namespace properties {
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index cc92c68..32057b4 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -76,9 +76,6 @@
error(1, errno, "couldn't stat %s", path);
}
- // /data/user/0 is a known safe symlink.
- if (strcmp("/data/user/0", path) == 0) return;
-
// Must be a real directory, not a symlink.
if (!S_ISDIR(st.st_mode)) {
error(1, 0, "%s not a directory: %o", path, st.st_mode);
@@ -191,27 +188,17 @@
}
// 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");
}
-
- // Handle a multi-user data path
- if (userId > 0) {
- free(info.data_dir);
- if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
- error(1, errno, "asprintf failed");
- }
- }
+ if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
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) {
@@ -231,6 +218,12 @@
error(1, 0, "package not debuggable: %s", pkgname);
}
+ // Ensure we have the right data path for the specific user.
+ free(info.data_dir);
+ if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+ error(1, errno, "asprintf failed");
+ }
+
// Check that the data directory path is valid.
check_data_path(pkgname, info.data_dir, userAppId);