Merge "rpc_binder: Change `trusty_tipc_fuzzer` to support multiple connections and messages" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5393e25..267571b 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -12,6 +12,7 @@
"-Wno-unused-argument",
"-Wno-unused-function",
"-Wno-nullability-completeness",
+ "-Wno-reorder-init-list",
"-Os",
"-fno-finite-loops",
"-DANDROID_DEBUGGABLE=0",
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index 0def8ae..3001ca1 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2006, 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.
+ */
+
.globl crash1
.type crash1, %function
crash1:
diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
index c56e19a..90ba9a1 100644
--- a/debuggerd/crasher/arm64/crashglue.S
+++ b/debuggerd/crasher/arm64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2013, 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.
+ */
+
.globl crash1
.type crash1, %function
crash1:
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
index f179e33..804a511 100644
--- a/debuggerd/crasher/riscv64/crashglue.S
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2022, 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.
+ */
+
.globl crash1
crash1:
.cfi_startproc
diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
index 453035b..fe7c648 100644
--- a/debuggerd/crasher/x86/crashglue.S
+++ b/debuggerd/crasher/x86/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2010, 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.
+ */
+
.globl crash1
crash1:
movl $0xa5a50000, %eax
diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
index c3d39c4..ae13aa7 100644
--- a/debuggerd/crasher/x86_64/crashglue.S
+++ b/debuggerd/crasher/x86_64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2010, 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.
+ */
+
.globl crash1
crash1:
movl $0xa5a50000, %eax
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 8241f0e..adf8738 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 0cb8e08..972a575 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -26,6 +26,7 @@
recvmsg: 1
recvfrom: 1
sysinfo: 1
+setsockopt: 1
process_vm_readv: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.riscv64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/fastboot/README.md b/fastboot/README.md
index 55583eb..28e623c 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -188,6 +188,21 @@
erase %s Erase a given partition (can only be used in conjunction)
with if-wipe -> eg. if-wipe erase cache
+Flashing Optimization:
+
+ After generating the list of tasks to execute, Fastboot will try and
+ optimize the flashing of the dynamic partitions by constructing an
+ optimized flash super task. Fastboot will explicitly pattern match the
+ following commands and try and concatenate it into this task. (doing so
+ will allow us to avoid the reboot into userspace fastbootd which takes
+ significant time)
+
+ //Optimizable Block
+ reboot fastboot
+ update-super ---> generate optimized flash super task
+ $FOR EACH {dynamic partition}
+ flash {dynamic partition}
+
## Client Variables
The "getvar:%s" command is used to read client variables which
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index e929f42..6de598f 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -639,6 +639,12 @@
return UpdateSuper(device, args[1], wipe);
}
+static bool IsLockedDsu() {
+ std::string active_dsu;
+ android::gsi::GetActiveDsu(&active_dsu);
+ return android::base::EndsWith(active_dsu, ".lock");
+}
+
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() != 2) {
return device->WriteFail("Invalid arguments");
@@ -653,6 +659,11 @@
return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
}
+ if ((args[1] == "wipe" || args[1] == "disable") && GetDeviceLockStatus() && IsLockedDsu()) {
+ // Block commands that modify the states of locked DSU
+ return device->WriteFail("Command not available on locked DSU/devices");
+ }
+
if (args[1] == "wipe") {
if (!android::gsi::UninstallGsi()) {
return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
@@ -661,6 +672,17 @@
if (!android::gsi::DisableGsi()) {
return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
}
+ } else if (args[1] == "status") {
+ std::string active_dsu;
+ if (!android::gsi::IsGsiRunning()) {
+ device->WriteInfo("Not running");
+ } else if (!android::gsi::GetActiveDsu(&active_dsu)) {
+ return device->WriteFail(strerror(errno));
+ } else {
+ device->WriteInfo("Running active DSU: " + active_dsu);
+ }
+ } else {
+ return device->WriteFail("Invalid arguments");
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6b6a982..0dc4e97 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -151,7 +151,8 @@
}
BootControlClient* FastbootDevice::boot1_1() const {
- if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
+ if (boot_control_hal_ &&
+ boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
return boot_control_hal_.get();
}
return nullptr;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8c607dd..56b90b9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -28,7 +28,6 @@
#include "fastboot.h"
-#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -44,12 +43,10 @@
#include <unistd.h>
#include <chrono>
-#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <regex>
-#include <sstream>
#include <string>
#include <thread>
#include <utility>
@@ -79,7 +76,6 @@
#include "fastboot_driver_interface.h"
#include "fs.h"
#include "storage.h"
-#include "super_flash_helper.h"
#include "task.h"
#include "tcp.h"
#include "transport.h"
@@ -93,7 +89,6 @@
using android::base::Split;
using android::base::Trim;
using android::base::unique_fd;
-using namespace std::string_literals;
using namespace std::placeholders;
#define FASTBOOT_INFO_VERSION 1
@@ -350,23 +345,21 @@
//
// The returned Transport is a singleton, so multiple calls to this function will return the same
// object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device(const char* local_serial, bool wait_for_device = true,
- bool announce = true) {
+static std::unique_ptr<Transport> open_device(const char* local_serial, bool wait_for_device = true,
+ bool announce = true) {
const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
while (true) {
if (network_serial.ok()) {
std::string error;
if (network_serial->protocol == Socket::Protocol::kTcp) {
- transport = tcp::Connect(network_serial->address, network_serial->port, &error)
- .release();
+ transport = tcp::Connect(network_serial->address, network_serial->port, &error);
} else if (network_serial->protocol == Socket::Protocol::kUdp) {
- transport = udp::Connect(network_serial->address, network_serial->port, &error)
- .release();
+ transport = udp::Connect(network_serial->address, network_serial->port, &error);
}
- if (transport == nullptr && announce) {
+ if (!transport && announce) {
LOG(ERROR) << "error: " << error;
}
} else if (network_serial.error().code() ==
@@ -378,12 +371,12 @@
Expect(network_serial);
}
- if (transport != nullptr) {
+ if (transport) {
return transport;
}
if (!wait_for_device) {
- return nullptr;
+ return transport;
}
if (announce) {
@@ -394,9 +387,9 @@
}
}
-static Transport* NetworkDeviceConnected(bool print = false) {
- Transport* transport = nullptr;
- Transport* result = nullptr;
+static std::unique_ptr<Transport> NetworkDeviceConnected(bool print = false) {
+ std::unique_ptr<Transport> transport;
+ std::unique_ptr<Transport> result;
ConnectedDevicesStorage storage;
std::set<std::string> devices;
@@ -409,11 +402,11 @@
transport = open_device(device.c_str(), false, false);
if (print) {
- PrintDevice(device.c_str(), transport == nullptr ? "offline" : "fastboot");
+ PrintDevice(device.c_str(), transport ? "offline" : "fastboot");
}
- if (transport != nullptr) {
- result = transport;
+ if (transport) {
+ result = std::move(transport);
}
}
@@ -431,21 +424,21 @@
//
// The returned Transport is a singleton, so multiple calls to this function will return the same
// object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device() {
+static std::unique_ptr<Transport> open_device() {
if (serial != nullptr) {
return open_device(serial);
}
bool announce = true;
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
while (true) {
transport = usb_open(match_fastboot(nullptr));
- if (transport != nullptr) {
+ if (transport) {
return transport;
}
transport = NetworkDeviceConnected();
- if (transport != nullptr) {
+ if (transport) {
return transport;
}
@@ -455,6 +448,8 @@
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
+
+ return transport;
}
static int Connect(int argc, char* argv[]) {
@@ -466,8 +461,7 @@
const char* local_serial = *argv;
Expect(ParseNetworkSerial(local_serial));
- const Transport* transport = open_device(local_serial, false);
- if (transport == nullptr) {
+ if (!open_device(local_serial, false)) {
return 1;
}
@@ -531,6 +525,7 @@
usb_open(list_devices_callback);
NetworkDeviceConnected(/* print */ true);
}
+
void syntax_error(const char* fmt, ...) {
fprintf(stderr, "fastboot: usage: ");
@@ -1190,9 +1185,10 @@
return partition_size;
}
-static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+static void copy_avb_footer(const ImageSource* source, const std::string& partition,
+ struct fastboot_buffer* buf) {
if (buf->sz < AVB_FOOTER_SIZE || is_logical(partition) ||
- should_flash_in_userspace(partition)) {
+ should_flash_in_userspace(source, partition)) {
return;
}
// If overflows and negative, it should be < buf->sz.
@@ -1249,9 +1245,9 @@
}
}
-static void flash_buf(const std::string& partition, struct fastboot_buffer* buf,
- const bool apply_vbmeta) {
- copy_avb_footer(partition, buf);
+static void flash_buf(const ImageSource* source, const std::string& partition,
+ struct fastboot_buffer* buf, const bool apply_vbmeta) {
+ copy_avb_footer(source, partition, buf);
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if (g_disable_verity || g_disable_verification) {
@@ -1285,7 +1281,7 @@
return current_slot;
}
-static int get_slot_count() {
+static int get_slot_count(fastboot::IFastBootDriver* fb) {
std::string var;
int count = 0;
if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
@@ -1295,8 +1291,8 @@
return count;
}
-bool supports_AB() {
- return get_slot_count() >= 2;
+bool supports_AB(fastboot::IFastBootDriver* fb) {
+ return get_slot_count(fb) >= 2;
}
// Given a current slot, this returns what the 'other' slot is.
@@ -1308,7 +1304,7 @@
}
static std::string get_other_slot(const std::string& current_slot) {
- return get_other_slot(current_slot, get_slot_count());
+ return get_other_slot(current_slot, get_slot_count(fb));
}
static std::string get_other_slot(int count) {
@@ -1316,7 +1312,7 @@
}
static std::string get_other_slot() {
- return get_other_slot(get_current_slot(), get_slot_count());
+ return get_other_slot(get_current_slot(), get_slot_count(fb));
}
static std::string verify_slot(const std::string& slot_name, bool allow_all) {
@@ -1325,7 +1321,7 @@
if (allow_all) {
return "all";
} else {
- int count = get_slot_count();
+ int count = get_slot_count(fb);
if (count > 0) {
return "a";
} else {
@@ -1334,7 +1330,7 @@
}
}
- int count = get_slot_count();
+ int count = get_slot_count(fb);
if (count == 0) die("Device does not support slots");
if (slot == "other") {
@@ -1407,7 +1403,7 @@
slot.c_str());
}
if (has_slot == "yes") {
- for (int i = 0; i < get_slot_count(); i++) {
+ for (int i = 0; i < get_slot_count(fb); i++) {
do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
}
} else {
@@ -1418,14 +1414,6 @@
}
}
-bool is_retrofit_device(fastboot::IFastBootDriver* fb) {
- std::string value;
- if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
- return false;
- }
- return android::base::StartsWith(value, "system_");
-}
-
// Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch
// the full image.
static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd,
@@ -1522,13 +1510,13 @@
fb->ResizePartition(pname, std::to_string(buf.image_size));
}
std::string flash_pname = repack_ramdisk(pname, &buf, fp->fb);
- flash_buf(flash_pname, &buf, apply_vbmeta);
+ flash_buf(fp->source.get(), flash_pname, &buf, apply_vbmeta);
}
// Sets slot_override as the active slot. If slot_override is blank,
// set current slot as active instead. This clears slot-unbootable.
static void set_active(const std::string& slot_override) {
- if (!supports_AB()) return;
+ if (!supports_AB(fb)) return;
if (slot_override != "") {
fb->SetActive(slot_override);
@@ -1547,9 +1535,7 @@
void reboot_to_userspace_fastboot() {
fb->RebootTo("fastboot");
-
- auto* old_transport = fb->set_transport(nullptr);
- delete old_transport;
+ fb->set_transport(nullptr);
// Give the current connection time to close.
std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -1678,7 +1664,7 @@
}
for (size_t i = 0; i < tasks->size(); i++) {
if (auto flash_task = tasks->at(i)->AsFlashTask()) {
- if (FlashTask::IsDynamicParitition(fp->source, flash_task)) {
+ if (FlashTask::IsDynamicParitition(fp->source.get(), flash_task)) {
if (!loc) {
loc = i;
}
@@ -1807,9 +1793,10 @@
tasks = CollectTasksFromImageList();
}
if (fp_->exclude_dynamic_partitions) {
- auto is_non_static_flash_task = [](const auto& task) -> bool {
+ auto is_non_static_flash_task = [&](const auto& task) -> bool {
if (auto flash_task = task->AsFlashTask()) {
- if (!should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
+ if (!should_flash_in_userspace(fp_->source.get(),
+ flash_task->GetPartitionAndSlot())) {
return false;
}
}
@@ -1845,7 +1832,7 @@
fp_->secondary_slot = get_other_slot();
}
if (fp_->secondary_slot == "") {
- if (supports_AB()) {
+ if (supports_AB(fb)) {
fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
}
fp_->skip_secondary = true;
@@ -1878,19 +1865,6 @@
// Sync the super partition. This will reboot to userspace fastboot if needed.
tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
- for (const auto& [image, slot] : os_images_) {
- // Retrofit devices have two super partitions, named super_a and super_b.
- // On these devices, secondary slots must be flashed as physical
- // partitions (otherwise they would not mount on first boot). To enforce
- // this, we delete any logical partitions for the "other" slot.
- if (is_retrofit_device(fp_->fb)) {
- std::string partition_name = image->part_name + "_"s + slot;
- if (image->IsSecondary() && should_flash_in_userspace(partition_name)) {
- fp_->fb->DeletePartition(partition_name);
- }
- tasks.emplace_back(std::make_unique<DeleteTask>(fp_, partition_name));
- }
- }
AddFlashTasks(os_images_, tasks);
@@ -1949,8 +1923,7 @@
if (error != 0) {
die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
}
- ZipImageSource zp = ZipImageSource(zip);
- fp->source = &zp;
+ fp->source.reset(new ZipImageSource(zip));
FlashAllTool tool(fp);
tool.Flash();
@@ -1971,8 +1944,7 @@
}
static void do_flashall(FlashingPlan* fp) {
- LocalImageSource s = LocalImageSource();
- fp->source = &s;
+ fp->source.reset(new LocalImageSource());
FlashAllTool tool(fp);
tool.Flash();
}
@@ -2088,7 +2060,8 @@
if (!load_buf_fd(std::move(fd), &buf, fp)) {
die("Cannot read image: %s", strerror(errno));
}
- flash_buf(partition, &buf, is_vbmeta_partition(partition));
+
+ flash_buf(fp->source.get(), partition, &buf, is_vbmeta_partition(partition));
return;
failed:
@@ -2102,18 +2075,26 @@
}
}
-bool should_flash_in_userspace(const std::string& partition_name) {
- if (!get_android_product_out()) {
+bool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name) {
+ if (!source) {
+ if (!get_android_product_out()) {
+ return false;
+ }
+ auto path = find_item_given_name("super_empty.img");
+ if (path.empty() || access(path.c_str(), R_OK)) {
+ return false;
+ }
+ auto metadata = android::fs_mgr::ReadFromImageFile(path);
+ if (!metadata) {
+ return false;
+ }
+ return should_flash_in_userspace(*metadata.get(), partition_name);
+ }
+ std::vector<char> contents;
+ if (!source->ReadFile("super_empty.img", &contents)) {
return false;
}
- auto path = find_item_given_name("super_empty.img");
- if (path.empty() || access(path.c_str(), R_OK)) {
- return false;
- }
- auto metadata = android::fs_mgr::ReadFromImageFile(path);
- if (!metadata) {
- return false;
- }
+ auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
return should_flash_in_userspace(*metadata.get(), partition_name);
}
@@ -2137,7 +2118,7 @@
if (metadata.block_devices.size() > 1) {
ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);
} else {
- auto image_path = temp_dir.path + "/"s + super_bdev_name + ".img";
+ auto image_path = std::string(temp_dir.path) + "/" + std::string(super_bdev_name) + ".img";
ok = WriteToImageFile(image_path, metadata, block_size, {}, true);
}
if (!ok) {
@@ -2156,7 +2137,7 @@
image_name = partition + ".img";
}
- auto image_path = temp_dir.path + "/"s + image_name;
+ auto image_path = std::string(temp_dir.path) + "/" + image_name;
auto flash = [&](const std::string& partition_name) {
do_flash(partition_name.c_str(), image_path.c_str(), false, fp);
};
@@ -2372,8 +2353,8 @@
return show_help();
}
- Transport* transport = open_device();
- if (transport == nullptr) {
+ std::unique_ptr<Transport> transport = open_device();
+ if (!transport) {
return 1;
}
fastboot::DriverCallbacks driver_callbacks = {
@@ -2383,7 +2364,7 @@
.text = TextMessage,
};
- fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+ fastboot::FastBootDriver fastboot_driver(std::move(transport), driver_callbacks, false);
fb = &fastboot_driver;
fp->fb = &fastboot_driver;
@@ -2574,14 +2555,12 @@
std::make_unique<ResizeTask>(fp.get(), partition, size, fp->slot_override);
resize_task->Run();
} else if (command == "gsi") {
- std::string arg = next_arg(&args);
- if (arg == "wipe") {
- fb->RawCommand("gsi:wipe", "wiping GSI");
- } else if (arg == "disable") {
- fb->RawCommand("gsi:disable", "disabling GSI");
- } else {
- syntax_error("expected 'wipe' or 'disable'");
+ if (args.empty()) syntax_error("invalid gsi command");
+ std::string cmd("gsi");
+ while (!args.empty()) {
+ cmd += ":" + next_arg(&args);
}
+ fb->RawCommand(cmd, "");
} else if (command == "wipe-super") {
std::string image;
if (args.empty()) {
@@ -2628,9 +2607,6 @@
}
fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
- auto* old_transport = fb->set_transport(nullptr);
- delete old_transport;
-
return 0;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 75b8d29..2c40890 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -28,11 +28,10 @@
#pragma once
#include <functional>
+#include <memory>
#include <string>
-#include "fastboot_driver.h"
#include "fastboot_driver_interface.h"
#include "filesystem.h"
-#include "super_flash_helper.h"
#include "task.h"
#include "util.h"
@@ -91,7 +90,7 @@
// If the image uses the default slot, or the user specified "all", then
// the paired string will be empty. If the image requests a specific slot
// (for example, system_other) it is specified instead.
- ImageSource* source;
+ std::unique_ptr<ImageSource> source;
bool wants_wipe = false;
bool skip_reboot = false;
bool wants_set_active = false;
@@ -150,7 +149,7 @@
};
char* get_android_product_out();
-bool should_flash_in_userspace(const std::string& partition_name);
+bool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name);
bool is_userspace_fastboot();
void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
const FlashingPlan* fp);
@@ -183,13 +182,12 @@
};
Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
-bool supports_AB();
std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);
std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
-bool is_retrofit_device(fastboot::IFastBootDriver* fb);
+bool supports_AB(fastboot::IFastBootDriver* fb);
bool is_logical(const std::string& partition);
void fb_perform_format(const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 9770ab2..e5ef66b 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -58,9 +58,10 @@
namespace fastboot {
/*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
+FastBootDriver::FastBootDriver(std::unique_ptr<Transport> transport,
+ DriverCallbacks driver_callbacks,
bool no_checks)
- : transport_(transport),
+ : transport_(std::move(transport)),
prolog_(std::move(driver_callbacks.prolog)),
epilog_(std::move(driver_callbacks.epilog)),
info_(std::move(driver_callbacks.info)),
@@ -627,9 +628,8 @@
return 0;
}
-Transport* FastBootDriver::set_transport(Transport* transport) {
- std::swap(transport_, transport);
- return transport;
+void FastBootDriver::set_transport(std::unique_ptr<Transport> transport) {
+ transport_ = std::move(transport);
}
} // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 8774ead..49cebd9 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -27,9 +27,9 @@
*/
#pragma once
#include <cstdlib>
-#include <deque>
#include <functional>
#include <limits>
+#include <memory>
#include <string>
#include <vector>
@@ -37,10 +37,8 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <bootimg.h>
-#include <inttypes.h>
#include <sparse/sparse.h>
-#include "constants.h"
#include "fastboot_driver_interface.h"
#include "transport.h"
@@ -63,7 +61,7 @@
static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
- FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
+ FastBootDriver(std::unique_ptr<Transport> transport, DriverCallbacks driver_callbacks = {},
bool no_checks = false);
~FastBootDriver();
@@ -124,9 +122,7 @@
std::string Error();
RetCode WaitForDisconnect() override;
- // Note: set_transport will return the previous transport.
- Transport* set_transport(Transport* transport);
- Transport* transport() const { return transport_; }
+ void set_transport(std::unique_ptr<Transport> transport);
RetCode RawCommand(const std::string& cmd, const std::string& message,
std::string* response = nullptr, std::vector<std::string>* info = nullptr,
@@ -143,7 +139,7 @@
std::string ErrnoStr(const std::string& msg);
- Transport* transport_;
+ std::unique_ptr<Transport> transport_;
private:
RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
diff --git a/fastboot/fastboot_driver_test.cpp b/fastboot/fastboot_driver_test.cpp
index 6f6cf8c..d2033b0 100644
--- a/fastboot/fastboot_driver_test.cpp
+++ b/fastboot/fastboot_driver_test.cpp
@@ -16,6 +16,7 @@
#include "fastboot_driver.h"
+#include <memory>
#include <optional>
#include <gtest/gtest.h>
@@ -30,13 +31,14 @@
};
TEST_F(DriverTest, GetVar) {
- MockTransport transport;
- FastBootDriver driver(&transport);
+ std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+ MockTransport* transport = transport_pointer.get();
+ FastBootDriver driver(std::move(transport_pointer));
- EXPECT_CALL(transport, Write(_, _))
+ EXPECT_CALL(*transport, Write(_, _))
.With(AllArgs(RawData("getvar:version")))
.WillOnce(ReturnArg<1>());
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
std::string output;
ASSERT_EQ(driver.GetVar("version", &output), SUCCESS) << driver.Error();
@@ -44,14 +46,15 @@
}
TEST_F(DriverTest, InfoMessage) {
- MockTransport transport;
- FastBootDriver driver(&transport);
+ std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+ MockTransport* transport = transport_pointer.get();
+ FastBootDriver driver(std::move(transport_pointer));
- EXPECT_CALL(transport, Write(_, _))
+ EXPECT_CALL(*transport, Write(_, _))
.With(AllArgs(RawData("oem dmesg")))
.WillOnce(ReturnArg<1>());
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
std::vector<std::string> info;
ASSERT_EQ(driver.RawCommand("oem dmesg", "", nullptr, &info), SUCCESS) << driver.Error();
@@ -60,28 +63,29 @@
}
TEST_F(DriverTest, TextMessage) {
- MockTransport transport;
std::string text;
+ std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+ MockTransport* transport = transport_pointer.get();
DriverCallbacks callbacks{[](const std::string&) {}, [](int) {}, [](const std::string&) {},
[&text](const std::string& extra_text) { text += extra_text; }};
- FastBootDriver driver(&transport, callbacks);
+ FastBootDriver driver(std::move(transport_pointer), callbacks);
- EXPECT_CALL(transport, Write(_, _))
+ EXPECT_CALL(*transport, Write(_, _))
.With(AllArgs(RawData("oem trusty runtest trusty.hwaes.bench")))
.WillOnce(ReturnArg<1>());
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
- EXPECT_CALL(transport, Read(_, _))
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
+ EXPECT_CALL(*transport, Read(_, _))
.WillOnce(Invoke(
CopyData("TEXT, albeit very long and split over multiple TEXT messages.")));
- EXPECT_CALL(transport, Read(_, _))
+ EXPECT_CALL(*transport, Read(_, _))
.WillOnce(Invoke(CopyData("TEXT Indeed we can do that now with a TEXT message whenever "
"we feel like it.")));
- EXPECT_CALL(transport, Read(_, _))
+ EXPECT_CALL(*transport, Read(_, _))
.WillOnce(Invoke(CopyData("TEXT Isn't that truly super cool?")));
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
std::vector<std::string> info;
ASSERT_EQ(driver.RawCommand("oem trusty runtest trusty.hwaes.bench", "", nullptr, &info),
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 9b5e5f7..94a53ed 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -128,7 +128,7 @@
return MatchFastboot(info, device_serial);
};
for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
if (usb)
transport = std::unique_ptr<TransportSniffer>(
new TransportSniffer(std::move(usb), serial_port));
@@ -143,7 +143,7 @@
} else {
ASSERT_EQ(device_path, cb_scratch); // The path can not change
}
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
// No error checking since non-A/B devices may not support the command
fb->GetVar("current-slot", &initial_slot);
}
@@ -200,7 +200,7 @@
if (IsFastbootOverTcp()) {
ConnectTcpFastbootDevice();
device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
return;
}
@@ -212,7 +212,7 @@
return MatchFastboot(info, device_serial);
};
while (!transport) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
if (usb) {
transport = std::unique_ptr<TransportSniffer>(
new TransportSniffer(std::move(usb), serial_port));
@@ -220,7 +220,7 @@
std::this_thread::sleep_for(1s);
}
device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
}
void FastBootTest::SetLockState(bool unlock, bool assert_change) {
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index e635937..79f3939 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -50,6 +50,7 @@
#include <gtest/gtest.h>
#include <sparse/sparse.h>
+#include "constants.h"
#include "fastboot_driver.h"
#include "usb.h"
@@ -166,16 +167,15 @@
const auto matcher = [](usb_ifc_info* info) -> int {
return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
};
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
transport = usb_open(matcher);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
- ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
- << 10 * FastBootTest::MAX_USB_TRIES << "ms";
+ ASSERT_NE(transport.get(), nullptr) << "Could not find the fastboot device after: "
+ << 10 * FastBootTest::MAX_USB_TRIES << "ms";
if (transport) {
transport->Close();
- delete transport;
}
}
@@ -929,8 +929,7 @@
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
std::string resp;
- EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
- << "Device is unresponsive to getvar command";
+ EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "Device is unresponsive to getvar command";
}
TEST_F(Fuzz, CommandTooLarge) {
@@ -986,11 +985,10 @@
TEST_F(Fuzz, SparseZeroBlkSize) {
// handcrafted malform sparse file with zero as block size
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1005,13 +1003,10 @@
TEST_F(Fuzz, SparseVeryLargeBlkSize) {
// handcrafted sparse file with block size of ~4GB and divisible 4
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
- '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
- '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
- '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
- '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\xF0', '\xFF', '\xFF', '\xFF', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1022,11 +1017,10 @@
TEST_F(Fuzz, SparseTrimmed) {
// handcrafted malform sparse file which is trimmed
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1041,11 +1035,10 @@
TEST_F(Fuzz, SparseInvalidChurk) {
// handcrafted malform sparse file with invalid churk
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1895,9 +1888,10 @@
if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
printf("<Waiting for Device>\n");
const auto matcher = [](usb_ifc_info* info) -> int {
- return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+ return fastboot::FastBootTest::MatchFastboot(info,
+ fastboot::FastBootTest::device_serial);
};
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
while (!transport) {
transport = usb_open(matcher);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index f0eed0c..25c5a6e 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -15,8 +15,7 @@
//
#include "task.h"
-#include <cstddef>
-#include <iostream>
+#include "fastboot_driver.h"
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -42,7 +41,8 @@
void FlashTask::Run() {
auto flash = [&](const std::string& partition) {
- if (should_flash_in_userspace(partition) && !is_userspace_fastboot() && !fp_->force_flash) {
+ if (should_flash_in_userspace(fp_->source.get(), partition) && !is_userspace_fastboot() &&
+ !fp_->force_flash) {
die("The partition you are trying to flash is dynamic, and "
"should be flashed via fastbootd. Please run:\n"
"\n"
@@ -130,6 +130,7 @@
// Send the data to the device.
flash_partition_files(super_name_, files);
}
+
std::string OptimizedFlashSuperTask::ToString() const {
return "optimized-flash-super";
}
@@ -165,7 +166,7 @@
LOG(INFO) << "super optimization is disabled";
return nullptr;
}
- if (!supports_AB()) {
+ if (!supports_AB(fp->fb)) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
}
@@ -173,7 +174,7 @@
LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
return nullptr;
}
- if (!CanOptimize(fp->source, tasks)) {
+ if (!CanOptimize(fp->source.get(), tasks)) {
return nullptr;
}
@@ -218,17 +219,21 @@
auto s = helper->GetSparseLayout();
if (!s) return nullptr;
- // Remove images that we already flashed, just in case we have non-dynamic OS images.
+
+ // Remove tasks that are concatenated into this optimized task
auto remove_if_callback = [&](const auto& task) -> bool {
if (auto flash_task = task->AsFlashTask()) {
return helper->WillFlash(flash_task->GetPartitionAndSlot());
} else if (auto update_super_task = task->AsUpdateSuperTask()) {
return true;
} else if (auto reboot_task = task->AsRebootTask()) {
- return true;
+ if (reboot_task->GetTarget() == "fastboot") {
+ return true;
+ }
}
return false;
};
+
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
diff --git a/fastboot/task.h b/fastboot/task.h
index 6ebe381..a98c874 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -15,10 +15,8 @@
//
#pragma once
-#include <sstream>
#include <string>
-#include "fastboot_driver.h"
#include "super_flash_helper.h"
#include "util.h"
@@ -29,6 +27,7 @@
class FlashTask;
class RebootTask;
class UpdateSuperTask;
+class OptimizedFlashSuperTask;
class WipeTask;
class ResizeTask;
class Task {
@@ -40,6 +39,7 @@
virtual FlashTask* AsFlashTask() { return nullptr; }
virtual RebootTask* AsRebootTask() { return nullptr; }
virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
+ virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() { return nullptr; }
virtual WipeTask* AsWipeTask() { return nullptr; }
virtual ResizeTask* AsResizeTask() { return nullptr; }
@@ -86,13 +86,13 @@
public:
OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
+ virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() override { return this; }
static std::unique_ptr<OptimizedFlashSuperTask> Initialize(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
static bool CanOptimize(const ImageSource* source,
const std::vector<std::unique_ptr<Task>>& tasks);
- using ImageEntry = std::pair<const Image*, std::string>;
void Run() override;
std::string ToString() const override;
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 1ba3f4a..9cde1a8 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -19,11 +19,10 @@
#include "fastboot_driver_mock.h"
#include <gtest/gtest.h>
-#include <fstream>
#include <iostream>
#include <memory>
-#include <unordered_map>
#include "android-base/strings.h"
+#include "gmock/gmock.h"
using android::base::Split;
using testing::_;
@@ -193,8 +192,7 @@
GTEST_SKIP();
}
- LocalImageSource s;
- fp->source = &s;
+ fp->source.reset(new LocalImageSource);
fp->sparse_limit = std::numeric_limits<int64_t>::max();
fastboot::MockFastbootDriver fb;
@@ -235,3 +233,138 @@
<< "size of fastboot-info task list: " << fastboot_info_tasks.size()
<< " size of hardcoded task list: " << hardcoded_tasks.size();
}
+TEST_F(ParseTest, IsDynamicParitiontest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ fp->source.reset(new LocalImageSource);
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = true;
+ fp->should_use_fastboot_info = true;
+
+ std::vector<std::pair<std::string, bool>> test_cases = {
+ {"flash boot", false},
+ {"flash init_boot", false},
+ {"flash --apply-vbmeta vbmeta", false},
+ {"flash product", true},
+ {"flash system", true},
+ {"flash --slot-other system system_other.img", true},
+ };
+ for (auto& test : test_cases) {
+ std::unique_ptr<Task> task =
+ ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, " "));
+ auto flash_task = task->AsFlashTask();
+ ASSERT_FALSE(flash_task == nullptr);
+ ASSERT_EQ(FlashTask::IsDynamicParitition(fp->source.get(), flash_task), test.second);
+ }
+}
+
+TEST_F(ParseTest, CanOptimizeTest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ fp->source.reset(new LocalImageSource);
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = false;
+ fp->should_use_fastboot_info = true;
+
+ std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ false},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+ "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+ false},
+ };
+
+ auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };
+
+ for (auto& test : patternmatchtest) {
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+ tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
+ ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source.get(), tasks), test.second);
+ }
+}
+
+// Note: this test is exclusively testing that optimized flash super pattern matches a given task
+// list and is able to optimized based on a correct sequence of tasks
+TEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ fp->source.reset(new LocalImageSource);
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = true;
+ fp->should_use_fastboot_info = true;
+
+ ON_CALL(fb, GetVar("super-partition-name", _, _))
+ .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+ ON_CALL(fb, GetVar("slot-count", _, _))
+ .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("2"),
+ testing::Return(fastboot::SUCCESS)));
+
+ ON_CALL(fb, GetVar("partition-size:super", _, _))
+ .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("1000"),
+ testing::Return(fastboot::SUCCESS)));
+
+ std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ false},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+ "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+ false},
+ };
+
+ for (auto& test : patternmatchtest) {
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+ // Check to make sure we have an optimized flash super task && no more dynamic partition
+ // flashing tasks
+ auto&& IsOptimized = [](const FlashingPlan* fp,
+ const std::vector<std::unique_ptr<Task>>& tasks) {
+ bool contains_optimized_task = false;
+ for (auto& task : tasks) {
+ if (auto optimized_task = task->AsOptimizedFlashSuperTask()) {
+ contains_optimized_task = true;
+ }
+ if (auto flash_task = task->AsFlashTask()) {
+ if (FlashTask::IsDynamicParitition(fp->source.get(), flash_task)) {
+ return false;
+ }
+ }
+ }
+ return contains_optimized_task;
+ };
+ ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);
+ }
+}
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 69581ab..d85cb81 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,6 +29,7 @@
#pragma once
#include <functional>
+#include <memory>
#include "transport.h"
@@ -66,4 +67,4 @@
typedef std::function<int(usb_ifc_info*)> ifc_match_func;
// 0 is non blocking
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 964488c..37bb304 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -503,9 +503,15 @@
return 0;
}
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+ std::unique_ptr<UsbTransport> result;
std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
- return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr;
+
+ if (handle) {
+ result = std::make_unique<LinuxUsbTransport>(std::move(handle), timeout_ms);
+ }
+
+ return result;
}
/* Wait for the system to notice the device is gone, so that a subsequent
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 8b852f5..28300b2 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -469,16 +469,20 @@
/*
* Definitions of this file's public functions.
*/
-
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+ std::unique_ptr<UsbTransport> result;
std::unique_ptr<usb_handle> handle;
if (init_usb(callback, &handle) < 0) {
/* Something went wrong initializing USB. */
- return nullptr;
+ return result;
}
- return new OsxUsbTransport(std::move(handle), timeout_ms);
+ if (handle) {
+ result = std::make_unique<OsxUsbTransport>(std::move(handle), timeout_ms);
+ }
+
+ return result;
}
OsxUsbTransport::~OsxUsbTransport() {
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 67bf8a3..56a6e7d 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -381,7 +381,13 @@
return handle;
}
-UsbTransport* usb_open(ifc_match_func callback, uint32_t) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t) {
+ std::unique_ptr<UsbTransport> result;
std::unique_ptr<usb_handle> handle = find_usb_device(callback);
- return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
+
+ if (handle) {
+ result = std::make_unique<WindowsUsbTransport>(std::move(handle));
+ }
+
+ return result;
}
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 1694969..37b4988 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -16,7 +16,8 @@
"name": "fiemap_writer_test"
},
{
- "name": "fs_mgr_vendor_overlay_test"
+ "name": "fs_mgr_vendor_overlay_test",
+ "keywords": ["internal"]
},
{
"name": "vts_libsnapshot_test"
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 68576f2..2cc0d2d 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -688,7 +688,7 @@
continue;
}
} else {
- if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
+ if (!fs_mgr_overlayfs_already_mounted(overlay_mount_point, false /* overlay */)) {
continue;
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index 8fb63b1..9f17c06 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -733,15 +733,18 @@
bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
Fstab fstab;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ if (!ReadFstabFromProcMounts(&fstab)) {
return false;
}
const auto lowerdir = kLowerdirOption + mount_point;
- for (const auto& entry : fstab) {
- if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
- if (mount_point != entry.mount_point) continue;
- if (!overlay_only) return true;
- const auto options = android::base::Split(entry.fs_options, ",");
+ for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+ if (!overlay_only) {
+ return true;
+ }
+ if (entry->fs_type != "overlay" && entry->fs_type != "overlayfs") {
+ continue;
+ }
+ const auto options = android::base::Split(entry->fs_options, ",");
for (const auto& opt : options) {
if (opt == lowerdir) {
return true;
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 2e34920..ee83cda 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -655,10 +655,12 @@
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 2\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta.img"));
android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
@@ -876,10 +878,12 @@
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: vbmeta_system\n"
" Rollback Index Location: 2\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta.img"));
bool fatal_error = false;
@@ -909,7 +913,8 @@
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 3\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta_system.img"));
chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
index 1c47c07..d49affb 100644
--- a/fs_mgr/libfs_avb/tests/basic_test.cpp
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -268,10 +268,12 @@
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 2\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta.img"));
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 04b5a02..6fad662 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -201,6 +201,7 @@
"libsnapshot_cow/snapshot_reader.cpp",
"libsnapshot_cow/writer_base.cpp",
"libsnapshot_cow/writer_v2.cpp",
+ "libsnapshot_cow/writer_v3.cpp",
],
export_include_dirs: ["include"],
host_supported: true,
@@ -392,6 +393,7 @@
srcs: [
"libsnapshot_cow/snapshot_reader_test.cpp",
"libsnapshot_cow/test_v2.cpp",
+ "libsnapshot_cow/test_v3.cpp",
],
cflags: [
"-D_FILE_OFFSET_BITS=64",
@@ -415,6 +417,9 @@
test_options: {
min_shipping_api_level: 30,
},
+ data: [
+ "tools/testdata/cow_v2",
+ ],
auto_gen_config: true,
require_root: false,
host_supported: true,
@@ -446,6 +451,54 @@
],
}
+cc_binary {
+ name: "create_snapshot",
+ host_supported: true,
+ device_supported: false,
+
+ srcs: ["libsnapshot_cow/create_cow.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ static_libs: [
+ "liblog",
+ "libbase",
+ "libext4_utils",
+ "libsnapshot_cow",
+ "libcrypto",
+ "libbrotli",
+ "libz",
+ "liblz4",
+ "libzstd",
+ "libgflags",
+ ],
+ shared_libs: [
+ ],
+
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+
+ dist: {
+ targets: [
+ "sdk",
+ "sdk-repo-platform-tools",
+ "sdk_repo",
+ ],
+ },
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
python_library_host {
name: "snapshot_proto_python",
srcs: [
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
index cf65615..8191d61 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -24,24 +24,28 @@
class ICompressor {
public:
- explicit ICompressor(uint32_t compression_level) : compression_level_(compression_level) {}
+ explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+ : compression_level_(compression_level), block_size_(block_size) {}
virtual ~ICompressor() {}
// Factory methods for compression methods.
- static std::unique_ptr<ICompressor> Gz(uint32_t compression_level);
- static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level);
- static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level);
- static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t BLOCK_SZ);
+ static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t block_size);
+ static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level,
+ const int32_t block_size);
+ static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t block_size);
+ static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t block_size);
- static std::unique_ptr<ICompressor> Create(CowCompression compression, const int32_t BLOCK_SZ);
+ static std::unique_ptr<ICompressor> Create(CowCompression compression,
+ const int32_t block_size);
uint32_t GetCompressionLevel() const { return compression_level_; }
-
+ uint32_t GetBlockSize() const { return block_size_; }
[[nodiscard]] virtual std::basic_string<uint8_t> Compress(const void* data,
size_t length) const = 0;
private:
uint32_t compression_level_;
+ uint32_t block_size_;
};
} // namespace snapshot
} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index c9a4dee..bfa8f6b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -22,6 +22,9 @@
namespace android {
namespace snapshot {
+struct CowOperationV3;
+typedef CowOperationV3 CowOperation;
+
static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
static constexpr uint32_t kCowVersionMajor = 2;
static constexpr uint32_t kCowVersionMinor = 0;
@@ -49,7 +52,9 @@
// | Footer (fixed) |
// +-----------------------+
//
-// The operations begin immediately after the header, and the "raw data"
+// After the header is a 2mb scratch space that is used to read ahead data during merge operations
+//
+// The operations begin immediately after the scratch space, and the "raw data"
// immediately follows the operation which refers to it. While streaming
// an OTA, we can immediately write the op and data, syncing after each pair,
// while storing operation metadata in memory. At the end, we compute data and
@@ -107,9 +112,8 @@
uint64_t num_ops;
} __attribute__((packed));
-// Cow operations are currently fixed-size entries, but this may change if
-// needed.
-struct CowOperation {
+// V2 version of COW. On disk format for older devices
+struct CowOperationV2 {
// The operation code (see the constants and structures below).
uint8_t type;
@@ -143,7 +147,32 @@
uint64_t source;
} __attribute__((packed));
-static_assert(sizeof(CowOperation) == sizeof(CowFooterOperation));
+// The on disk format of cow (currently == CowOperation)
+struct CowOperationV3 {
+ // If this operation reads from the data section of the COW, this contains
+ // the length.
+ uint16_t data_length;
+
+ // The block of data in the new image that this operation modifies.
+ uint32_t new_block;
+
+ // source_info with have the following layout
+ // |---4 bits ---| ---12 bits---| --- 48 bits ---|
+ // |--- type --- | -- unused -- | --- source --- |
+ //
+ // The value of |source| depends on the operation code.
+ //
+ // CopyOp: a 32-bit block location in the source image.
+ // ReplaceOp: an absolute byte offset within the COW's data section.
+ // XorOp: an absolute byte offset in the source image.
+ // ZeroOp: unused
+ // LabelOp: a 64-bit opaque identifier.
+ //
+ // A block is compressed if it’s data is < block_sz
+ uint64_t source_info;
+} __attribute__((packed));
+
+static_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));
static constexpr uint8_t kCowCopyOp = 1;
static constexpr uint8_t kCowReplaceOp = 2;
@@ -170,11 +199,22 @@
static constexpr uint8_t kCowReadAheadInProgress = 1;
static constexpr uint8_t kCowReadAheadDone = 2;
-static inline uint64_t GetCowOpSourceInfoData(const CowOperation* op) {
- return op->source;
+// this is a mask to grab the last 48 bits of a 64 bit integer
+static constexpr uint64_t kCowOpSourceInfoDataMask = (0x1ULL << 48) - 1;
+// this is a mask to grab the first 4 bits of a 64 bit integer
+static constexpr uint64_t kCowOpSourceInfoTypeBit = 60;
+static constexpr uint64_t kCowOpSourceInfoTypeNumBits = 4;
+static constexpr uint64_t kCowOpSourceInfoTypeMask = (1 << kCowOpSourceInfoTypeNumBits) - 1;
+
+static constexpr void SetCowOpSourceInfoType(CowOperation* op, const uint8_t type) {
+ op->source_info |= uint64_t(type) << kCowOpSourceInfoTypeBit;
}
-static inline bool GetCowOpSourceInfoCompression(const CowOperation* op) {
- return op->compression != kCowCompressNone;
+static constexpr uint64_t GetCowOpSourceInfoType(const CowOperation& op) {
+ return (op.source_info >> kCowOpSourceInfoTypeBit) & kCowOpSourceInfoTypeMask;
+}
+
+static constexpr uint64_t GetCowOpSourceInfoData(const CowOperation& op) {
+ return op.source_info & kCowOpSourceInfoDataMask;
}
struct CowFooter {
@@ -198,10 +238,12 @@
// 2MB Scratch space used for read-ahead
static constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);
+
std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
// Ops that are internal to the Cow Format and not OTA data
bool IsMetadataOp(const CowOperation& op);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4ce52f..2721937 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -165,10 +165,10 @@
void UpdateMergeOpsCompleted(int num_merge_ops) { header_.num_merge_ops += num_merge_ops; }
private:
- bool ParseOps(std::optional<uint64_t> label);
+ bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);
bool PrepMergeOps();
uint64_t FindNumCopyops();
- uint8_t GetCompressionType(const CowOperation* op);
+ uint8_t GetCompressionType();
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
@@ -185,7 +185,10 @@
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
ReaderFlags reader_flag_;
bool is_merge_{};
+ uint8_t compression_type_ = kCowCompressNone;
};
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index c056a19..e7b0020 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -73,6 +73,9 @@
class SnapshotMergeStats;
class SnapshotStatus;
+using std::chrono::duration_cast;
+using namespace std::chrono_literals;
+
static constexpr const std::string_view kCowGroupName = "cow";
static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
@@ -424,6 +427,7 @@
FRIEND_TEST(SnapshotTest, MergeFailureCode);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);
FRIEND_TEST(SnapshotUpdateTest, AddPartition);
FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
@@ -436,11 +440,13 @@
FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
+ FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
friend class LockTestConsumer;
friend class SnapshotFuzzEnv;
+ friend class MapSnapshots;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
@@ -455,7 +461,7 @@
bool EnsureImageManager();
// Ensure we're connected to snapuserd.
- bool EnsureSnapuserdConnected();
+ bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
// Helpers for first-stage init.
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
@@ -548,6 +554,16 @@
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
+ // Boot device off snapshots without slot switch
+ bool BootFromSnapshotsWithoutSlotSwitch();
+
+ // Remove kBootSnapshotsWithoutSlotSwitch so that device can boot
+ // without snapshots on the current slot
+ bool PrepareDeviceToBootWithoutSnapshot();
+
+ // Is the kBootSnapshotsWithoutSlotSwitch present
+ bool IsSnapshotWithoutSlotSwitch();
+
// List the known snapshot names.
bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
const std::string& suffix = "");
@@ -662,6 +678,7 @@
std::string GetRollbackIndicatorPath();
std::string GetForwardMergeIndicatorPath();
std::string GetOldPartitionMetadataPath();
+ std::string GetBootSnapshotsWithoutSlotSwitchPath();
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 71ac59f..abc7d33 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -56,16 +56,16 @@
}
std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
- const int32_t BLOCK_SZ) {
+ const int32_t block_size) {
switch (compression.algorithm) {
case kCowCompressLz4:
- return ICompressor::Lz4(compression.compression_level);
+ return ICompressor::Lz4(compression.compression_level, block_size);
case kCowCompressBrotli:
- return ICompressor::Brotli(compression.compression_level);
+ return ICompressor::Brotli(compression.compression_level, block_size);
case kCowCompressGz:
- return ICompressor::Gz(compression.compression_level);
+ return ICompressor::Gz(compression.compression_level, block_size);
case kCowCompressZstd:
- return ICompressor::Zstd(compression.compression_level, BLOCK_SZ);
+ return ICompressor::Zstd(compression.compression_level, block_size);
case kCowCompressNone:
return nullptr;
}
@@ -100,7 +100,8 @@
class GzCompressor final : public ICompressor {
public:
- GzCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+ GzCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
const auto bound = compressBound(length);
@@ -120,7 +121,8 @@
class Lz4Compressor final : public ICompressor {
public:
- Lz4Compressor(uint32_t compression_level) : ICompressor(compression_level){};
+ Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
const auto bound = LZ4_compressBound(length);
@@ -151,7 +153,8 @@
class BrotliCompressor final : public ICompressor {
public:
- BrotliCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+ BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
const auto bound = BrotliEncoderMaxCompressedSize(length);
@@ -176,10 +179,11 @@
class ZstdCompressor final : public ICompressor {
public:
- ZstdCompressor(uint32_t compression_level, const uint32_t MAX_BLOCK_SIZE)
- : ICompressor(compression_level), zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
+ ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size),
+ zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
- ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(MAX_BLOCK_SIZE));
+ ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(GetBlockSize()));
};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
@@ -313,20 +317,23 @@
return true;
}
-std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level) {
- return std::make_unique<BrotliCompressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<BrotliCompressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level) {
- return std::make_unique<GzCompressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+ return std::make_unique<GzCompressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level) {
- return std::make_unique<Lz4Compressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<Lz4Compressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level, const int32_t BLOCK_SZ) {
- return std::make_unique<ZstdCompressor>(compression_level, BLOCK_SZ);
+std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<ZstdCompressor>(compression_level, block_size);
}
void CompressWorker::Finalize() {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index ff3ccec..1be592d 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -14,11 +14,14 @@
// limitations under the License.
//
+#include <inttypes.h>
#include <libsnapshot/cow_format.h>
#include <sys/types.h>
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/cow_format.h>
#include "writer_v2.h"
namespace android {
@@ -26,45 +29,80 @@
using android::base::unique_fd;
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
- os << "CowOperation(type:";
- if (op.type == kCowCopyOp)
- os << "kCowCopyOp, ";
- else if (op.type == kCowReplaceOp)
- os << "kCowReplaceOp, ";
- else if (op.type == kCowZeroOp)
- os << "kZeroOp, ";
- else if (op.type == kCowFooterOp)
- os << "kCowFooterOp, ";
- else if (op.type == kCowLabelOp)
- os << "kCowLabelOp, ";
- else if (op.type == kCowClusterOp)
- os << "kCowClusterOp ";
- else if (op.type == kCowXorOp)
- os << "kCowXorOp ";
- else if (op.type == kCowSequenceOp)
- os << "kCowSequenceOp ";
- else if (op.type == kCowFooterOp)
- os << "kCowFooterOp ";
- else
- os << (int)op.type << "?,";
- os << "compression:";
- if (op.compression == kCowCompressNone)
- os << "kCowCompressNone, ";
- else if (op.compression == kCowCompressGz)
- os << "kCowCompressGz, ";
- else if (op.compression == kCowCompressBrotli)
- os << "kCowCompressBrotli, ";
- else
- os << (int)op.compression << "?, ";
- os << "data_length:" << op.data_length << ",\t";
- os << "new_block:" << op.new_block << ",\t";
+std::ostream& EmitCowTypeString(std::ostream& os, uint8_t cow_type) {
+ switch (cow_type) {
+ case kCowCopyOp:
+ return os << "kCowCopyOp";
+ case kCowReplaceOp:
+ return os << "kCowReplaceOp";
+ case kCowZeroOp:
+ return os << "kZeroOp";
+ case kCowFooterOp:
+ return os << "kCowFooterOp";
+ case kCowLabelOp:
+ return os << "kCowLabelOp";
+ case kCowClusterOp:
+ return os << "kCowClusterOp";
+ case kCowXorOp:
+ return os << "kCowXorOp";
+ case kCowSequenceOp:
+ return os << "kCowSequenceOp";
+ default:
+ return os << (int)cow_type << "unknown";
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& op) {
+ os << "CowOperationV2(";
+ EmitCowTypeString(os, op.type) << ", ";
+ switch (op.compression) {
+ case kCowCompressNone:
+ os << "uncompressed, ";
+ break;
+ case kCowCompressGz:
+ os << "gz, ";
+ break;
+ case kCowCompressBrotli:
+ os << "brotli, ";
+ break;
+ case kCowCompressLz4:
+ os << "lz4, ";
+ break;
+ case kCowCompressZstd:
+ os << "zstd, ";
+ break;
+ }
+ os << "data_length:" << op.data_length << ", ";
+ os << "new_block:" << op.new_block << ", ";
os << "source:" << op.source;
os << ")";
return os;
}
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
+std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
+ os << "CowOperation(";
+ EmitCowTypeString(os, GetCowOpSourceInfoType(op));
+ if (GetCowOpSourceInfoType(op) == kCowReplaceOp || GetCowOpSourceInfoType(op) == kCowXorOp ||
+ GetCowOpSourceInfoType(op) == kCowSequenceOp) {
+ os << ", data_length:" << op.data_length;
+ }
+ if (GetCowOpSourceInfoType(op) != kCowClusterOp &&
+ GetCowOpSourceInfoType(op) != kCowSequenceOp && GetCowOpSourceInfoType(op) != kCowLabelOp) {
+ os << ", new_block:" << op.new_block;
+ }
+ if (GetCowOpSourceInfoType(op) == kCowXorOp || GetCowOpSourceInfoType(op) == kCowReplaceOp ||
+ GetCowOpSourceInfoType(op) == kCowCopyOp) {
+ os << ", source:" << (op.source_info & kCowOpSourceInfoDataMask);
+ } else if (GetCowOpSourceInfoType(op) == kCowClusterOp) {
+ os << ", cluster_data:" << (op.source_info & kCowOpSourceInfoDataMask);
+ } else {
+ os << ", label:0x" << android::base::StringPrintf("%" PRIx64, op.source_info);
+ }
+ os << ")";
+ return os;
+}
+
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
} else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
@@ -74,18 +112,18 @@
}
}
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
- return cluster_ops * sizeof(CowOperation);
+ return cluster_ops * sizeof(CowOperationV2);
} else if (cluster_ops == 0) {
- return sizeof(CowOperation);
+ return sizeof(CowOperationV2);
} else {
return 0;
}
}
bool IsMetadataOp(const CowOperation& op) {
- switch (op.type) {
+ switch (GetCowOpSourceInfoType(op)) {
case kCowLabelOp:
case kCowClusterOp:
case kCowFooterOp:
@@ -97,7 +135,7 @@
}
bool IsOrderedOp(const CowOperation& op) {
- switch (op.type) {
+ switch (GetCowOpSourceInfoType(op)) {
case kCowCopyOp:
case kCowXorOp:
return true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 1b5d724..996037e 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -24,6 +24,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <zlib.h>
@@ -33,6 +34,35 @@
namespace android {
namespace snapshot {
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+
+ memset(header, 0, sizeof(*header));
+
+ if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
+ return false;
+ }
+ if (header->prefix.magic != kCowMagicNumber) {
+ LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
+ << "Expected: " << kCowMagicNumber;
+ return false;
+ }
+ if (header->prefix.header_size > sizeof(CowHeader)) {
+ LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
+ << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
+ return false;
+ }
+
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ return android::base::ReadFully(fd, header, header->prefix.header_size);
+}
+
CowReader::CowReader(ReaderFlags reader_flag, bool is_merge)
: fd_(-1),
header_(),
@@ -55,6 +85,7 @@
cow->data_loc_ = data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
+ cow->compression_type_ = compression_type_;
return cow;
}
@@ -101,8 +132,43 @@
footer_ = parser.footer();
fd_size_ = parser.fd_size();
last_label_ = parser.last_label();
- ops_ = parser.ops();
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);
+ SetCowOpSourceInfoType(&new_op, 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 (GetCowOpSourceInfoType(new_op) != 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;
+ }
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
@@ -224,7 +290,7 @@
for (size_t i = 0; i < ops_->size(); i++) {
auto& current_op = ops_->data()[i];
- if (current_op.type == kCowSequenceOp) {
+ if (GetCowOpSourceInfoType(current_op) == kCowSequenceOp) {
size_t seq_len = current_op.data_length / sizeof(uint32_t);
merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
@@ -536,11 +602,11 @@
}
bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {
- switch (op->type) {
+ switch (GetCowOpSourceInfoType(*op)) {
case kCowSequenceOp:
case kCowReplaceOp:
case kCowXorOp:
- return GetRawBytes(GetCowOpSourceInfoData(op), buffer, len, read);
+ return GetRawBytes(GetCowOpSourceInfoData(*op), buffer, len, read);
default:
LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
return false;
@@ -597,14 +663,14 @@
size_t remaining_;
};
-uint8_t CowReader::GetCompressionType(const CowOperation* op) {
- return op->compression;
+uint8_t CowReader::GetCompressionType() {
+ return compression_type_;
}
ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
size_t ignore_bytes) {
std::unique_ptr<IDecompressor> decompressor;
- switch (GetCompressionType(op)) {
+ switch (GetCompressionType()) {
case kCowCompressNone:
break;
case kCowCompressGz:
@@ -624,15 +690,15 @@
}
break;
default:
- LOG(ERROR) << "Unknown compression type: " << GetCompressionType(op);
+ LOG(ERROR) << "Unknown compression type: " << GetCompressionType();
return -1;
}
uint64_t offset;
- if (op->type == kCowXorOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowXorOp) {
offset = data_loc_->at(op->new_block);
} else {
- offset = GetCowOpSourceInfoData(op);
+ offset = GetCowOpSourceInfoData(*op);
}
if (!decompressor) {
@@ -646,12 +712,12 @@
}
bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {
- switch (op->type) {
+ switch (GetCowOpSourceInfoType(*op)) {
case kCowCopyOp:
- *source_offset = GetCowOpSourceInfoData(op) * header_.block_size;
+ *source_offset = GetCowOpSourceInfoData(*op) * header_.block_size;
return true;
case kCowXorOp:
- *source_offset = GetCowOpSourceInfoData(op);
+ *source_offset = GetCowOpSourceInfoData(*op);
return true;
default:
return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
new file mode 100644
index 0000000..efb1035
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
@@ -0,0 +1,361 @@
+#include <linux/types.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <condition_variable>
+#include <cstring>
+#include <future>
+#include <iostream>
+#include <limits>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <storage_literals/storage_literals.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+
+#include <openssl/sha.h>
+
+DEFINE_string(source, "", "Source partition image");
+DEFINE_string(target, "", "Target partition image");
+DEFINE_string(compression, "lz4",
+ "Compression algorithm. Default is set to lz4. Available options: lz4, zstd, gz");
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+using namespace android;
+using android::base::unique_fd;
+
+using android::snapshot::CreateCowWriter;
+using android::snapshot::ICowWriter;
+
+class CreateSnapshot {
+ public:
+ CreateSnapshot(const std::string& src_file, const std::string& target_file,
+ const std::string& patch_file, const std::string& compression);
+ bool CreateSnapshotPatch();
+
+ private:
+ /* source.img */
+ std::string src_file_;
+ /* target.img */
+ std::string target_file_;
+ /* snapshot-patch generated */
+ std::string patch_file_;
+
+ /*
+ * Active file which is being parsed by this instance.
+ * It will either be source.img or target.img.
+ */
+ std::string parsing_file_;
+ bool create_snapshot_patch_ = false;
+
+ const int kNumThreads = 6;
+ const size_t kBlockSizeToRead = 1_MiB;
+
+ std::unordered_map<std::string, int> source_block_hash_;
+ std::mutex source_block_hash_lock_;
+
+ std::unique_ptr<ICowWriter> writer_;
+ std::mutex write_lock_;
+
+ std::unique_ptr<uint8_t[]> zblock_;
+
+ std::string compression_ = "lz4";
+ unique_fd fd_;
+
+ const int BLOCK_SZ = 4_KiB;
+ void SHA256(const void* data, size_t length, uint8_t out[32]);
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ bool ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz);
+ std::string ToHexString(const uint8_t* buf, size_t len);
+
+ bool CreateSnapshotFile();
+ bool FindSourceBlockHash();
+ bool PrepareParse(std::string& parsing_file, const bool createSnapshot);
+ bool ParsePartition();
+ bool WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash);
+};
+
+void CreateSnapshotLogger(android::base::LogId, android::base::LogSeverity severity, const char*,
+ const char*, unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+CreateSnapshot::CreateSnapshot(const std::string& src_file, const std::string& target_file,
+ const std::string& patch_file, const std::string& compression)
+ : src_file_(src_file), target_file_(target_file), patch_file_(patch_file) {
+ if (!compression.empty()) {
+ compression_ = compression;
+ }
+}
+
+bool CreateSnapshot::PrepareParse(std::string& parsing_file, const bool createSnapshot) {
+ parsing_file_ = parsing_file;
+ create_snapshot_patch_ = createSnapshot;
+
+ if (createSnapshot) {
+ fd_.reset(open(patch_file_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open the snapshot-patch file: " << patch_file_;
+ return false;
+ }
+
+ zblock_ = std::make_unique<uint8_t[]>(BLOCK_SZ);
+ std::memset(zblock_.get(), 0, BLOCK_SZ);
+
+ CowOptions options;
+ options.compression = compression_;
+ options.num_compress_threads = 2;
+ options.batch_write = true;
+ options.cluster_ops = 600;
+ writer_ = CreateCowWriter(2, options, std::move(fd_));
+ }
+ return true;
+}
+
+/*
+ * Create per-block sha256 hash of source partition
+ */
+bool CreateSnapshot::FindSourceBlockHash() {
+ if (!PrepareParse(src_file_, false)) {
+ return false;
+ }
+ return ParsePartition();
+}
+
+/*
+ * Create snapshot file by comparing sha256 per block
+ * of target.img with the constructed per-block sha256 hash
+ * of source partition.
+ */
+bool CreateSnapshot::CreateSnapshotFile() {
+ if (!PrepareParse(target_file_, true)) {
+ return false;
+ }
+ return ParsePartition();
+}
+
+/*
+ * Creates snapshot patch file by comparing source.img and target.img
+ */
+bool CreateSnapshot::CreateSnapshotPatch() {
+ if (!FindSourceBlockHash()) {
+ return false;
+ }
+ return CreateSnapshotFile();
+}
+
+void CreateSnapshot::SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+std::string CreateSnapshot::ToHexString(const uint8_t* buf, size_t len) {
+ char lookup[] = "0123456789abcdef";
+ std::string out(len * 2 + 1, '\0');
+ char* outp = out.data();
+ for (; len > 0; len--, buf++) {
+ *outp++ = (char)lookup[*buf >> 4];
+ *outp++ = (char)lookup[*buf & 0xf];
+ }
+ return out;
+}
+
+bool CreateSnapshot::WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash) {
+ if (std::memcmp(zblock_.get(), buffer, BLOCK_SZ) == 0) {
+ std::lock_guard<std::mutex> lock(write_lock_);
+ return writer_->AddZeroBlocks(block, 1);
+ }
+
+ auto iter = source_block_hash_.find(block_hash);
+ if (iter != source_block_hash_.end()) {
+ std::lock_guard<std::mutex> lock(write_lock_);
+ return writer_->AddCopy(block, iter->second, 1);
+ }
+ std::lock_guard<std::mutex> lock(write_lock_);
+ return writer_->AddRawBlocks(block, buffer, BLOCK_SZ);
+}
+
+bool CreateSnapshot::ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "open failed: " << parsing_file_;
+ return false;
+ }
+
+ loff_t file_offset = offset;
+ const uint64_t read_sz = kBlockSizeToRead;
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ LOG(ERROR) << "Failed to read block from block device: " << parsing_file_
+ << " at offset: " << file_offset << " read-size: " << to_read
+ << " block-size: " << dev_sz;
+ return false;
+ }
+
+ if (!IsBlockAligned(to_read)) {
+ LOG(ERROR) << "unable to parse the un-aligned request: " << to_read;
+ return false;
+ }
+
+ size_t num_blocks = to_read / BLOCK_SZ;
+ uint64_t buffer_offset = 0;
+ off_t foffset = file_offset;
+
+ while (num_blocks) {
+ const void* bufptr = (char*)buffer.get() + buffer_offset;
+ uint64_t blkindex = foffset / BLOCK_SZ;
+
+ uint8_t checksum[32];
+ SHA256(bufptr, BLOCK_SZ, checksum);
+ std::string hash = ToHexString(checksum, sizeof(checksum));
+
+ if (create_snapshot_patch_) {
+ if (!WriteSnapshot(bufptr, blkindex, hash)) {
+ LOG(ERROR) << "WriteSnapshot failed for block: " << blkindex;
+ return false;
+ }
+ } else {
+ std::lock_guard<std::mutex> lock(source_block_hash_lock_);
+ {
+ if (source_block_hash_.count(hash) == 0) {
+ source_block_hash_[hash] = blkindex;
+ }
+ }
+ }
+ buffer_offset += BLOCK_SZ;
+ foffset += BLOCK_SZ;
+ num_blocks -= 1;
+ }
+
+ file_offset += (skip_blocks * to_read);
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool CreateSnapshot::ParsePartition() {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "open failed: " << parsing_file_;
+ return false;
+ }
+
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ LOG(ERROR) << "Could not determine block device size: " << parsing_file_;
+ return false;
+ }
+
+ if (!IsBlockAligned(dev_sz)) {
+ LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+ return false;
+ }
+
+ int num_threads = kNumThreads;
+
+ std::vector<std::future<bool>> threads;
+ off_t start_offset = 0;
+ const int skip_blocks = num_threads;
+
+ while (num_threads) {
+ threads.emplace_back(std::async(std::launch::async, &CreateSnapshot::ReadBlocks, this,
+ start_offset, skip_blocks, dev_sz));
+ start_offset += kBlockSizeToRead;
+ num_threads -= 1;
+ if (start_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ bool ret = true;
+ for (auto& t : threads) {
+ ret = t.get() && ret;
+ }
+
+ if (ret && create_snapshot_patch_ && !writer_->Finalize()) {
+ LOG(ERROR) << "Finzalize failed";
+ return false;
+ }
+
+ return ret;
+}
+
+} // namespace snapshot
+} // namespace android
+
+constexpr char kUsage[] = R"(
+NAME
+ create_snapshot - Create snapshot patches by comparing two partition images
+
+SYNOPSIS
+ create_snapshot --source=<source.img> --target=<target.img> --compression="<compression-algorithm"
+
+ source.img -> Source partition image
+ target.img -> Target partition image
+ compressoin -> compression algorithm. Default set to lz4. Supported types are gz, lz4, zstd.
+
+EXAMPLES
+
+ $ create_snapshot $SOURCE_BUILD/system.img $TARGET_BUILD/system.img
+ $ create_snapshot $SOURCE_BUILD/product.img $TARGET_BUILD/product.img --compression="zstd"
+
+)";
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, &android::snapshot::CreateSnapshotLogger);
+ ::gflags::SetUsageMessage(kUsage);
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_source.empty() || FLAGS_target.empty()) {
+ LOG(INFO) << kUsage;
+ return 0;
+ }
+
+ std::string fname = android::base::Basename(FLAGS_target.c_str());
+ auto parts = android::base::Split(fname, ".");
+ std::string snapshotfile = parts[0] + ".patch";
+ android::snapshot::CreateSnapshot snapshot(FLAGS_source, FLAGS_target, snapshotfile,
+ FLAGS_compression);
+
+ if (!snapshot.CreateSnapshotPatch()) {
+ LOG(ERROR) << "Snapshot creation failed";
+ return -1;
+ }
+
+ LOG(INFO) << "Snapshot patch: " << snapshotfile << " created successfully";
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index a6dee4f..aaaf20c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -22,9 +22,11 @@
#include <string>
#include <vector>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <gflags/gflags.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include "parser_v2.h"
@@ -38,11 +40,13 @@
DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing");
DEFINE_bool(show_merge_sequence, false, "Show merge order sequence");
DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser");
+DEFINE_string(extract_to, "", "Extract the COW contents to the given file");
namespace android {
namespace snapshot {
using android::base::borrowed_fd;
+using android::base::unique_fd;
void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
unsigned int, const char* message) {
@@ -53,7 +57,7 @@
}
}
-static void ShowBad(CowReader& reader, const struct CowOperation* op) {
+static void ShowBad(CowReader& reader, const CowOperation* op) {
size_t count;
auto buffer = std::make_unique<uint8_t[]>(op->data_length);
@@ -104,12 +108,21 @@
}
static bool Inspect(const std::string& path) {
- android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ unique_fd fd(open(path.c_str(), O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << path;
return false;
}
+ unique_fd extract_to;
+ if (!FLAGS_extract_to.empty()) {
+ extract_to.reset(open(FLAGS_extract_to.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));
+ if (extract_to < 0) {
+ PLOG(ERROR) << "could not open " << FLAGS_extract_to << " for writing";
+ return false;
+ }
+ }
+
CowReader reader;
auto start_time = std::chrono::steady_clock::now();
@@ -186,15 +199,27 @@
if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
- if (FLAGS_decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) {
+ if ((FLAGS_decompress || extract_to >= 0) && GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
std::cerr << "Failed to decompress for :" << *op << "\n";
success = false;
if (FLAGS_show_bad_data) ShowBad(reader, op);
}
+ if (extract_to >= 0) {
+ off_t offset = uint64_t(op->new_block) * header.block_size;
+ if (!android::base::WriteFullyAtOffset(extract_to, buffer.data(), buffer.size(),
+ offset)) {
+ PLOG(ERROR) << "failed to write block " << op->new_block;
+ return false;
+ }
+ }
+ } else if (extract_to >= 0 && !IsMetadataOp(*op) &&
+ GetCowOpSourceInfoType(*op) != kCowZeroOp) {
+ PLOG(ERROR) << "Cannot extract op yet: " << *op;
+ return false;
}
- if (op->type == kCowSequenceOp && FLAGS_show_merge_sequence) {
+ if (GetCowOpSourceInfoType(*op) == kCowSequenceOp && FLAGS_show_merge_sequence) {
size_t read;
std::vector<uint32_t> merge_op_blocks;
size_t seq_len = op->data_length / sizeof(uint32_t);
@@ -212,13 +237,13 @@
}
}
- if (op->type == kCowCopyOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowCopyOp) {
copy_ops++;
- } else if (op->type == kCowReplaceOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
replace_ops++;
- } else if (op->type == kCowZeroOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowZeroOp) {
zero_ops++;
- } else if (op->type == kCowXorOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowXorOp) {
xor_ops++;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index fdb5c59..a7307bf 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -23,35 +23,6 @@
using android::base::borrowed_fd;
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
- if (lseek(fd.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
-
- memset(header, 0, sizeof(*header));
-
- if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
- return false;
- }
- if (header->prefix.magic != kCowMagicNumber) {
- LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
- << "Expected: " << kCowMagicNumber;
- return false;
- }
- if (header->prefix.header_size > sizeof(CowHeader)) {
- LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
- << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
- return false;
- }
-
- if (lseek(fd.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
- return android::base::ReadFully(fd, header, header->prefix.header_size);
-}
-
bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
auto pos = lseek(fd.get(), 0, SEEK_END);
if (pos < 0) {
@@ -66,18 +37,18 @@
<< sizeof(CowFooter);
return false;
}
- if (header_.op_size != sizeof(CowOperation)) {
+ if (header_.op_size != sizeof(CowOperationV2)) {
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
+ << sizeof(CowOperationV2);
return false;
}
if (header_.cluster_ops == 1) {
LOG(ERROR) << "Clusters must contain at least two operations to function.";
return false;
}
- if (header_.op_size != sizeof(CowOperation)) {
+ if (header_.op_size != sizeof(CowOperationV2)) {
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
+ << sizeof(CowOperationV2);
return false;
}
if (header_.cluster_ops == 1) {
@@ -123,23 +94,23 @@
uint64_t data_pos = 0;
if (header_.cluster_ops) {
- data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+ data_pos = pos + header_.cluster_ops * sizeof(CowOperationV2);
} else {
- data_pos = pos + sizeof(CowOperation);
+ data_pos = pos + sizeof(CowOperationV2);
}
- auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+ auto ops_buffer = std::make_shared<std::vector<CowOperationV2>>();
uint64_t current_op_num = 0;
uint64_t cluster_ops = header_.cluster_ops ?: 1;
bool done = false;
// Alternating op clusters and data
while (!done) {
- uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+ uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperationV2));
if (to_add == 0) break;
ops_buffer->resize(current_op_num + to_add);
if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
- to_add * sizeof(CowOperation))) {
+ to_add * sizeof(CowOperationV2))) {
PLOG(ERROR) << "read op failed";
return false;
}
@@ -150,7 +121,7 @@
if (current_op.type == kCowXorOp) {
data_loc->insert({current_op.new_block, data_pos});
}
- pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+ pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
if (current_op.type == kCowClusterOp) {
@@ -222,7 +193,7 @@
<< ops_buffer->size();
return false;
}
- if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
+ if (ops_buffer->size() * sizeof(CowOperationV2) != footer_->op.ops_size) {
LOG(ERROR) << "ops size does not match ";
return false;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
index afcf687..f51ff88 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -33,7 +33,7 @@
const CowHeader& header() const { return header_; }
const std::optional<CowFooter>& footer() const { return footer_; }
- std::shared_ptr<std::vector<CowOperation>> ops() { return ops_; }
+ 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_; }
@@ -43,13 +43,11 @@
CowHeader header_ = {};
std::optional<CowFooter> footer_;
- std::shared_ptr<std::vector<CowOperation>> ops_;
+ 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_;
};
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
index a3e40d9..d7ee432 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -147,7 +147,7 @@
op = ops_[chunk];
}
- if (!op || op->type == kCowCopyOp) {
+ if (!op || GetCowOpSourceInfoType(*op) == kCowCopyOp) {
borrowed_fd fd = GetSourceFd();
if (fd < 0) {
// GetSourceFd sets errno.
@@ -169,15 +169,15 @@
// ReadFullyAtOffset sets errno.
return -1;
}
- } else if (op->type == kCowZeroOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowZeroOp) {
memset(buffer, 0, bytes_to_read);
- } else if (op->type == kCowReplaceOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
errno = EIO;
return -1;
}
- } else if (op->type == kCowXorOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowXorOp) {
borrowed_fd fd = GetSourceFd();
if (fd < 0) {
// GetSourceFd sets errno.
@@ -208,7 +208,8 @@
((char*)buffer)[i] ^= data[i];
}
} else {
- LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
+ LOG(ERROR) << "CompressedSnapshotReader unknown op type: "
+ << uint32_t(GetCowOpSourceInfoType(*op));
errno = EINVAL;
return -1;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index e59bd92..d962875 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -25,6 +25,7 @@
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
#include "cow_decompress.h"
+#include "libsnapshot/cow_format.h"
#include "writer_v2.h"
using android::base::unique_fd;
@@ -85,11 +86,10 @@
size_t i = 0;
while (!iter->AtEnd()) {
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10 + i);
- ASSERT_EQ(op->source, 1000 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
iter->Next();
i += 1;
}
@@ -132,11 +132,10 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(op->source, 20);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
std::string sink(data.size(), '\0');
@@ -144,8 +143,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_FALSE(GetCowOpSourceInfoCompression(op));
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -156,21 +154,19 @@
op = iter->Get();
// Note: the zero operation gets split into two blocks.
- ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -211,11 +207,10 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(op->source, 20);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
std::string sink(data.size(), '\0');
@@ -223,11 +218,10 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowXorOp);
- ASSERT_FALSE(GetCowOpSourceInfoCompression(op));
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowXorOp);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
@@ -236,21 +230,19 @@
op = iter->Get();
// Note: the zero operation gets split into two blocks.
- ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -282,8 +274,7 @@
std::string sink(data.size(), '\0');
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -335,16 +326,16 @@
while (!iter->AtEnd()) {
auto op = iter->Get();
- if (op->type == kCowXorOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowXorOp) {
total_blocks += 1;
std::string sink(xor_data.size(), '\0');
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, xor_data);
}
- if (op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 100) {
data.resize(options.block_size);
@@ -409,7 +400,7 @@
while (!iter->AtEnd()) {
auto op = iter->Get();
- if (op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 50) {
data.resize(options.block_size);
@@ -529,8 +520,7 @@
std::string sink(data.size(), '\0');
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -540,7 +530,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
@@ -548,7 +538,6 @@
sink = {};
sink.resize(data2.size(), '\0');
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 41); // compressed!
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -558,7 +547,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowClusterOp);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -592,8 +581,7 @@
std::string sink(options.block_size, '\0');
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
}
@@ -666,7 +654,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
@@ -676,14 +664,14 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 3);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 3);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
@@ -729,14 +717,14 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
iter->Next();
@@ -787,8 +775,8 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -838,7 +826,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(0, options.block_size));
@@ -848,7 +836,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));
@@ -856,26 +844,26 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
iter->Next();
@@ -919,7 +907,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(0, options.block_size));
@@ -927,52 +915,52 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowCopyOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 6);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 6);
iter->Next();
@@ -1018,14 +1006,14 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 50);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowLabelOp);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 50);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
@@ -1033,7 +1021,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(GetCowOpSourceInfoType(*op), kCowClusterOp);
iter->Next();
@@ -1130,12 +1118,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
num_replace++;
ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op->type == kCowClusterOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1191,12 +1179,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
num_replace++;
ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op->type == kCowClusterOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1242,12 +1230,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowReplaceOp) {
num_replace++;
ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op->type == kCowClusterOp) {
+ } else if (GetCowOpSourceInfoType(*op) == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1504,6 +1492,37 @@
ASSERT_FALSE(reader.VerifyMergeOps());
}
+unique_fd OpenTestFile(const std::string& file, int flags) {
+ std::string path = "tools/testdata/" + file;
+
+ unique_fd fd(open(path.c_str(), flags));
+ if (fd >= 0) {
+ return fd;
+ }
+
+ path = android::base::GetExecutableDirectory() + "/" + path;
+ return unique_fd{open(path.c_str(), flags)};
+}
+
+TEST_F(CowTest, CompatibilityTest) {
+ std::string filename = "cow_v2";
+ auto fd = OpenTestFile(filename, O_RDONLY);
+ if (fd.get() == -1) {
+ LOG(ERROR) << filename << " not found";
+ GTEST_SKIP();
+ }
+ CowReader reader;
+ reader.Parse(fd);
+
+ 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);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
new file mode 100644
index 0000000..a4d617f
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -0,0 +1,62 @@
+// 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 <sys/stat.h>
+
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include "cow_decompress.h"
+#include "libsnapshot/cow_format.h"
+#include "writer_v3.h"
+using android::base::unique_fd;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+namespace snapshot {
+
+class CowOperationV3Test : public ::testing::Test {
+ protected:
+ virtual void SetUp() override {
+ cow_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_->fd, 0) << strerror(errno);
+ }
+
+ virtual void TearDown() override { cow_ = nullptr; }
+
+ unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }
+
+ std::unique_ptr<TemporaryFile> cow_;
+};
+
+TEST_F(CowOperationV3Test, CowTypeTest) {
+ CowOperationV3 new_op;
+ CowOperationV2 old_op;
+ old_op.type = kCowReplaceOp;
+ ASSERT_NE(GetCowOpSourceInfoType(new_op), old_op.type);
+ SetCowOpSourceInfoType(&new_op, old_op.type);
+ ASSERT_EQ(GetCowOpSourceInfoType(new_op), old_op.type);
+}
+
+} // namespace snapshot
+} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
index ff34c59..2ffc37b 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
@@ -191,5 +191,16 @@
block_dev_size);
}
+bool CowWriterBase::Sync() {
+ if (is_dev_null_) {
+ return true;
+ }
+ if (fsync(fd_.get()) < 0) {
+ PLOG(ERROR) << "fsync failed";
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
index c8b4772..cdf6222 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -34,6 +34,7 @@
// If the given label is not found, Initialize will fail.
virtual bool Initialize(std::optional<uint64_t> label = {}) = 0;
+ bool Sync();
bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index ee445a2..e51dbde 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -110,7 +110,7 @@
header_.prefix.minor_version = kCowVersionMinor;
header_.prefix.header_size = sizeof(CowHeader);
header_.footer_size = sizeof(CowFooter);
- header_.op_size = sizeof(CowOperation);
+ header_.op_size = sizeof(CowOperationV2);
header_.block_size = options_.block_size;
header_.num_merge_ops = options_.num_merge_ops;
header_.cluster_ops = options_.cluster_ops;
@@ -159,9 +159,9 @@
struct iovec* cowop_ptr = cowop_vec_.get();
struct iovec* data_ptr = data_vec_.get();
for (size_t i = 0; i < header_.cluster_ops; i++) {
- std::unique_ptr<CowOperation> op = std::make_unique<CowOperation>();
+ std::unique_ptr<CowOperationV2> op = std::make_unique<CowOperationV2>();
cowop_ptr[i].iov_base = op.get();
- cowop_ptr[i].iov_len = sizeof(CowOperation);
+ cowop_ptr[i].iov_len = sizeof(CowOperationV2);
opbuffer_vec_.push_back(std::move(op));
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(header_.block_size * 2);
@@ -214,19 +214,19 @@
}
void CowWriterV2::InitPos() {
- next_op_pos_ = sizeof(header_) + header_.buffer_size;
- cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
+ next_op_pos_ = sizeof(CowHeader) + header_.buffer_size;
+ cluster_size_ = header_.cluster_ops * sizeof(CowOperationV2);
if (header_.cluster_ops) {
next_data_pos_ = next_op_pos_ + cluster_size_;
} else {
- next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+ next_data_pos_ = next_op_pos_ + sizeof(CowOperationV2);
}
current_cluster_size_ = 0;
current_data_size_ = 0;
}
bool CowWriterV2::OpenForWrite() {
- // This limitation is tied to the data field size in CowOperation.
+ // This limitation is tied to the data field size in CowOperationV2.
if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Block size is too large";
return false;
@@ -313,7 +313,7 @@
CHECK(!merge_in_progress_);
for (size_t i = 0; i < num_blocks; i++) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowCopyOp;
op.new_block = new_block + i;
op.source = old_block + i;
@@ -399,7 +399,7 @@
num_blocks -= pending_blocks;
while (i < size / header_.block_size && pending_blocks) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.new_block = new_block_start + i;
op.type = type;
if (type == kCowXorOp) {
@@ -451,7 +451,7 @@
bool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
CHECK(!merge_in_progress_);
for (uint64_t i = 0; i < num_blocks; i++) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowZeroOp;
op.new_block = new_block_start + i;
op.source = 0;
@@ -462,7 +462,7 @@
bool CowWriterV2::EmitLabel(uint64_t label) {
CHECK(!merge_in_progress_);
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowLabelOp;
op.source = label;
return WriteOperation(op) && Sync();
@@ -473,7 +473,7 @@
size_t to_add = 0;
size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
while (num_ops > 0) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowSequenceOp;
op.source = next_data_pos_;
to_add = std::min(num_ops, max_ops);
@@ -489,16 +489,16 @@
}
bool CowWriterV2::EmitCluster() {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowClusterOp;
// Next cluster starts after remainder of current cluster and the next data block.
- op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
+ op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperationV2);
return WriteOperation(op);
}
bool CowWriterV2::EmitClusterIfNeeded() {
// If there isn't room for another op and the cluster end op, end the current cluster
- if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
+ if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperationV2)) {
if (!EmitCluster()) return false;
}
return true;
@@ -539,7 +539,7 @@
extra_cluster = true;
}
- footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperation);
+ footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperationV2);
if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "Failed to seek to footer position.";
return false;
@@ -611,9 +611,9 @@
if (op_vec_index_) {
ret = pwritev(fd_.get(), cowop_vec_.get(), op_vec_index_, current_op_pos_);
- if (ret != (op_vec_index_ * sizeof(CowOperation))) {
- PLOG(ERROR) << "pwritev failed for CowOperation. Expected: "
- << (op_vec_index_ * sizeof(CowOperation));
+ if (ret != (op_vec_index_ * sizeof(CowOperationV2))) {
+ PLOG(ERROR) << "pwritev failed for CowOperationV2. Expected: "
+ << (op_vec_index_ * sizeof(CowOperationV2));
return false;
}
}
@@ -635,17 +635,16 @@
return true;
}
-bool CowWriterV2::WriteOperation(const CowOperation& op, const void* data, size_t size) {
- if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op))) {
- return false;
- }
- if (!EnsureSpaceAvailable(next_data_pos_ + size)) {
+bool CowWriterV2::WriteOperation(const CowOperationV2& op, const void* data, size_t size) {
+ if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op)) ||
+ !EnsureSpaceAvailable(next_data_pos_ + size)) {
return false;
}
if (batch_write_) {
- CowOperation* cow_op = reinterpret_cast<CowOperation*>(cowop_vec_[op_vec_index_].iov_base);
- std::memcpy(cow_op, &op, sizeof(CowOperation));
+ CowOperationV2* cow_op =
+ reinterpret_cast<CowOperationV2*>(cowop_vec_[op_vec_index_].iov_base);
+ std::memcpy(cow_op, &op, sizeof(CowOperationV2));
op_vec_index_ += 1;
if (data != nullptr && size > 0) {
@@ -683,7 +682,7 @@
return EmitClusterIfNeeded();
}
-void CowWriterV2::AddOperation(const CowOperation& op) {
+void CowWriterV2::AddOperation(const CowOperationV2& op) {
footer_.op.num_ops++;
if (op.type == kCowClusterOp) {
@@ -695,7 +694,7 @@
}
next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
- next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
+ next_op_pos_ += sizeof(CowOperationV2) + GetNextOpOffset(op, header_.cluster_ops);
}
bool CowWriterV2::WriteRawData(const void* data, const size_t size) {
@@ -705,17 +704,6 @@
return true;
}
-bool CowWriterV2::Sync() {
- if (is_dev_null_) {
- return true;
- }
- if (fsync(fd_.get()) < 0) {
- PLOG(ERROR) << "fsync failed";
- return false;
- }
- return true;
-}
-
bool CowWriterV2::Truncate(off_t length) {
if (is_dev_null_ || is_block_device_) {
return true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 131a068..ce97b01 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -50,15 +50,14 @@
bool OpenForAppend(uint64_t label);
bool GetDataPos(uint64_t* pos);
bool WriteRawData(const void* data, size_t size);
- bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
- void AddOperation(const CowOperation& op);
+ bool WriteOperation(const CowOperationV2& op, const void* data = nullptr, size_t size = 0);
+ void AddOperation(const CowOperationV2& op);
void InitPos();
void InitBatchWrites();
void InitWorkers();
bool FlushCluster();
bool CompressBlocks(size_t num_blocks, const void* data);
- bool Sync();
bool Truncate(off_t length);
bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
@@ -84,7 +83,7 @@
std::vector<std::basic_string<uint8_t>> compressed_buf_;
std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
- std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
+ std::vector<std::unique_ptr<CowOperationV2>> opbuffer_vec_;
std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
std::unique_ptr<struct iovec[]> cowop_vec_;
int op_vec_index_ = 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
new file mode 100644
index 0000000..2b9867e
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright (C) 2020 The Android Open Source_info 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 "writer_v3.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
+#include <zlib.h>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+using android::base::unique_fd;
+
+CowWriterV3::CowWriterV3(const CowOptions& options, unique_fd&& fd)
+ : CowWriterBase(options, std::move(fd)) {}
+
+CowWriterV3::~CowWriterV3() {}
+
+bool CowWriterV3::Initialize(std::optional<uint64_t> label) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (label) return false;
+ return false;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+bool CowWriterV3::EmitLabel(uint64_t label) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (label) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (num_ops && data) return false;
+ return false;
+}
+
+bool CowWriterV3::Finalize() {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ return false;
+}
+
+uint64_t CowWriterV3::GetCowSize() {
+ LOG(ERROR) << __LINE__ << " " << __FILE__
+ << " <- Get Cow Size function here should never be called";
+ return 0;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
new file mode 100644
index 0000000..ddd7287
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -0,0 +1,43 @@
+// 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 <future>
+#include "writer_base.h"
+
+namespace android {
+namespace snapshot {
+
+class CowWriterV3 : public CowWriterBase {
+ public:
+ explicit CowWriterV3(const CowOptions& options, android::base::unique_fd&& fd);
+ ~CowWriterV3() override;
+
+ bool Initialize(std::optional<uint64_t> label = {}) override;
+ bool Finalize() override;
+ uint64_t GetCowSize() override;
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) override;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ virtual bool EmitLabel(uint64_t label) override;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 7057223..5bc7e65 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -131,15 +131,28 @@
return is_optimized;
}
-void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
+bool WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
unsigned int sectors_per_block) {
const auto block_boundary = de.start_block() + de.num_blocks();
for (auto b = de.start_block(); b < block_boundary; ++b) {
for (unsigned int s = 0; s < sectors_per_block; ++s) {
- const auto sector_id = b * sectors_per_block + s;
+ // sector_id = b * sectors_per_block + s;
+ uint64_t block_start_sector_id;
+ if (__builtin_mul_overflow(b, sectors_per_block, &block_start_sector_id)) {
+ LOG(ERROR) << "Integer overflow when calculating sector id (" << b << " * "
+ << sectors_per_block << ")";
+ return false;
+ }
+ uint64_t sector_id;
+ if (__builtin_add_overflow(block_start_sector_id, s, §or_id)) {
+ LOG(ERROR) << "Integer overflow when calculating sector id ("
+ << block_start_sector_id << " + " << s << ")";
+ return false;
+ }
sc->WriteSector(sector_id);
}
}
+ return true;
}
std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
@@ -167,7 +180,7 @@
// Allocate space for extra extents (if any). These extents are those that can be
// used for error corrections or to store verity hash trees.
for (const auto& de : extra_extents) {
- WriteExtent(&sc, de, sectors_per_block);
+ if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
}
if (update == nullptr) return sc.cow_size_bytes();
@@ -182,7 +195,7 @@
}
for (const auto& de : written_op->dst_extents()) {
- WriteExtent(&sc, de, sectors_per_block);
+ if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
}
}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 51389a0..4743a42 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -83,6 +83,8 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+ "/metadata/ota/snapshot-boot-without-slot-switch";
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
@@ -217,6 +219,12 @@
auto file = LockExclusive();
if (!file) return false;
+ if (IsSnapshotWithoutSlotSwitch()) {
+ LOG(ERROR) << "Cannot cancel the snapshots as partitions are mounted off the snapshots on "
+ "current slot.";
+ return false;
+ }
+
UpdateState state = ReadUpdateState(file.get());
if (state == UpdateState::None) {
RemoveInvalidSnapshots(file.get());
@@ -299,10 +307,9 @@
// - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
// matches the incoming update.
std::vector<std::string> files = {
- GetSnapshotBootIndicatorPath(),
- GetRollbackIndicatorPath(),
- GetForwardMergeIndicatorPath(),
- GetOldPartitionMetadataPath(),
+ GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath(),
+ GetForwardMergeIndicatorPath(), GetOldPartitionMetadataPath(),
+ GetBootSnapshotsWithoutSlotSwitchPath(),
};
for (const auto& file : files) {
RemoveFileIfExists(file);
@@ -483,6 +490,32 @@
LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
return false;
}
+ } else if (IsSnapshotWithoutSlotSwitch()) {
+ // When snapshots are on current slot, we determine the size
+ // of block device based on the number of COW operations. We cannot
+ // use base device as it will be from older image.
+ size_t num_ops = 0;
+ uint64_t dev_sz = 0;
+ unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open " << cow_file;
+ return false;
+ }
+
+ CowReader reader;
+ if (!reader.Parse(std::move(fd))) {
+ LOG(ERROR) << "Failed to parse cow " << cow_file;
+ return false;
+ }
+
+ const auto& header = reader.GetHeader();
+ if (header.prefix.major_version > 2) {
+ LOG(ERROR) << "COW format not supported";
+ return false;
+ }
+ num_ops = reader.get_num_total_data_ops();
+ dev_sz = (num_ops * header.block_size);
+ base_sectors = dev_sz >> 9;
} else {
// For userspace snapshots, the size of the base device is taken as the
// size of the dm-user block device. Since there is no pseudo mapping
@@ -1479,6 +1512,10 @@
return result;
}
+std::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {
+ return metadata_dir_ + "/" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);
+}
+
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
}
@@ -2120,6 +2157,10 @@
return state;
}
+bool SnapshotManager::IsSnapshotWithoutSlotSwitch() {
+ return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);
+}
+
bool SnapshotManager::UpdateUsesCompression() {
auto lock = LockShared();
if (!lock) return false;
@@ -2212,6 +2253,13 @@
}
bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+ if (IsSnapshotWithoutSlotSwitch()) {
+ if (GetCurrentSlot() != Slot::Source) {
+ LOG(ERROR) << "Snapshots marked to boot without slot switch; but slot is wrong";
+ return false;
+ }
+ return true;
+ }
// If we fail to read, we'll wind up using CreateLogicalPartitions, which
// will create devices that look like the old slot, except with extra
// content at the end of each device. This will confuse dm-verity, and
@@ -2347,7 +2395,8 @@
// completed, live_snapshot_status is set to nullopt.
std::optional<SnapshotStatus> live_snapshot_status;
do {
- if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+ if (!IsSnapshotWithoutSlotSwitch() &&
+ !(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
<< params.GetPartitionName();
break;
@@ -2703,7 +2752,7 @@
// to unmap; hence, we can't be deleting the device
// as the table would be mounted off partitions and will fail.
if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
- if (!DeleteDeviceIfExists(dm_user_name)) {
+ if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {
LOG(ERROR) << "Cannot unmap " << dm_user_name;
return false;
}
@@ -3098,7 +3147,7 @@
return true;
}
-bool SnapshotManager::EnsureSnapuserdConnected() {
+bool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {
if (snapuserd_client_) {
return true;
}
@@ -3107,7 +3156,7 @@
return false;
}
- snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+ snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
@@ -4372,13 +4421,70 @@
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
auto slot = GetCurrentSlot();
if (slot == Slot::Target) {
+ // Merge in-progress
if (IsSnapuserdRequired()) {
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;
}
+bool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ auto contents = device_->GetSlotSuffix();
+ // This is the indicator which tells first-stage init
+ // to boot from snapshots even though there was no slot-switch
+ auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();
+ if (!WriteStringToFileAtomic(contents, boot_file)) {
+ PLOG(ERROR) << "write failed: " << boot_file;
+ return false;
+ }
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ update_status.set_state(UpdateState::Initiated);
+ update_status.set_userspace_snapshots(true);
+ update_status.set_using_snapuserd(true);
+ if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+ return false;
+ }
+ return true;
+}
+
+bool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());
+ android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ update_status.set_state(UpdateState::Cancelled);
+ if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 3b6d26a..e506110 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2559,6 +2559,56 @@
}
}
+TEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {
+ MountMetadata();
+ AddOperationForPartitions();
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ if (!sm->UpdateUsesUserSnapshots()) {
+ GTEST_SKIP() << "Test does not apply as UserSnapshots aren't enabled.";
+ }
+
+ ASSERT_TRUE(WriteSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ if (ShouldSkipLegacyMerging()) {
+ GTEST_SKIP() << "Skipping legacy merge test";
+ }
+ // Mark the indicator
+ ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());
+
+ ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+ sm->set_use_first_stage_snapuserd(true);
+
+ ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());
+
+ // Map snapshots
+ ASSERT_TRUE(sm->MapAllSnapshots(10s));
+
+ // New updates should fail
+ ASSERT_FALSE(sm->BeginUpdate());
+
+ // Snapshots cannot be cancelled
+ ASSERT_FALSE(sm->CancelUpdate());
+
+ // Merge cannot start
+ ASSERT_FALSE(sm->InitiateMerge());
+
+ // Read bytes back and verify they match the cache.
+ ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+ // Remove the indicators
+ ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());
+
+ // Ensure snapshots are still mounted
+ ASSERT_TRUE(sm->IsUserspaceSnapshotUpdateInProgress());
+
+ // Cleanup snapshots
+ ASSERT_TRUE(sm->UnmapAllSnapshots());
+}
+
TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
AddOperationForPartitions();
// Execute the update.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 38eb719..ebaca2d 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -17,14 +17,25 @@
#include <sysexits.h>
#include <chrono>
+#include <filesystem>
+#include <fstream>
+#include <future>
#include <iostream>
#include <map>
#include <sstream>
+#include <thread>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
@@ -33,6 +44,8 @@
#include <libsnapshot/snapshot.h>
#include <storage_literals/storage_literals.h>
+#include "partition_cow_creator.h"
+
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
#include <BootControlClient.h>
#endif
@@ -56,13 +69,211 @@
" merge\n"
" Deprecated.\n"
" map\n"
- " Map all partitions at /dev/block/mapper\n";
+ " Map all partitions at /dev/block/mapper\n"
+ " map-snapshots <directory where snapshot patches are present>\n"
+ " Map all snapshots based on patches present in the directory\n"
+ " unmap-snapshots\n"
+ " Unmap all pre-created snapshots\n"
+ " delete-snapshots\n"
+ " Delete all pre-created snapshots\n"
+ " revert-snapshots\n"
+ " Prepares devices to boot without snapshots on next boot.\n"
+ " This does not delete the snapshot. It only removes the indicators\n"
+ " so that first stage init will not mount from snapshots.\n";
return EX_USAGE;
}
namespace android {
namespace snapshot {
+class MapSnapshots {
+ public:
+ MapSnapshots(std::string path = "");
+ bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
+ bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
+ bool FinishSnapshotWrites();
+ bool UnmapCowImagePath(std::string& name);
+ bool DeleteSnapshots();
+ bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
+ bool BeginUpdate();
+
+ private:
+ std::optional<std::string> GetCowImagePath(std::string& name);
+ bool WriteSnapshotPatch(std::string cow_device, std::string patch);
+ std::unique_ptr<SnapshotManager::LockedFile> lock_;
+ std::unique_ptr<SnapshotManager> sm_;
+ std::vector<std::future<bool>> threads_;
+ std::string snapshot_dir_path_;
+};
+
+MapSnapshots::MapSnapshots(std::string path) {
+ sm_ = SnapshotManager::New();
+ if (!sm_) {
+ std::cout << "Failed to create snapshotmanager";
+ exit(1);
+ }
+ snapshot_dir_path_ = path + "/";
+}
+
+bool MapSnapshots::BeginUpdate() {
+ lock_ = sm_->LockExclusive();
+ std::vector<std::string> snapshots;
+ sm_->ListSnapshots(lock_.get(), &snapshots);
+ if (!snapshots.empty()) {
+ // Snapshots are already present.
+ return true;
+ }
+
+ lock_ = nullptr;
+ if (!sm_->BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+ lock_ = sm_->LockExclusive();
+ return true;
+}
+
+bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
+ std::string parsing_file = snapshot_dir_path_ + patchfile;
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open file: " << parsing_file;
+ return false;
+ }
+
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ LOG(ERROR) << "Could not determine block device size: " << parsing_file;
+ return false;
+ }
+
+ const int block_sz = 4_KiB;
+ dev_sz += block_sz - 1;
+ dev_sz &= ~(block_sz - 1);
+
+ SnapshotStatus status;
+ status.set_state(SnapshotState::CREATED);
+ status.set_using_snapuserd(true);
+ status.set_old_partition_size(0);
+ status.set_name(partition_name);
+ status.set_cow_file_size(dev_sz);
+ status.set_cow_partition_size(0);
+
+ PartitionCowCreator cow_creator;
+ cow_creator.using_snapuserd = true;
+
+ if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {
+ LOG(ERROR) << "CreateSnapshot failed";
+ return false;
+ }
+
+ if (!sm_->CreateCowImage(lock_.get(), partition_name)) {
+ LOG(ERROR) << "CreateCowImage failed";
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {
+ auto cow_dev = sm_->MapCowImage(name, 5s);
+ if (!cow_dev.has_value()) {
+ LOG(ERROR) << "Failed to get COW device path";
+ return std::nullopt;
+ }
+
+ LOG(INFO) << "COW Device path: " << cow_dev.value();
+ return cow_dev;
+}
+
+bool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {
+ std::string patch_file = snapshot_dir_path_ + patch;
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open file: " << patch_file;
+ return false;
+ }
+
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ std::cout << "Could not determine block device size: " << patch_file;
+ return false;
+ }
+
+ android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));
+ if (cfd < 0) {
+ LOG(ERROR) << "Failed to open file: " << cow_device;
+ return false;
+ }
+
+ const uint64_t read_sz = 1_MiB;
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+ off_t file_offset = 0;
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ PLOG(ERROR) << "ReadFullyAtOffset failed";
+ return false;
+ }
+
+ if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {
+ PLOG(ERROR) << "WriteFullyAtOffset failed";
+ return false;
+ }
+ file_offset += to_read;
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+ fsync(cfd.get());
+ return true;
+}
+
+bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {
+ auto path = GetCowImagePath(pname);
+ if (!path.has_value()) {
+ return false;
+ }
+ threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,
+ path.value(), snapshot_patch));
+ return true;
+}
+
+bool MapSnapshots::FinishSnapshotWrites() {
+ bool ret = true;
+ for (auto& t : threads_) {
+ ret = t.get() && ret;
+ }
+
+ lock_ = nullptr;
+ if (ret) {
+ LOG(INFO) << "Pre-created snapshots successfully copied";
+ if (!sm_->FinishedSnapshotWrites(false)) {
+ return false;
+ }
+ return sm_->BootFromSnapshotsWithoutSlotSwitch();
+ }
+
+ LOG(ERROR) << "Snapshot copy failed";
+ return false;
+}
+
+bool MapSnapshots::UnmapCowImagePath(std::string& name) {
+ return sm_->UnmapCowImage(name);
+}
+
+bool MapSnapshots::DeleteSnapshots() {
+ lock_ = sm_->LockExclusive();
+ if (!sm_->RemoveAllUpdateState(lock_.get())) {
+ LOG(ERROR) << "Remove All Update State failed";
+ return false;
+ }
+ return true;
+}
+
bool DumpCmdHandler(int /*argc*/, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
return SnapshotManager::New()->Dump(std::cout);
@@ -85,6 +296,134 @@
return false;
}
+bool GetVerityPartitions(std::vector<std::string>& partitions) {
+ auto& dm = android::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& block_device : dm_block_devices) {
+ std::string dm_block_name = block_device.first;
+ std::string slot_suffix = fs_mgr_get_slot_suffix();
+ std::string partition = dm_block_name + slot_suffix;
+ partitions.push_back(partition);
+ }
+ return true;
+}
+
+bool UnMapPrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ std::vector<std::string> partitions;
+ if (!GetVerityPartitions(partitions)) {
+ return false;
+ }
+
+ MapSnapshots snapshot;
+ for (auto partition : partitions) {
+ if (!snapshot.UnmapCowImagePath(partition)) {
+ LOG(ERROR) << "UnmapCowImagePath failed: " << partition;
+ }
+ }
+ return true;
+}
+
+bool RemovePrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return false;
+ }
+
+ MapSnapshots snapshot;
+ if (!snapshot.CleanupSnapshot()) {
+ LOG(ERROR) << "CleanupSnapshot failed";
+ return false;
+ }
+ return true;
+}
+
+bool DeletePrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ MapSnapshots snapshot;
+ return snapshot.DeleteSnapshots();
+}
+
+bool MapPrecreatedSnapshots(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ if (argc < 3) {
+ std::cerr << " map-snapshots <directory location where snapshot patches are present>"
+ " Map all snapshots based on patches present in the directory\n";
+ return false;
+ }
+
+ std::string path = std::string(argv[2]);
+ std::vector<std::string> patchfiles;
+
+ for (const auto& entry : std::filesystem::directory_iterator(path)) {
+ if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
+ patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
+ }
+ }
+ auto& dm = android::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;
+ }
+
+ std::vector<std::pair<std::string, std::string>> partitions;
+ for (auto& patchfile : patchfiles) {
+ auto npos = patchfile.rfind(".patch");
+ auto dm_block_name = patchfile.substr(0, npos);
+ if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {
+ std::string slot_suffix = fs_mgr_get_slot_suffix();
+ std::string partition = dm_block_name + slot_suffix;
+ partitions.push_back(std::make_pair(partition, patchfile));
+ }
+ }
+
+ MapSnapshots cow(path);
+ if (!cow.BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+
+ for (auto& pair : partitions) {
+ if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
+ LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
+ return false;
+ }
+ if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {
+ LOG(ERROR) << "InitiateThreadedSnapshotWrite failed for: " << pair.first;
+ return false;
+ }
+ }
+
+ return cow.FinishSnapshotWrites();
+}
+
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
bool CreateTestUpdate(SnapshotManager* sm) {
chromeos_update_engine::DeltaArchiveManifest manifest;
@@ -137,8 +476,8 @@
.block_device = fs_mgr_get_super_partition_name(target_slot_number),
.metadata_slot = {target_slot_number},
.partition_name = system_target_name,
- .partition_opener = &opener,
.timeout_ms = 10s,
+ .partition_opener = &opener,
};
auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
if (!writer) {
@@ -211,6 +550,10 @@
{"test-blank-ota", TestOtaHandler},
#endif
{"unmap", UnmapCmdHandler},
+ {"map-snapshots", MapPrecreatedSnapshots},
+ {"unmap-snapshots", UnMapPrecreatedSnapshots},
+ {"delete-snapshots", DeletePrecreatedSnapshots},
+ {"revert-snapshots", RemovePrecreatedSnapshots},
// clang-format on
};
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index e56ffbe..1b0c563 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -74,6 +74,11 @@
"user-space-merge/worker.cpp",
"utility.cpp",
],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
static_libs: [
"libbase",
"libdm",
@@ -104,6 +109,12 @@
"user-space-merge/snapuserd_server.cpp",
],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+
static_libs: [
"libbase",
"libbrotli",
@@ -226,6 +237,11 @@
"testing/host_harness.cpp",
"user-space-merge/snapuserd_test.cpp",
],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"liblog",
@@ -277,3 +293,48 @@
"vts",
],
}
+
+cc_binary_host {
+ name: "snapuserd_extractor",
+ defaults: [
+ "fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
+ ],
+ srcs: [
+ "testing/dm_user_harness.cpp",
+ "testing/harness.cpp",
+ "testing/host_harness.cpp",
+ "user-space-merge/extractor.cpp",
+ "snapuserd_extractor.cpp",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libcutils_sockets",
+ "libdm",
+ "libext2_uuid",
+ "libext4_utils",
+ "libfs_mgr_file_wait",
+ "libgflags",
+ "libsnapshot_cow",
+ "libsnapuserd",
+ "liburing",
+ "libz",
+ ],
+ include_dirs: [
+ "bionic/libc/kernel",
+ ".",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ "libfiemap_headers",
+ ],
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 71664bf..0be6ff5 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -25,6 +25,7 @@
#include <csignal>
#include <optional>
#include <set>
+#include "libsnapshot/cow_format.h"
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -406,9 +407,9 @@
break;
}
- if (cow_op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*cow_op) == kCowReplaceOp) {
replace_ops++;
- } else if (cow_op->type == kCowZeroOp) {
+ } else if (GetCowOpSourceInfoType(*cow_op) == kCowZeroOp) {
zero_ops++;
}
@@ -508,7 +509,7 @@
// the merge of operations are done based on the ops present
// in the file.
//===========================================================
- uint64_t block_source = GetCowOpSourceInfoData(cow_op);
+ uint64_t block_source = GetCowOpSourceInfoData(*cow_op);
if (prev_id.has_value()) {
if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
break;
@@ -540,7 +541,7 @@
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
offset += sizeof(struct disk_exception);
num_ops += 1;
- if (cow_op->type == kCowCopyOp) {
+ if (GetCowOpSourceInfoType(*cow_op) == kCowCopyOp) {
copy_ops++;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
index d5fbe91..b2a4f2c 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "libsnapshot/cow_format.h"
#include "snapuserd.h"
#include <csignal>
@@ -172,7 +173,7 @@
}
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- uint64_t source_block = GetCowOpSourceInfoData(cow_op);
+ uint64_t source_block = GetCowOpSourceInfoData(*cow_op);
if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
overlap_ = true;
}
@@ -191,8 +192,8 @@
// Get the first block with offset
const CowOperation* cow_op = GetRAOpIter();
CHECK_NE(cow_op, nullptr);
- *source_offset = GetCowOpSourceInfoData(cow_op);
- if (cow_op->type == kCowCopyOp) {
+ *source_offset = GetCowOpSourceInfoData(*cow_op);
+ if (GetCowOpSourceInfoData(*cow_op) == kCowCopyOp) {
*source_offset *= BLOCK_SZ;
}
RAIterNext();
@@ -210,8 +211,8 @@
while (!RAIterDone() && num_ops) {
const CowOperation* op = GetRAOpIter();
CHECK_NE(op, nullptr);
- uint64_t next_offset = GetCowOpSourceInfoData(op);
- if (op->type == kCowCopyOp) {
+ uint64_t next_offset = GetCowOpSourceInfoData(*op);
+ if (GetCowOpSourceInfoData(*op) == kCowCopyOp) {
next_offset *= BLOCK_SZ;
}
if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
index 559dcc7..0930a5d 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "libsnapshot/cow_format.h"
#include "snapuserd.h"
#include <csignal>
@@ -103,7 +104,7 @@
ssize_t rv = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
if (rv != BLOCK_SZ) {
SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block
- << ", return = " << rv;
+ << ", return = " << rv << ", COW operation = " << *cow_op;
return false;
}
return true;
@@ -177,7 +178,7 @@
return false;
}
- switch (cow_op->type) {
+ switch (GetCowOpSourceInfoType(*cow_op)) {
case kCowReplaceOp: {
return ProcessReplaceOp(cow_op);
}
@@ -191,7 +192,8 @@
}
default: {
- SNAP_LOG(ERROR) << "Unsupported operation-type found: " << cow_op->type;
+ SNAP_LOG(ERROR) << "Unsupported operation-type found: "
+ << GetCowOpSourceInfoType(*cow_op);
}
}
return false;
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
new file mode 100644
index 0000000..f46cd5b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
@@ -0,0 +1,68 @@
+// 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 <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
+#include "user-space-merge/extractor.h"
+
+using namespace std::string_literals;
+
+DEFINE_string(base, "", "Base device/image");
+DEFINE_string(cow, "", "COW device/image");
+DEFINE_string(out, "", "Output path");
+DEFINE_int32(num_sectors, 0, "Number of sectors to read");
+
+int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ android::base::InitLogging(argv);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_out.empty()) {
+ LOG(ERROR) << "Missing -out argument.";
+ return 1;
+ }
+ if (FLAGS_base.empty()) {
+ LOG(ERROR) << "Missing -base argument.";
+ return 1;
+ }
+ if (FLAGS_cow.empty()) {
+ LOG(ERROR) << "missing -out argument.";
+ return 1;
+ }
+ if (!FLAGS_num_sectors) {
+ LOG(ERROR) << "missing -num_sectors argument.";
+ return 1;
+ }
+
+ android::snapshot::Extractor extractor(FLAGS_base, FLAGS_cow);
+ if (!extractor.Init()) {
+ return 1;
+ }
+ if (!extractor.Extract(FLAGS_num_sectors, FLAGS_out)) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
new file mode 100644
index 0000000..c5718d5
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -0,0 +1,90 @@
+// 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 "extractor.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+using namespace std::string_literals;
+
+namespace android {
+namespace snapshot {
+
+Extractor::Extractor(const std::string& base_path, const std::string& cow_path)
+ : base_path_(base_path), cow_path_(cow_path), control_name_("test") {}
+
+bool Extractor::Init() {
+ auto opener = factory_.CreateTestOpener(control_name_);
+ handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
+ opener, 1, false, false);
+ if (!handler_->InitCowDevice()) {
+ return false;
+ }
+ if (!handler_->InitializeWorkers()) {
+ return false;
+ }
+
+ read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,
+ handler_->GetSharedPtr(), opener);
+ if (!read_worker_->Init()) {
+ return false;
+ }
+ block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+ handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+ return true;
+}
+
+Extractor::~Extractor() {
+ factory_.DeleteQueue(control_name_);
+}
+
+bool Extractor::Extract(off_t num_sectors, const std::string& out_path) {
+ unique_fd out_fd(open(out_path.c_str(), O_RDWR | O_CLOEXEC | O_TRUNC | O_CREAT, 0664));
+ if (out_fd < 0) {
+ PLOG(ERROR) << "Could not open for writing: " << out_path;
+ return false;
+ }
+
+ for (off_t i = 0; i < num_sectors; i++) {
+ if (!read_worker_->RequestSectors(i, 512)) {
+ LOG(ERROR) << "Read sector " << i << " failed.";
+ return false;
+ }
+ std::string result = std::move(block_server_->sent_io());
+ off_t offset = i * 512;
+ if (!android::base::WriteFullyAtOffset(out_fd, result.data(), result.size(), offset)) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
new file mode 100644
index 0000000..65285b1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
@@ -0,0 +1,51 @@
+// 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 <string>
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "snapuserd_core.h"
+#include "testing/host_harness.h"
+
+namespace android {
+namespace snapshot {
+
+class Extractor final {
+ public:
+ Extractor(const std::string& base_path, const std::string& cow_path);
+ ~Extractor();
+
+ bool Init();
+ bool Extract(off_t num_sectors, const std::string& out_path);
+
+ private:
+ std::string base_path_;
+ std::string cow_path_;
+
+ TestBlockServerFactory factory_;
+ HostTestHarness harness_;
+ std::string control_name_;
+ std::shared_ptr<SnapshotHandler> handler_;
+ std::unique_ptr<ReadWorker> read_worker_;
+ std::future<bool> handler_thread_;
+ TestBlockServer* block_server_ = nullptr;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index 11b8d7c..d06ba58 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -17,6 +17,7 @@
#include <pthread.h>
+#include "libsnapshot/cow_format.h"
#include "snapuserd_core.h"
#include "utility.h"
@@ -114,13 +115,13 @@
SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
return false;
}
- if (cow_op->type == kCowReplaceOp) {
+ if (GetCowOpSourceInfoType(*cow_op) == kCowReplaceOp) {
if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
SNAP_LOG(ERROR) << "Failed to read COW in merge";
return false;
}
} else {
- CHECK(cow_op->type == kCowZeroOp);
+ CHECK(GetCowOpSourceInfoType(*cow_op) == kCowZeroOp);
memset(buffer, 0, BLOCK_SZ);
}
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
index b9ecfa5..ffa23ea 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -18,6 +18,7 @@
#include <pthread.h>
+#include "libsnapshot/cow_format.h"
#include "snapuserd_core.h"
#include "utility.h"
@@ -63,7 +64,7 @@
<< " Op: " << *cow_op;
if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
std::string op;
- if (cow_op->type == kCowCopyOp)
+ if (GetCowOpSourceInfoType(*cow_op) == kCowCopyOp)
op = "Copy-op";
else {
op = "Xor-op";
@@ -133,7 +134,7 @@
}
case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
bool ret;
- if (cow_op->type == kCowCopyOp) {
+ if (GetCowOpSourceInfoType(*cow_op) == kCowCopyOp) {
ret = ProcessCopyOp(cow_op, buffer);
} else {
ret = ProcessXorOp(cow_op, buffer);
@@ -167,7 +168,7 @@
return false;
}
- switch (cow_op->type) {
+ switch (GetCowOpSourceInfoType(*cow_op)) {
case kCowReplaceOp: {
return ProcessReplaceOp(cow_op, buffer);
}
@@ -183,7 +184,7 @@
}
default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ SNAP_LOG(ERROR) << "Unknown operation-type found: " << GetCowOpSourceInfoType(*cow_op);
}
}
return false;
@@ -283,7 +284,8 @@
// We found the sector in mapping. Check the type of COW OP and
// process it.
if (!ProcessCowOp(it->second, buffer)) {
- SNAP_LOG(ERROR) << "ProcessCowOp failed";
+ SNAP_LOG(ERROR)
+ << "ProcessCowOp failed, sector = " << sector << ", size = " << sz;
return false;
}
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 c295851..536ff2f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -24,6 +24,7 @@
#include <android-base/strings.h>
#include <snapuserd/dm_user_block_server.h>
+#include "libsnapshot/cow_format.h"
#include "merge_worker.h"
#include "read_worker.h"
@@ -173,6 +174,10 @@
}
SNAP_LOG(INFO) << "Merge-ops: " << header.num_merge_ops;
+ if (header.num_merge_ops) {
+ resume_merge_ = true;
+ SNAP_LOG(INFO) << "Resume Snapshot-merge";
+ }
if (!MmapMetadata()) {
SNAP_LOG(ERROR) << "mmap failed";
@@ -192,13 +197,13 @@
while (!cowop_iter->AtEnd()) {
const CowOperation* cow_op = cowop_iter->Get();
- if (cow_op->type == kCowCopyOp) {
+ if (GetCowOpSourceInfoType(*cow_op) == kCowCopyOp) {
copy_ops += 1;
- } else if (cow_op->type == kCowReplaceOp) {
+ } else if (GetCowOpSourceInfoType(*cow_op) == kCowReplaceOp) {
replace_ops += 1;
- } else if (cow_op->type == kCowZeroOp) {
+ } else if (GetCowOpSourceInfoType(*cow_op) == kCowZeroOp) {
zero_ops += 1;
- } else if (cow_op->type == kCowXorOp) {
+ } else if (GetCowOpSourceInfoType(*cow_op) == kCowXorOp) {
xor_ops += 1;
}
@@ -295,6 +300,11 @@
if (ra_thread_) {
ra_thread_status =
std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
+ // If this is a merge-resume path, wait until RA thread is fully up as
+ // the data has to be re-constructed from the scratch space.
+ if (resume_merge_ && ShouldReconstructDataFromCow()) {
+ WaitForRaThreadToStart();
+ }
}
// Launch worker threads
@@ -307,7 +317,9 @@
std::async(std::launch::async, &MergeWorker::Run, merge_thread_.get());
// Now that the worker threads are up, scan the partitions.
- if (perform_verification_) {
+ // If the snapshot-merge is being resumed, there is no need to scan as the
+ // current slot is already marked as boot complete.
+ if (perform_verification_ && !resume_merge_) {
update_verify_->VerifyUpdatePartition();
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 622fc50..f88406d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -147,6 +147,8 @@
void WakeupMonitorMergeThread();
void WaitForMergeComplete();
bool WaitForMergeBegin();
+ void RaThreadStarted();
+ void WaitForRaThreadToStart();
void NotifyRAForMergeReady();
bool WaitForMergeReady();
void MergeFailed();
@@ -221,6 +223,7 @@
// Read-ahead related
bool populate_data_from_cow_ = false;
bool ra_thread_ = false;
+ bool ra_thread_started_ = false;
int total_ra_blocks_merged_ = 0;
MERGE_IO_TRANSITION io_state_ = MERGE_IO_TRANSITION::INVALID;
std::unique_ptr<ReadAhead> read_ahead_thread_;
@@ -242,6 +245,7 @@
bool scratch_space_ = false;
int num_worker_threads_ = kNumWorkerThreads;
bool perform_verification_ = true;
+ bool resume_merge_ = false;
std::unique_ptr<struct io_uring> ring_;
std::unique_ptr<UpdateVerify> update_verify_;
@@ -249,6 +253,7 @@
};
std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value);
+static_assert(sizeof(off_t) == sizeof(uint64_t));
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index d2128c5..d7d43a6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -18,6 +18,7 @@
#include <pthread.h>
+#include "libsnapshot/cow_format.h"
#include "snapuserd_core.h"
#include "utility.h"
@@ -77,7 +78,7 @@
SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
return nr_consecutive;
}
- if (cow_op->type == kCowXorOp) {
+ if (GetCowOpSourceInfoType(*cow_op) == kCowXorOp) {
xor_op_vec.push_back(cow_op);
}
@@ -106,7 +107,7 @@
break;
}
- if (op->type == kCowXorOp) {
+ if (GetCowOpSourceInfoType(*op) == kCowXorOp) {
xor_op_vec.push_back(op);
}
@@ -206,6 +207,7 @@
return false;
}
+ snapuserd_->RaThreadStarted();
SNAP_LOG(INFO) << "ReconstructDataFromCow success";
notify_read_ahead_failed.Cancel();
return true;
@@ -716,9 +718,13 @@
total_ra_blocks_completed_ += total_blocks_merged_;
snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
- // Flush the data only if we have a overlapping blocks in the region
+ // Flush the scratch data - Technically, we should flush only for overlapping
+ // blocks; However, since this region is mmap'ed, the dirty pages can still
+ // get flushed to disk at any random point in time. Instead, make sure
+ // the data in scratch is in the correct state before merge thread resumes.
+ //
// Notify the Merge thread to resume merging this window
- if (!snapuserd_->ReadAheadIOCompleted(overlap_)) {
+ if (!snapuserd_->ReadAheadIOCompleted(true)) {
SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
snapuserd_->ReadAheadIOFailed();
return false;
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 01fe06f..65f31cf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -235,9 +235,11 @@
bool Merge();
void ValidateMerge();
void ReadSnapshotDeviceAndValidate();
+ void ReadSnapshotAndValidateOverlappingBlocks();
void Shutdown();
void MergeInterrupt();
void MergeInterruptFixed(int duration);
+ void MergeInterruptAndValidate(int duration);
void MergeInterruptRandomly(int max_duration);
bool StartMerge();
void CheckMergeCompletion();
@@ -358,6 +360,76 @@
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
+void SnapuserdTest::ReadSnapshotAndValidateOverlappingBlocks() {
+ // Open COW device
+ unique_fd fd(open(cow_system_->path, O_RDONLY));
+ ASSERT_GE(fd, 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(fd));
+
+ const auto& header = reader.GetHeader();
+ size_t total_mapped_addr_length = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
+
+ ASSERT_GE(header.prefix.major_version, 2);
+
+ void* mapped_addr = mmap(NULL, total_mapped_addr_length, PROT_READ, MAP_SHARED, fd.get(), 0);
+ ASSERT_NE(mapped_addr, MAP_FAILED);
+
+ bool populate_data_from_scratch = false;
+ struct BufferState* ra_state =
+ reinterpret_cast<struct BufferState*>((char*)mapped_addr + header.prefix.header_size);
+ if (ra_state->read_ahead_state == kCowReadAheadDone) {
+ populate_data_from_scratch = true;
+ }
+
+ size_t num_merge_ops = header.num_merge_ops;
+ // We have some partial merge operations completed.
+ // To test the merge-resume path, forcefully corrupt the data of the base
+ // device for the offsets where the merge is still pending.
+ if (num_merge_ops && populate_data_from_scratch) {
+ std::string corrupt_buffer(4096, 0);
+ // Corrupt two blocks from the point where the merge has to be resumed by
+ // writing down zeroe's.
+ //
+ // Now, since this is a merge-resume path, the "correct" data should be
+ // in the scratch space of the COW device. When there is an I/O request
+ // from the snapshot device, the data has to be retrieved from the
+ // scratch space. If not and I/O is routed to the base device, we
+ // may end up with corruption.
+ off_t corrupt_offset = (num_merge_ops + 2) * 4096;
+
+ if (corrupt_offset < size_) {
+ ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
+ 4096, corrupt_offset),
+ true);
+ corrupt_offset -= 4096;
+ ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
+ 4096, corrupt_offset),
+ true);
+ fsync(base_fd_.get());
+ }
+ }
+
+ // Time to read the snapshot device.
+ unique_fd snapshot_fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY | O_DIRECT | O_SYNC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ void* buff_addr;
+ ASSERT_EQ(posix_memalign(&buff_addr, 4096, size_), 0);
+
+ std::unique_ptr<void, decltype(&::free)> snapshot_buffer(buff_addr, ::free);
+
+ // Scan the entire snapshot device and read the data and verify data
+ // integrity. Since the base device was forcefully corrupted, the data from
+ // this scan should be retrieved from scratch space of the COW partition.
+ //
+ // Furthermore, after the merge is complete, base device data is again
+ // verified as the aforementioned corrupted blocks aren't persisted.
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapshot_buffer.get(), size_, 0), true);
+ ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);
+}
+
void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
@@ -665,6 +737,20 @@
ASSERT_TRUE(Merge());
}
+void SnapuserdTest::MergeInterruptAndValidate(int duration) {
+ ASSERT_TRUE(StartMerge());
+
+ for (int i = 0; i < 15; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ReadSnapshotAndValidateOverlappingBlocks();
+ ASSERT_TRUE(StartMerge());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ASSERT_TRUE(Merge());
+}
+
void SnapuserdTest::MergeInterrupt() {
// Interrupt merge at various intervals
ASSERT_TRUE(StartMerge());
@@ -714,10 +800,12 @@
}
ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Issue I/O before merge begins
- std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+ auto read_future =
+ std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
// Start the merge
ASSERT_TRUE(Merge());
ValidateMerge();
+ read_future.wait();
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
@@ -728,9 +816,11 @@
// Start the merge
ASSERT_TRUE(StartMerge());
// Issue I/O in parallel when merge is in-progress
- std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+ auto read_future =
+ std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
CheckMergeCompletion();
ValidateMerge();
+ read_future.wait();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Resume) {
@@ -757,6 +847,15 @@
ValidateMerge();
}
+TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_IO_Validate_TEST) {
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptAndValidate(2));
+ ValidateMerge();
+}
+
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index f3e0019..8d090bf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -366,6 +366,26 @@
}
}
+void SnapshotHandler::RaThreadStarted() {
+ std::unique_lock<std::mutex> lock(lock_);
+ ra_thread_started_ = true;
+}
+
+void SnapshotHandler::WaitForRaThreadToStart() {
+ auto now = std::chrono::system_clock::now();
+ auto deadline = now + 3s;
+ {
+ std::unique_lock<std::mutex> lock(lock_);
+ while (!ra_thread_started_) {
+ auto status = cv.wait_until(lock, deadline);
+ if (status == std::cv_status::timeout) {
+ SNAP_LOG(ERROR) << "Read-ahead thread did not start";
+ return;
+ }
+ }
+ }
+}
+
std::string SnapshotHandler::GetMergeStatus() {
bool merge_not_initiated = false;
bool merge_monitored = false;
@@ -618,7 +638,6 @@
std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
if (it == read_ahead_buffer_map_.end()) {
- SNAP_LOG(ERROR) << "Block: " << block << " not found in RA buffer";
return false;
}
@@ -642,6 +661,13 @@
MERGE_GROUP_STATE state = blk_state->merge_state_;
switch (state) {
case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
+ // If this is a merge-resume path, check if the data is
+ // available from scratch space. Data from scratch space takes
+ // higher precedence than from source device for overlapping
+ // blocks.
+ if (resume_merge_ && GetRABuffer(&lock, new_block, buffer)) {
+ return (MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS);
+ }
blk_state->num_ios_in_progress += 1; // ref count
[[fallthrough]];
}
diff --git a/fs_mgr/libsnapshot/tools/Android.bp b/fs_mgr/libsnapshot/tools/Android.bp
index cfa0cef..0f08286 100644
--- a/fs_mgr/libsnapshot/tools/Android.bp
+++ b/fs_mgr/libsnapshot/tools/Android.bp
@@ -20,3 +20,26 @@
cflags: ["-Werror"],
}
+
+
+cc_binary {
+ name: "basic_v2_cow_writer",
+ host_supported: true,
+ defaults: [
+ "fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
+ ],
+
+ srcs: ["basic_v2_cow_writer.cpp"],
+
+ static_libs: [
+ "libsnapshot_cow",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp b/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp
new file mode 100644
index 0000000..72fb0f5
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp
@@ -0,0 +1,63 @@
+//
+// 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_writer.h>
+#include <filesystem>
+
+#include "android-base/unique_fd.h"
+
+using namespace android::snapshot;
+
+// This writes a simple cow v2 file in the current directory. This file will serve as testdata for
+// ensuring our v3 cow reader will be able to read a cow file created by the v2 writer.
+//
+// WARNING: We should not be overriding this test file, as it will serve as historic marker for what
+// a device with old writer_v2 will write as a cow.
+void write_cow_v2() {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ char cwd_buffer[1024];
+ size_t cwd_buffer_size = sizeof(cwd_buffer);
+
+ // Get the current working directory path.
+ char* err = getcwd(cwd_buffer, cwd_buffer_size);
+ if (!err) {
+ LOG(ERROR) << "Couldn't get current directory";
+ }
+ android::base::unique_fd fd(open(strcat(cwd_buffer, "/cow_v2"), O_CREAT | O_RDWR, 0666));
+ if (fd.get() == -1) {
+ LOG(FATAL) << "couldn't open tmp_cow";
+ }
+ std::unique_ptr<ICowWriter> writer = CreateCowWriter(2, options, std::move(fd));
+ writer->AddCopy(0, 5);
+ writer->AddRawBlocks(2, data.data(), data.size());
+ writer->AddLabel(1);
+ writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
+ writer->AddZeroBlocks(5, 10);
+ writer->AddLabel(2);
+ writer->Finalize();
+}
+
+int main() {
+ write_cow_v2();
+}
diff --git a/fs_mgr/libsnapshot/tools/cow_benchmark.cpp b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
index da2b879..4d5e346 100644
--- a/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
+++ b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
@@ -1,4 +1,18 @@
-
+//
+// 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 <memory>
#include <array>
diff --git a/fs_mgr/libsnapshot/tools/testdata/cow_v2 b/fs_mgr/libsnapshot/tools/testdata/cow_v2
new file mode 100644
index 0000000..9f37dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/cow_v2
Binary files differ
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index e4cf582..0c97632 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -432,7 +432,7 @@
}
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
- mHealthInfo->batteryTechnology = String8(buf.c_str());
+ mHealthInfo->batteryTechnology = buf;
if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
mHealthInfo->chargingPolicy = getBatteryChargingPolicy(buf.c_str());
@@ -786,39 +786,35 @@
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryStatusPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
}
if (mHealthdConfig->batteryHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryHealthPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
}
if (mHealthdConfig->batteryPresentPath.empty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryPresentPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
}
if (mHealthdConfig->batteryCapacityPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryCapacityPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
}
if (mHealthdConfig->batteryVoltagePath.empty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
}
}
@@ -827,7 +823,7 @@
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargePath = path;
}
@@ -835,7 +831,7 @@
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
}
@@ -843,27 +839,29 @@
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCycleCountPath = path;
}
if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryCapacityLevelPath = path;
+ }
}
if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeTimeToFullNowPath = path;
}
if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
}
@@ -871,7 +869,7 @@
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
}
@@ -879,7 +877,7 @@
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
}
@@ -887,7 +885,7 @@
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
}
}
@@ -896,19 +894,19 @@
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
}
if (mHealthdConfig->batteryStateOfHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/state_of_health", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryStateOfHealthPath = path;
} else {
path.clear();
path.appendFormat("%s/%s/health_index", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryStateOfHealthPath = path;
}
}
@@ -916,32 +914,36 @@
if (mHealthdConfig->batteryHealthStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health_status", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryHealthStatusPath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryHealthStatusPath = path;
+ }
}
if (mHealthdConfig->batteryManufacturingDatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/manufacturing_date", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryManufacturingDatePath = path;
}
if (mHealthdConfig->batteryFirstUsageDatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/first_usage_date", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryFirstUsageDatePath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryFirstUsageDatePath = path;
+ }
}
if (mHealthdConfig->chargingStatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charging_state", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->chargingStatePath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingStatePath = path;
}
if (mHealthdConfig->chargingPolicyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charging_policy", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
}
break;
diff --git a/healthd/BatteryMonitor_v1.cpp b/healthd/BatteryMonitor_v1.cpp
index 686c338..2e0cfc9 100644
--- a/healthd/BatteryMonitor_v1.cpp
+++ b/healthd/BatteryMonitor_v1.cpp
@@ -352,7 +352,7 @@
mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
- mHealthInfo->batteryTechnology = String8(buf.c_str());
+ mHealthInfo->batteryTechnology = buf;
double MaxPower = 0;
@@ -639,39 +639,35 @@
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryStatusPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
}
if (mHealthdConfig->batteryHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryHealthPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
}
if (mHealthdConfig->batteryPresentPath.empty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryPresentPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
}
if (mHealthdConfig->batteryCapacityPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryCapacityPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
}
if (mHealthdConfig->batteryVoltagePath.empty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
}
}
@@ -680,7 +676,7 @@
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargePath = path;
}
@@ -688,7 +684,7 @@
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
}
@@ -696,27 +692,29 @@
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCycleCountPath = path;
}
if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryCapacityLevelPath = path;
+ }
}
if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeTimeToFullNowPath = path;
}
if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
}
@@ -724,7 +722,7 @@
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
}
@@ -732,7 +730,7 @@
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
}
@@ -740,7 +738,7 @@
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
}
}
@@ -749,7 +747,7 @@
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
}
diff --git a/init/Android.bp b/init/Android.bp
index d4852d6..4c25ad7 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -174,7 +174,6 @@
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
- "libsigningutils",
"libsnapshot_cow",
"libsnapshot_init",
"libxml2",
@@ -183,7 +182,6 @@
],
shared_libs: [
"libbase",
- "libcrypto",
"libcutils",
"libdl",
"libext4_utils",
@@ -199,7 +197,6 @@
"libselinux",
"libunwindstack",
"libutils",
- "libziparchive",
],
header_libs: ["bionic_libc_platform_headers"],
bootstrap: true,
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 402b501..36ca379 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,14 +8,6 @@
},
{
"name": "MicrodroidHostTestCases"
- },
- {
- "name": "CtsSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.SeamendcHostTest"
- }
- ]
}
],
"hwasan-presubmit": [
@@ -27,14 +19,6 @@
},
{
"name": "MicrodroidHostTestCases"
- },
- {
- "name": "CtsSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.SeamendcHostTest"
- }
- ]
}
]
}
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a70e866..a95a4a3 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -475,8 +475,6 @@
{ 0, 0 },
};
-#define DATA_MNT_POINT "/data"
-
/* mount <type> <device> <path> <flags ...> <options> */
static Result<void> do_mount(const BuiltinArguments& args) {
const char* options = nullptr;
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index ab6ff03..0e2cd2a 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -66,18 +66,12 @@
CAP_MAP_ENTRY(WAKE_ALARM),
CAP_MAP_ENTRY(BLOCK_SUSPEND),
CAP_MAP_ENTRY(AUDIT_READ),
-#if defined(__BIONIC__)
CAP_MAP_ENTRY(PERFMON),
CAP_MAP_ENTRY(BPF),
CAP_MAP_ENTRY(CHECKPOINT_RESTORE),
-#endif
};
-#if defined(__BIONIC__)
static_assert(CAP_LAST_CAP == CAP_CHECKPOINT_RESTORE, "CAP_LAST_CAP is not CAP_CHECKPOINT_RESTORE");
-#else
-static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
-#endif
static bool ComputeCapAmbientSupported() {
#if defined(__ANDROID__)
diff --git a/init/capabilities.h b/init/capabilities.h
index 891e0ac..fc80c98 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,17 +21,6 @@
#include <string>
#include <type_traits>
-#if !defined(__ANDROID__)
-#ifndef CAP_BLOCK_SUSPEND
-#define CAP_BLOCK_SUSPEND 36
-#endif
-#ifndef CAP_AUDIT_READ
-#define CAP_AUDIT_READ 37
-#endif
-#undef CAP_LAST_CAP
-#define CAP_LAST_CAP CAP_AUDIT_READ
-#endif
-
namespace android {
namespace init {
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c6a287a..e48fa15 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -239,8 +239,12 @@
module_dirs.emplace_back(entry->d_name);
break;
}
- // Ignore _16k/_64k module dirs on 4K kernels
- if (GetPageSizeSuffix(entry->d_name) != page_size_suffix) {
+ // Is a directory does not have page size suffix, it does not mean this directory is for 4K
+ // kernels. Certain 16K kernel builds put all modules in /lib/modules/`uname -r` without any
+ // suffix. Therefore, only ignore a directory if it has _16k/_64k suffix and the suffix does
+ // not match system page size.
+ const auto dir_page_size_suffix = GetPageSizeSuffix(entry->d_name);
+ if (!dir_page_size_suffix.empty() && dir_page_size_suffix != page_size_suffix) {
continue;
}
int dir_major = 0, dir_minor = 0;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f5de17d..cdd0afe 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1247,9 +1247,6 @@
// Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
- if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
- LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
- }
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
&property_infos);
@@ -1273,7 +1270,6 @@
LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
- LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
@@ -1465,8 +1461,6 @@
work_.pop_front();
}
- std::this_thread::sleep_for(1s);
-
// Perform write/fsync outside the lock.
WritePersistentProperty(std::get<0>(item), std::get<1>(item));
NotifyPropertyChange(std::get<0>(item), std::get<1>(item));
diff --git a/init/security.cpp b/init/security.cpp
index 6e616be..0c73fae 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -106,21 +106,17 @@
// uml does not support mmap_rnd_bits
return {};
#elif defined(__aarch64__)
- // arm64 architecture supports 18 - 33 rnd bits depending on pagesize and
- // VA_SIZE. However the kernel might have been compiled with a narrower
- // range using CONFIG_ARCH_MMAP_RND_BITS_MIN/MAX. To use the maximum
- // supported number of bits, we start from the theoretical maximum of 33
- // bits and try smaller values until we reach 24 bits which is the
- // Android-specific minimum. Don't go lower even if the configured maximum
- // is smaller than 24.
+ // arm64 supports 14 - 33 rnd bits depending on page size and ARM64_VA_BITS.
+ // The kernel (6.5) still defaults to 39 va bits for 4KiB pages, so shipping
+ // devices are only getting 24 bits of randomness in practice.
if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
return {};
}
#elif defined(__riscv)
- // TODO: sv48 and sv57 were both added to the kernel this year, so we
- // probably just need some kernel fixes to enable higher ASLR randomization,
- // but for now 24 is the maximum that the kernel supports.
- if (SetMmapRndBitsMin(24, 18, false)) {
+ // TODO: sv48 and sv57 have both been added to the kernel, but the kernel
+ // still doesn't support more than 24 bits.
+ // https://github.com/google/android-riscv64/issues/1
+ if (SetMmapRndBitsMin(24, 24, false)) {
return {};
}
#elif defined(__x86_64__)
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ac102eb..9095b85 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,29 +26,26 @@
// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
-// The split policy is for supporting treble devices and updateable apexes. It splits the SEPolicy
-// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
-// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
-// /odm/etc/selinux, and /dev/selinux (the apex portion of policy). This is necessary to allow
-// images to be updated independently of the vendor image, while maintaining contributions from
-// multiple partitions in the SEPolicy. This is especially important for VTS testing, where the
-// SEPolicy on the Google System Image may not be identical to the system image shipped on a
-// vendor's device.
+// The split policy is for supporting treble devices. It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
+// portion of the policy). This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
// The split SEPolicy is loaded as described below:
// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
// /odm/etc/selinux/precompiled_sepolicy if odm parition is present. Stored along with this file
-// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
-// that were used to compile this precompiled policy. The system partition contains a similar
-// sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
-// product, and apex contain sha256 hashes of their SEPolicy. Init loads this
+// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
+// were used to compile this precompiled policy. The system partition contains a similar sha256
+// of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext and
+// product paritition contain sha256 hashes of their SEPolicy. The init loads this
// precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-// /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
-// respectively.
-// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
-// them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
-// to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by
-// the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+// /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
+// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
+// have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
+// compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the
+// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
// That function contains even more documentation with the specific implementation details of how
// the SEPolicy is compiled if needed.
@@ -61,15 +58,12 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <fstream>
-#include <CertUtils.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
-#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
@@ -77,7 +71,6 @@
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
#include <selinux/android.h>
-#include <ziparchive/zip_archive.h>
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
@@ -245,7 +238,6 @@
precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
{"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
- {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
};
for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -324,7 +316,7 @@
// * vendor -- policy needed due to logic contained in the vendor image,
// * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
// with newer versions of platform policy.
- // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
+ // * (optional) policy needed due to logic on product, system_ext, or odm images.
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
@@ -420,12 +412,6 @@
if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
odm_policy_cil_file.clear();
}
-
- // apex_sepolicy.cil is default but optional.
- std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
- if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
- apex_policy_cil_file.clear();
- }
const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
// clang-format off
@@ -468,9 +454,6 @@
if (!odm_policy_cil_file.empty()) {
compile_args.push_back(odm_policy_cil_file.c_str());
}
- if (!apex_policy_cil_file.empty()) {
- compile_args.push_back(apex_policy_cil_file.c_str());
- }
compile_args.push_back(nullptr);
if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -497,194 +480,6 @@
return true;
}
-constexpr const char* kSigningCertRelease =
- "/system/etc/selinux/com.android.sepolicy.cert-release.der";
-const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
-const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
-const std::string kSepolicyZip = "SEPolicy.zip";
-const std::string kSepolicySignature = "SEPolicy.zip.sig";
-
-const std::string kTmpfsDir = "/dev/selinux/";
-
-// Files that are deleted after policy is compiled/loaded.
-const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
-// Files that need to persist because they are used by userspace processes.
-const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
- "apex_service_contexts", "apex_seapp_contexts",
- "apex_test"};
-
-Result<void> CreateTmpfsDir() {
- mode_t mode = 0744;
- struct stat stat_data;
- if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
- if (errno != ENOENT) {
- return ErrnoError() << "Could not stat " << kTmpfsDir;
- }
- if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
- return ErrnoError() << "Could not mkdir " << kTmpfsDir;
- }
- } else {
- if (!S_ISDIR(stat_data.st_mode)) {
- return Error() << kTmpfsDir << " exists and is not a directory.";
- }
- LOG(WARNING) << "Directory " << kTmpfsDir << " already exists";
- }
-
- // Need to manually call chmod because mkdir will create a folder with
- // permissions mode & ~umask.
- if (chmod(kTmpfsDir.c_str(), mode) != 0) {
- return ErrnoError() << "Could not chmod " << kTmpfsDir;
- }
-
- return {};
-}
-
-Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
- ZipEntry entry;
- std::string dstPath = kTmpfsDir + fileName;
-
- int ret = FindEntry(archive, fileName, &entry);
- if (ret != 0) {
- // All files are optional. If a file doesn't exist, return without error.
- return {};
- }
-
- unique_fd fd(TEMP_FAILURE_RETRY(
- open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
- if (fd == -1) {
- return ErrnoError() << "Failed to open " << dstPath;
- }
-
- ret = ExtractEntryToFile(archive, &entry, fd.get());
- if (ret != 0) {
- return Error() << "Failed to extract entry \"" << fileName << "\" ("
- << entry.uncompressed_length << " bytes) to \"" << dstPath
- << "\": " << ErrorCodeString(ret);
- }
-
- return {};
-}
-
-Result<void> GetPolicyFromApex(const std::string& dir) {
- LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
- unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
- if (fd < 0) {
- return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
- }
-
- ZipArchiveHandle handle;
- int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
- /*assume_ownership=*/false);
- if (ret < 0) {
- return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
- << ErrorCodeString(ret);
- }
-
- auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
-
- auto create = CreateTmpfsDir();
- if (!create.ok()) {
- return create.error();
- }
-
- for (const auto& file : kApexSepolicy) {
- auto extract = PutFileInTmpfs(handle, file);
- if (!extract.ok()) {
- return extract.error();
- }
- }
- for (const auto& file : kApexSepolicyTmp) {
- auto extract = PutFileInTmpfs(handle, file);
- if (!extract.ok()) {
- return extract.error();
- }
- }
- return {};
-}
-
-Result<void> SepolicyCheckSignature(const std::string& dir) {
- std::string signature;
- if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
- return ErrnoError() << "Failed to read " << kSepolicySignature;
- }
-
- std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
- if (!sepolicyZip) {
- return Error() << "Failed to open " << kSepolicyZip;
- }
- sepolicyZip.seekg(0);
- std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
- std::istreambuf_iterator<char>());
-
- auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
- if (!releaseKey.ok()) {
- return releaseKey.error();
- }
-
- return verifySignature(sepolicyStr, signature, *releaseKey);
-}
-
-Result<void> SepolicyVerify(const std::string& dir) {
- auto sepolicySignature = SepolicyCheckSignature(dir);
- if (!sepolicySignature.ok()) {
- return Error() << "Apex SEPolicy failed signature check";
- }
- return {};
-}
-
-void CleanupApexSepolicy() {
- for (const auto& file : kApexSepolicyTmp) {
- std::string path = kTmpfsDir + file;
- unlink(path.c_str());
- }
-}
-
-// Updatable sepolicy is shipped within an zip within an APEX. Because
-// it needs to be available before Apexes are mounted, apexd copies
-// the zip from the APEX and stores it in /metadata/sepolicy. If there is
-// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
-// loaded from /system/etc/selinux/apex. Init performs the following
-// steps on boot:
-//
-// 1. Validates the zip by checking its signature against a public key that is
-// stored in /system/etc/selinux.
-// 2. Extracts files from zip and stores them in /dev/selinux.
-// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
-// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
-// is used. This is the same flow as on-device compilation of policy for Treble.
-// 4. Cleans up files in /dev/selinux which are no longer needed.
-// 5. Restorecons the remaining files in /dev/selinux.
-// 6. Sets selinux into enforcing mode and continues normal booting.
-//
-void PrepareApexSepolicy() {
- // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
- // /system. If neither exists, do nothing.
- std::string dir;
- if (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0) {
- dir = kSepolicyApexMetadataDir;
- } else if (access((kSepolicyApexSystemDir + kSepolicyZip).c_str(), F_OK) == 0) {
- dir = kSepolicyApexSystemDir;
- } else {
- LOG(INFO) << "APEX Sepolicy not found";
- return;
- }
-
- auto sepolicyVerify = SepolicyVerify(dir);
- if (!sepolicyVerify.ok()) {
- LOG(INFO) << "Error: " << sepolicyVerify.error();
- // If signature verification fails, fall back to version on /system.
- // This file doesn't need to be verified because it lives on the system partition which
- // is signed and protected by verified boot.
- dir = kSepolicyApexSystemDir;
- }
-
- auto apex = GetPolicyFromApex(dir);
- if (!apex.ok()) {
- // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
- LOG(ERROR) << apex.error();
- }
-}
-
void ReadPolicy(std::string* policy) {
PolicyFile policy_file;
@@ -961,12 +756,9 @@
LOG(INFO) << "Opening SELinux policy";
- PrepareApexSepolicy();
-
// Read the policy before potentially killing snapuserd.
std::string policy;
ReadPolicy(&policy);
- CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
@@ -982,13 +774,6 @@
snapuserd_helper->FinishTransition();
snapuserd_helper = nullptr;
}
-
- // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
- // needed to transition files from tmpfs to *_contexts_file context should not be granted to
- // any process after selinux is set into enforcing mode.
- if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
- PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
- }
}
int SetupSelinux(char** argv) {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 55a8694..8ae7d9e 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -171,11 +171,15 @@
"libasync_safe",
],
},
+ linux: {
+ srcs: [
+ "canned_fs_config.cpp",
+ "fs_config.cpp",
+ ],
+ },
not_windows: {
srcs: libcutils_nonwindows_sources + [
"ashmem-host.cpp",
- "canned_fs_config.cpp",
- "fs_config.cpp",
"trace-host.cpp",
],
},
@@ -201,8 +205,6 @@
srcs: libcutils_nonwindows_sources + [
"android_reboot.cpp",
"ashmem-dev.cpp",
- "canned_fs_config.cpp",
- "fs_config.cpp",
"klog.cpp",
"partition_utils.cpp",
"qtaguid.cpp",
diff --git a/libcutils/arch-x86/cache.h b/libcutils/arch-x86/cache.h
deleted file mode 100644
index 1c22fea..0000000
--- a/libcutils/arch-x86/cache.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#if defined(__slm__)
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
-#else
-/* Values are optimized for Atom */
-#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */
-#endif
-
-#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/arch-x86_64/cache.h b/libcutils/arch-x86_64/cache.h
deleted file mode 100644
index f144309..0000000
--- a/libcutils/arch-x86_64/cache.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
-
-#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 6a27f9a..410dbfd 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -44,16 +44,6 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-/* Will be added to UAPI once upstream change is merged */
-#define F_SEAL_FUTURE_WRITE 0x0010
-
-/*
- * The minimum vendor API level at and after which it is safe to use memfd.
- * This is to facilitate deprecation of ashmem.
- */
-#define MIN_MEMFD_VENDOR_API_LEVEL 29
-#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
-
/* ashmem identity */
static dev_t __ashmem_rdev;
/*
@@ -91,55 +81,17 @@
/* Determine if vendor processes would be ok with memfd in the system:
*
- * If VNDK is using older libcutils, don't use memfd. This is so that the
- * same shared memory mechanism is used across binder transactions between
- * vendor partition processes and system partition processes.
+ * Previously this function checked if memfd is supported by checking if
+ * vendor VNDK version is greater than Q. As we can assume all treblelized
+ * device using this code is up to date enough to use memfd, memfd is allowed
+ * if the device is treblelized.
*/
static bool check_vendor_memfd_allowed() {
- std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
+ static bool is_treblelized = android::base::GetBoolProperty("ro.treble.enabled", false);
- if (vndk_version == "") {
- ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
- vndk_version.c_str());
- return false;
- }
-
- /* No issues if vendor is targetting current Dessert */
- if (vndk_version == "current") {
- return false;
- }
-
- /* Check if VNDK version is a number and act on it */
- char* p;
- long int vers = strtol(vndk_version.c_str(), &p, 10);
- if (*p == 0) {
- if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
- ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
- vndk_version.c_str());
- return false;
- }
-
- return true;
- }
-
- // Non-numeric should be a single ASCII character. Characters after the
- // first are ignored.
- if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
- ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
- vndk_version.c_str());
- return false;
- }
-
- if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
- ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
- vndk_version.c_str());
- return false;
- }
-
- return true;
+ return is_treblelized;
}
-
/* Determine if memfd can be supported. This is just one-time hardwork
* which will be cached by the caller.
*/
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 26ac576..919be2f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -41,10 +41,6 @@
#include "fs_config.h"
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
using android::base::EndsWith;
using android::base::StartsWith;
@@ -257,12 +253,12 @@
len = strip(target_out_path, len, "/");
len = strip(target_out_path, len, "/system");
if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) {
- fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
+ fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY));
free(name);
}
}
if (fd < 0) {
- fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY | O_BINARY));
+ fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY));
}
return fd;
}
diff --git a/libcutils/include/private/android_filesystem_capability.h b/libcutils/include/private/android_filesystem_capability.h
deleted file mode 100644
index 0227b1d..0000000
--- a/libcutils/include/private/android_filesystem_capability.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-/*
- * Taken from linux/capability.h, with minor modifications
- */
-
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
-
-#include <stdint.h>
-
-#define __user
-#define __u32 uint32_t
-#define __le32 uint32_t
-
-#define _LINUX_CAPABILITY_VERSION_1 0x19980330
-#define _LINUX_CAPABILITY_U32S_1 1
-#define _LINUX_CAPABILITY_VERSION_2 0x20071026
-#define _LINUX_CAPABILITY_U32S_2 2
-#define _LINUX_CAPABILITY_VERSION_3 0x20080522
-#define _LINUX_CAPABILITY_U32S_3 2
-
-typedef struct __user_cap_header_struct {
- __u32 version;
- int pid;
-} __user* cap_user_header_t;
-
-typedef struct __user_cap_data_struct {
- __u32 effective;
- __u32 permitted;
- __u32 inheritable;
-} __user* cap_user_data_t;
-
-#define VFS_CAP_REVISION_MASK 0xFF000000
-#define VFS_CAP_REVISION_SHIFT 24
-#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
-#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
-#define VFS_CAP_REVISION_1 0x01000000
-#define VFS_CAP_U32_1 1
-#define XATTR_CAPS_SZ_1 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_1))
-#define VFS_CAP_REVISION_2 0x02000000
-#define VFS_CAP_U32_2 2
-#define XATTR_CAPS_SZ_2 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_2))
-#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
-#define VFS_CAP_U32 VFS_CAP_U32_2
-#define VFS_CAP_REVISION VFS_CAP_REVISION_2
-
-struct vfs_cap_data {
- __le32 magic_etc;
- struct {
- __le32 permitted;
- __le32 inheritable;
- } data[VFS_CAP_U32];
-};
-
-#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
-#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1
-#define CAP_CHOWN 0
-#define CAP_DAC_OVERRIDE 1
-#define CAP_DAC_READ_SEARCH 2
-#define CAP_FOWNER 3
-#define CAP_FSETID 4
-#define CAP_KILL 5
-#define CAP_SETGID 6
-#define CAP_SETUID 7
-#define CAP_SETPCAP 8
-#define CAP_LINUX_IMMUTABLE 9
-#define CAP_NET_BIND_SERVICE 10
-#define CAP_NET_BROADCAST 11
-#define CAP_NET_ADMIN 12
-#define CAP_NET_RAW 13
-#define CAP_IPC_LOCK 14
-#define CAP_IPC_OWNER 15
-#define CAP_SYS_MODULE 16
-#define CAP_SYS_RAWIO 17
-#define CAP_SYS_CHROOT 18
-#define CAP_SYS_PTRACE 19
-#define CAP_SYS_PACCT 20
-#define CAP_SYS_ADMIN 21
-#define CAP_SYS_BOOT 22
-#define CAP_SYS_NICE 23
-#define CAP_SYS_RESOURCE 24
-#define CAP_SYS_TIME 25
-#define CAP_SYS_TTY_CONFIG 26
-#define CAP_MKNOD 27
-#define CAP_LEASE 28
-#define CAP_AUDIT_WRITE 29
-#define CAP_AUDIT_CONTROL 30
-#define CAP_SETFCAP 31
-#define CAP_MAC_OVERRIDE 32
-#define CAP_MAC_ADMIN 33
-#define CAP_SYSLOG 34
-#define CAP_WAKE_ALARM 35
-#define CAP_BLOCK_SUSPEND 36
-#define CAP_AUDIT_READ 37
-#define CAP_LAST_CAP CAP_AUDIT_READ
-#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
-#define CAP_TO_INDEX(x) ((x) >> 5)
-#define CAP_TO_MASK(x) (1 << ((x)&31))
-
-#undef __user
-#undef __u32
-#undef __le32
-
-#endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8a9a1ff..45f46e5 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -24,11 +24,7 @@
#include <stdint.h>
#include <sys/cdefs.h>
-#if defined(__BIONIC__)
#include <linux/capability.h>
-#else // defined(__BIONIC__)
-#include <private/android_filesystem_capability.h>
-#endif // defined(__BIONIC__)
/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
diff --git a/libcutils/iosched_policy.cpp b/libcutils/iosched_policy.cpp
index 012c537..f7c724d 100644
--- a/libcutils/iosched_policy.cpp
+++ b/libcutils/iosched_policy.cpp
@@ -24,8 +24,7 @@
#include <unistd.h>
#if defined(__ANDROID__)
-#define IOPRIO_WHO_PROCESS (1)
-#define IOPRIO_CLASS_SHIFT (13)
+#include <linux/ioprio.h>
#include <sys/syscall.h>
#define __android_unused
#else
diff --git a/libcutils/socket_local_unix.h b/libcutils/socket_local_unix.h
index 45b9856..ea98c08 100644
--- a/libcutils/socket_local_unix.h
+++ b/libcutils/socket_local_unix.h
@@ -17,6 +17,8 @@
#ifndef __SOCKET_LOCAL_H
#define __SOCKET_LOCAL_H
+#include <sys/socket.h>
+
#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 4adb796..99a2e2d 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -35,7 +35,7 @@
// can be extremely tricky and cause deadlock when using threads or atexit().
//
// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
-// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
+// (1) https://android.googlesource.com/platform/packages/modules/adb.git/+/main/sysdeps_win32.cpp
// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
bool initialize_windows_sockets() {
// There's no harm in calling WSAStartup() multiple times but no benefit
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 4506439..cc2565f 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -219,7 +219,7 @@
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
- if (!ret || errno != EBUSY) break;
+ if (!ret || errno != EBUSY || !retries) break;
std::this_thread::sleep_for(5ms);
}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index d013ec8..3e4393d 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,6 +1,13 @@
{
"Cgroups": [
{
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0775",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
"Controller": "cpu",
"Path": "/dev/cpuctl",
"Mode": "0755",
@@ -32,12 +39,6 @@
{
"Controller": "freezer",
"Path": "."
- },
- {
- "Controller": "io",
- "Path": ".",
- "NeedsActivation": true,
- "Optional": true
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 12f7b44..1fc66ba 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -76,24 +76,6 @@
"Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
- },
- {
- "Name": "BfqWeight",
- "Controller": "io",
- "File": "blkio.bfq.weight",
- "FileV2": "io.bfq.weight"
- },
- {
- "Name": "CfqGroupIdle",
- "Controller": "io",
- "File": "blkio.group_idle",
- "FileV2": "io.group_idle"
- },
- {
- "Name": "CfqWeight",
- "Controller": "io",
- "File": "blkio.weight",
- "FileV2": "io.weight"
}
],
@@ -457,30 +439,11 @@
"Name": "LowIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "10",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "200",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": "background"
}
}
]
@@ -489,30 +452,11 @@
"Name": "NormalIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "100",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "1000",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": ""
}
}
]
@@ -521,30 +465,11 @@
"Name": "HighIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "100",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "1000",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": ""
}
}
]
@@ -553,30 +478,11 @@
"Name": "MaxIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "100",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "1000",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": ""
}
}
]
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 162f0f4..2c05fbc 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -185,10 +185,21 @@
support_system_process: true,
},
- header_abi_checker: {
- // AFDO affects weak symbols.
- diff_flags: ["-allow-adding-removing-weak-symbols"],
- ref_dump_dirs: ["abi-dumps"],
+ target: {
+ product: {
+ header_abi_checker: {
+ // AFDO affects weak symbols.
+ diff_flags: ["-allow-adding-removing-weak-symbols"],
+ ref_dump_dirs: ["abi-dumps"],
+ },
+ },
+ vendor: {
+ header_abi_checker: {
+ // AFDO affects weak symbols.
+ diff_flags: ["-allow-adding-removing-weak-symbols"],
+ ref_dump_dirs: ["abi-dumps"],
+ },
+ },
},
}
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 4dcb35b..11f2c92 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -82,7 +82,7 @@
void CallStack::print(Printer& printer) const {
for (size_t i = 0; i < mFrameLines.size(); i++) {
- printer.printLine(mFrameLines[i]);
+ printer.printLine(mFrameLines[i].c_str());
}
}
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
index a271aee..8f9781b 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/String16_fuzz.cpp
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <functional>
#include <iostream>
+#include <vector>
#include "fuzzer/FuzzedDataProvider.h"
#include "utils/String16.h"
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index c6e6f74..6f4642e 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -33,50 +33,50 @@
TEST(String16Test, FromChar16_t) {
String16 tmp(u"Verify me");
- EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
}
TEST(String16Test, FromChar16_tSized) {
String16 tmp(u"Verify me", 7);
- EXPECT_STR16EQ(u"Verify ", tmp);
+ EXPECT_STR16EQ(u"Verify ", tmp.c_str());
}
TEST(String16Test, FromChar) {
String16 tmp("Verify me");
- EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
}
TEST(String16Test, FromCharSized) {
String16 tmp("Verify me", 7);
- EXPECT_STR16EQ(u"Verify ", tmp);
+ EXPECT_STR16EQ(u"Verify ", tmp.c_str());
}
TEST(String16Test, Copy) {
String16 tmp("Verify me");
String16 another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, CopyAssign) {
String16 tmp("Verify me");
String16 another;
another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, Move) {
String16 tmp("Verify me");
String16 another(std::move(tmp));
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, MoveAssign) {
String16 tmp("Verify me");
String16 another;
another = std::move(tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, Size) {
@@ -88,27 +88,27 @@
String16 tmp("Verify me");
tmp.setTo(u"New content");
EXPECT_EQ(11U, tmp.size());
- EXPECT_STR16EQ(u"New content", tmp);
+ EXPECT_STR16EQ(u"New content", tmp.c_str());
}
TEST(String16Test, Append) {
String16 tmp("Verify me");
tmp.append(String16("Hello"));
EXPECT_EQ(14U, tmp.size());
- EXPECT_STR16EQ(u"Verify meHello", tmp);
+ EXPECT_STR16EQ(u"Verify meHello", tmp.c_str());
}
TEST(String16Test, Insert) {
String16 tmp("Verify me");
tmp.insert(6, u"Insert");
EXPECT_EQ(15U, tmp.size());
- EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp.c_str());
}
TEST(String16Test, ReplaceAll) {
String16 tmp("Verify verify Verify");
tmp.replaceAll(u'r', u'!');
- EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp.c_str());
}
TEST(String16Test, Compare) {
@@ -127,8 +127,8 @@
TEST(String16Test, StaticStringCopy) {
StaticString16 tmp(u"Verify me");
String16 another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(tmp.isStaticString());
EXPECT_TRUE(another.isStaticString());
}
@@ -136,7 +136,7 @@
TEST(String16Test, StaticStringMove) {
StaticString16 tmp(u"Verify me");
String16 another(std::move(tmp));
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(another.isStaticString());
}
@@ -157,7 +157,7 @@
StaticString16 tmp(u"Verify me");
tmp.append(String16("Hello"));
EXPECT_EQ(14U, tmp.size());
- EXPECT_STR16EQ(u"Verify meHello", tmp);
+ EXPECT_STR16EQ(u"Verify meHello", tmp.c_str());
EXPECT_FALSE(tmp.isStaticString());
}
@@ -165,14 +165,14 @@
StaticString16 tmp(u"Verify me");
tmp.insert(6, u"Insert");
EXPECT_EQ(15U, tmp.size());
- EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp.c_str());
EXPECT_FALSE(tmp.isStaticString());
}
TEST(String16Test, StaticStringReplaceAll) {
StaticString16 tmp(u"Verify verify Verify");
tmp.replaceAll(u'r', u'!');
- EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp.c_str());
EXPECT_FALSE(tmp.isStaticString());
}
@@ -185,17 +185,17 @@
StaticString16 tmp(u"Verify me");
String16 another(u"nonstatic");
another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, StringCopyAssignFromStaticString) {
StaticString16 tmp(u"Verify me");
String16 another(u"nonstatic");
another = tmp;
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(another.isStaticString());
- EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
EXPECT_TRUE(tmp.isStaticString());
}
@@ -203,7 +203,7 @@
StaticString16 tmp(u"Verify me");
String16 another(u"nonstatic");
another = std::move(tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(another.isStaticString());
}
@@ -221,19 +221,19 @@
TEST(String16Test, ValidUtf8Conversion) {
String16 another("abcdef");
EXPECT_EQ(6U, another.size());
- EXPECT_STR16EQ(another, u"abcdef");
+ EXPECT_STR16EQ(another.c_str(), u"abcdef");
}
TEST(String16Test, append) {
String16 s;
EXPECT_EQ(OK, s.append(String16(u"foo")));
- EXPECT_STR16EQ(u"foo", s);
+ EXPECT_STR16EQ(u"foo", s.c_str());
EXPECT_EQ(OK, s.append(String16(u"bar")));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
EXPECT_EQ(OK, s.append(u"baz", 0));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
EXPECT_EQ(NO_MEMORY, s.append(u"baz", SIZE_MAX));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
}
TEST(String16Test, insert) {
@@ -241,19 +241,19 @@
// Inserting into the empty string inserts at the start.
EXPECT_EQ(OK, s.insert(123, u"foo"));
- EXPECT_STR16EQ(u"foo", s);
+ EXPECT_STR16EQ(u"foo", s.c_str());
// Inserting zero characters at any position is okay, but won't expand the string.
EXPECT_EQ(OK, s.insert(123, u"foo", 0));
- EXPECT_STR16EQ(u"foo", s);
+ EXPECT_STR16EQ(u"foo", s.c_str());
// Inserting past the end of a non-empty string appends.
EXPECT_EQ(OK, s.insert(123, u"bar"));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
EXPECT_EQ(OK, s.insert(3, u"!"));
- EXPECT_STR16EQ(u"foo!bar", s);
+ EXPECT_STR16EQ(u"foo!bar", s.c_str());
EXPECT_EQ(NO_MEMORY, s.insert(3, u"", SIZE_MAX));
- EXPECT_STR16EQ(u"foo!bar", s);
+ EXPECT_STR16EQ(u"foo!bar", s.c_str());
}
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 2b72847..4301f0e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -430,31 +430,6 @@
// ---------------------------------------------------------------------------
// Path functions
-static void setPathName(String8& s, const char* name) {
- size_t len = strlen(name);
- char* buf = s.lockBuffer(len);
-
- memcpy(buf, name, len);
-
- // remove trailing path separator, if present
- if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
- buf[len] = '\0';
-
- s.unlockBuffer(len);
-}
-
-String8 String8::getPathLeaf(void) const
-{
- const char* cp;
- const char*const buf = mString;
-
- cp = strrchr(buf, OS_PATH_SEPARATOR);
- if (cp == nullptr)
- return String8(*this);
- else
- return String8(cp+1);
-}
-
String8 String8::getPathDir(void) const
{
const char* cp;
@@ -467,40 +442,14 @@
return String8(str, cp - str);
}
-String8 String8::walkPath(String8* outRemains) const
-{
- const char* cp;
- const char*const str = mString;
- const char* buf = str;
-
- cp = strchr(buf, OS_PATH_SEPARATOR);
- if (cp == buf) {
- // don't include a leading '/'.
- buf = buf+1;
- cp = strchr(buf, OS_PATH_SEPARATOR);
- }
-
- if (cp == nullptr) {
- String8 res = buf != str ? String8(buf) : *this;
- if (outRemains) *outRemains = String8("");
- return res;
- }
-
- String8 res(buf, cp-buf);
- if (outRemains) *outRemains = String8(cp+1);
- return res;
-}
-
/*
* Helper function for finding the start of an extension in a pathname.
*
* Returns a pointer inside mString, or NULL if no extension was found.
*/
-char* String8::find_extension(void) const
-{
+static const char* find_extension(const char* str) {
const char* lastSlash;
const char* lastDot;
- const char* const str = mString;
// only look at the filename
lastSlash = strrchr(str, OS_PATH_SEPARATOR);
@@ -515,67 +464,16 @@
return nullptr;
// looks good, ship it
- return const_cast<char*>(lastDot);
+ return lastDot;
}
String8 String8::getPathExtension(void) const
{
- char* ext;
-
- ext = find_extension();
+ auto ext = find_extension(mString);
if (ext != nullptr)
return String8(ext);
else
return String8("");
}
-String8 String8::getBasePath(void) const
-{
- char* ext;
- const char* const str = mString;
-
- ext = find_extension();
- if (ext == nullptr)
- return String8(*this);
- else
- return String8(str, ext - str);
-}
-
-String8& String8::appendPath(const char* name)
-{
- // TODO: The test below will fail for Win32 paths. Fix later or ignore.
- if (name[0] != OS_PATH_SEPARATOR) {
- if (*name == '\0') {
- // nothing to do
- return *this;
- }
-
- size_t len = length();
- if (len == 0) {
- // no existing filename, just use the new one
- setPathName(*this, name);
- return *this;
- }
-
- // make room for oldPath + '/' + newPath
- int newlen = strlen(name);
-
- char* buf = lockBuffer(len+1+newlen);
-
- // insert a '/' if needed
- if (buf[len-1] != OS_PATH_SEPARATOR)
- buf[len++] = OS_PATH_SEPARATOR;
-
- memcpy(buf+len, name, newlen+1);
- len += newlen;
-
- unlockBuffer(len);
-
- return *this;
- } else {
- setPathName(*this, name);
- return *this;
- }
-}
-
}; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index 6f7a54f..cbce050 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -68,30 +68,6 @@
int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
str1->find(str2->c_str(), start_index);
},
-
- // Path handling
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getBasePath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathExtension();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathLeaf();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathDir();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- std::shared_ptr<android::String8> path_out_str =
- std::make_shared<android::String8>();
- str1->walkPath(path_out_str.get());
- path_out_str->clear();
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8*) -> void {
- str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
- },
};
void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 9c12cb1..e1fd13a 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -100,19 +100,19 @@
TEST_F(String8Test, ValidUtf16Conversion) {
char16_t tmp[] = u"abcdef";
String8 valid = String8(String16(tmp));
- EXPECT_STREQ(valid, "abcdef");
+ EXPECT_STREQ(valid.c_str(), "abcdef");
}
TEST_F(String8Test, append) {
String8 s;
EXPECT_EQ(OK, s.append("foo"));
- EXPECT_STREQ("foo", s);
+ EXPECT_STREQ("foo", s.c_str());
EXPECT_EQ(OK, s.append("bar"));
- EXPECT_STREQ("foobar", s);
+ EXPECT_STREQ("foobar", s.c_str());
EXPECT_EQ(OK, s.append("baz", 0));
- EXPECT_STREQ("foobar", s);
+ EXPECT_STREQ("foobar", s.c_str());
EXPECT_EQ(NO_MEMORY, s.append("baz", SIZE_MAX));
- EXPECT_STREQ("foobar", s);
+ EXPECT_STREQ("foobar", s.c_str());
}
TEST_F(String8Test, removeAll) {
@@ -123,12 +123,12 @@
// expect to return true and string content should remain unchanged
EXPECT_TRUE(s.removeAll(""));
- EXPECT_STREQ("Hello, world!", s);
+ EXPECT_STREQ("Hello, world!", s.c_str());
// expect to return false
EXPECT_FALSE(s.removeAll("x"));
- EXPECT_STREQ("Hello, world!", s);
+ EXPECT_STREQ("Hello, world!", s.c_str());
EXPECT_TRUE(s.removeAll("o"));
- EXPECT_STREQ("Hell, wrld!", s);
+ EXPECT_STREQ("Hell, wrld!", s.c_str());
}
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index 46badde..8881b44 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
"name" : "_ZN7android7RefBaseD2Ev"
},
{
- "name" : "_ZN7android7String810appendPathEPKc"
- },
- {
"name" : "_ZN7android7String810lockBufferEm"
},
{
@@ -1145,15 +1142,6 @@
"name" : "_ZNK7android7String810getPathDirEv"
},
{
- "name" : "_ZNK7android7String811getBasePathEv"
- },
- {
- "name" : "_ZNK7android7String811getPathLeafEv"
- },
- {
- "name" : "_ZNK7android7String814find_extensionEv"
- },
- {
"name" : "_ZNK7android7String816getPathExtensionEv"
},
{
@@ -1163,9 +1151,6 @@
"name" : "_ZNK7android7String86lengthEv"
},
{
- "name" : "_ZNK7android7String88walkPathEPS0_"
- },
- {
"name" : "_ZNK7android8String1610startsWithEPKDs"
},
{
@@ -6809,22 +6794,6 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
- "function_name" : "android::String8::appendPath",
- "linker_set_key" : "_ZN7android7String810appendPathEPKc",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- },
- {
- "referenced_type" : "_ZTIPKc"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::lockBuffer",
"linker_set_key" : "_ZN7android7String810lockBufferEm",
"parameters" :
@@ -9104,6 +9073,7 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
+ "access" : "private",
"function_name" : "android::String8::getPathDir",
"linker_set_key" : "_ZNK7android7String810getPathDirEv",
"parameters" :
@@ -9117,46 +9087,7 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::getBasePath",
- "linker_set_key" : "_ZNK7android7String811getBasePathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
- "function_name" : "android::String8::getPathLeaf",
- "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"access" : "private",
- "function_name" : "android::String8::find_extension",
- "linker_set_key" : "_ZNK7android7String814find_extensionEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIPc",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::getPathExtension",
"linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
"parameters" :
@@ -9203,23 +9134,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::walkPath",
- "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- },
- {
- "default_arg" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String16::startsWith",
"linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
"parameters" :
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index 219c766..e8236ea 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
"name" : "_ZN7android7RefBaseD2Ev"
},
{
- "name" : "_ZN7android7String810appendPathEPKc"
- },
- {
"name" : "_ZN7android7String810lockBufferEj"
},
{
@@ -1145,15 +1142,6 @@
"name" : "_ZNK7android7String810getPathDirEv"
},
{
- "name" : "_ZNK7android7String811getBasePathEv"
- },
- {
- "name" : "_ZNK7android7String811getPathLeafEv"
- },
- {
- "name" : "_ZNK7android7String814find_extensionEv"
- },
- {
"name" : "_ZNK7android7String816getPathExtensionEv"
},
{
@@ -1163,9 +1151,6 @@
"name" : "_ZNK7android7String86lengthEv"
},
{
- "name" : "_ZNK7android7String88walkPathEPS0_"
- },
- {
"name" : "_ZNK7android8String1610startsWithEPKDs"
},
{
@@ -6805,22 +6790,6 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
- "function_name" : "android::String8::appendPath",
- "linker_set_key" : "_ZN7android7String810appendPathEPKc",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- },
- {
- "referenced_type" : "_ZTIPKc"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::lockBuffer",
"linker_set_key" : "_ZN7android7String810lockBufferEj",
"parameters" :
@@ -9100,6 +9069,7 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
+ "access" : "private",
"function_name" : "android::String8::getPathDir",
"linker_set_key" : "_ZNK7android7String810getPathDirEv",
"parameters" :
@@ -9113,46 +9083,7 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::getBasePath",
- "linker_set_key" : "_ZNK7android7String811getBasePathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
- "function_name" : "android::String8::getPathLeaf",
- "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"access" : "private",
- "function_name" : "android::String8::find_extension",
- "linker_set_key" : "_ZNK7android7String814find_extensionEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIPc",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::getPathExtension",
"linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
"parameters" :
@@ -9199,23 +9130,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::walkPath",
- "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- },
- {
- "default_arg" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String16::startsWith",
"linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
"parameters" :
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index b48b907..c713576 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -24,6 +24,11 @@
#include <utils/String8.h>
#include <utils/TypeHelpers.h>
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -54,11 +59,7 @@
~String16();
inline const char16_t* c_str() const;
- inline const char16_t* string() const;
-private:
- static inline std::string std_string(const String16& str);
-public:
size_t size() const;
inline bool empty() const;
@@ -91,6 +92,7 @@
bool startsWith(const char16_t* prefix) const;
bool contains(const char16_t* chrs) const;
+ inline bool contains(const String16& other) const;
status_t replaceAll(char16_t replaceThis,
char16_t withThis);
@@ -113,6 +115,12 @@
inline operator const char16_t*() const;
+#ifdef HAS_STRING_VIEW
+ // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much
+ // lighter and if one needs, they can still create u16string from u16string_view.
+ inline operator std::u16string_view() const;
+#endif
+
// Static and non-static String16 behave the same for the users, so
// this method isn't of much use for the users. It is public for testing.
bool isStaticString() const;
@@ -179,6 +187,14 @@
template <size_t N>
explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+// These symbols are for potential backward compatibility with prebuilts. To be removed.
+#ifdef ENABLE_STRING16_OBSOLETE_METHODS
+public:
+#else
+private:
+#endif
+ inline const char16_t* string() const;
};
// String16 can be trivially moved using memcpy() because moving does not
@@ -249,11 +265,6 @@
return mString;
}
-inline std::string String16::std_string(const String16& str)
-{
- return std::string(String8(str).c_str());
-}
-
inline bool String16::empty() const
{
return length() == 0;
@@ -264,6 +275,11 @@
return size();
}
+inline bool String16::contains(const String16& other) const
+{
+ return contains(other.c_str());
+}
+
inline String16& String16::operator=(const String16& other)
{
setTo(other);
@@ -353,8 +369,15 @@
return mString;
}
+inline String16::operator std::u16string_view() const
+{
+ return {mString, length()};
+}
+
} // namespace android
// ---------------------------------------------------------------------------
+#undef HAS_STRING_VIEW
+
#endif // ANDROID_STRING16_H
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index ea25c6a..6d25072 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -18,7 +18,6 @@
#define ANDROID_STRING8_H
#include <iostream>
-#include <string>
#include <utils/Errors.h>
#include <utils/Unicode.h>
@@ -27,6 +26,16 @@
#include <string.h> // for strcmp
#include <stdarg.h>
+#if __has_include(<string>)
+#include <string>
+#define HAS_STRING
+#endif
+
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -56,16 +65,10 @@
static String8 formatV(const char* fmt, va_list args);
inline const char* c_str() const;
- inline const char* string() const;
-
-private:
- static inline std::string std_string(const String8& str);
-public:
inline size_t size() const;
inline size_t bytes() const;
inline bool empty() const;
- inline bool isEmpty() const;
size_t length() const;
@@ -113,6 +116,10 @@
inline operator const char*() const;
+#ifdef HAS_STRING_VIEW
+ inline explicit operator std::string_view() const;
+#endif
+
char* lockBuffer(size_t size);
void unlockBuffer();
status_t unlockBuffer(size_t size);
@@ -120,92 +127,35 @@
// return the index of the first byte of other in this at or after
// start, or -1 if not found
ssize_t find(const char* other, size_t start = 0) const;
+ inline ssize_t find(const String8& other, size_t start = 0) const;
// return true if this string contains the specified substring
inline bool contains(const char* other) const;
+ inline bool contains(const String8& other) const;
// removes all occurrence of the specified substring
// returns true if any were found and removed
bool removeAll(const char* other);
+ inline bool removeAll(const String8& other);
void toLower();
-
- /*
- * These methods operate on the string as if it were a path name.
- */
-
- /*
- * Get just the filename component.
- *
- * "/tmp/foo/bar.c" --> "bar.c"
- */
- String8 getPathLeaf(void) const;
-
- /*
- * Remove the last (file name) component, leaving just the directory
- * name.
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo"
- * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
- * "bar.c" --> ""
- */
- String8 getPathDir(void) const;
-
- /*
- * Retrieve the front (root dir) component. Optionally also return the
- * remaining components.
- *
- * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
- * "/tmp" --> "tmp" (remain = "")
- * "bar.c" --> "bar.c" (remain = "")
- */
- String8 walkPath(String8* outRemains = nullptr) const;
-
- /*
- * Return the filename extension. This is the last '.' and any number
- * of characters that follow it. The '.' is included in case we
- * decide to expand our definition of what constitutes an extension.
- *
- * "/tmp/foo/bar.c" --> ".c"
- * "/tmp" --> ""
- * "/tmp/foo.bar/baz" --> ""
- * "foo.jpeg" --> ".jpeg"
- * "foo." --> ""
- */
- String8 getPathExtension(void) const;
-
- /*
- * Return the path without the extension. Rules for what constitutes
- * an extension are described in the comment for getPathExtension().
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
- */
- String8 getBasePath(void) const;
-
- /*
- * Add a component to the pathname. We guarantee that there is
- * exactly one path separator between the old path and the new.
- * If there is no existing name, we just copy the new name in.
- *
- * If leaf is a fully qualified path (i.e. starts with '/', it
- * replaces whatever was there before.
- */
- String8& appendPath(const char* leaf);
- String8& appendPath(const String8& leaf) { return appendPath(leaf.c_str()); }
-
- /*
- * Like appendPath(), but does not affect this string. Returns a new one instead.
- */
- String8 appendPathCopy(const char* leaf) const
- { String8 p(*this); p.appendPath(leaf); return p; }
- String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.c_str()); }
-
private:
+ String8 getPathDir(void) const;
+ String8 getPathExtension(void) const;
+
status_t real_append(const char* other, size_t numChars);
- char* find_extension(void) const;
const char* mString;
+
+// These symbols are for potential backward compatibility with prebuilts. To be removed.
+#ifdef ENABLE_STRING8_OBSOLETE_METHODS
+public:
+#else
+private:
+#endif
+ inline const char* string() const;
+ inline bool isEmpty() const;
};
// String8 can be trivially moved using memcpy() because moving does not
@@ -239,11 +189,6 @@
return mString;
}
-inline std::string String8::std_string(const String8& str)
-{
- return std::string(str.c_str());
-}
-
inline size_t String8::size() const
{
return length();
@@ -264,11 +209,26 @@
return length();
}
+inline ssize_t String8::find(const String8& other, size_t start) const
+{
+ return find(other.c_str(), start);
+}
+
inline bool String8::contains(const char* other) const
{
return find(other) >= 0;
}
+inline bool String8::contains(const String8& other) const
+{
+ return contains(other.c_str());
+}
+
+inline bool String8::removeAll(const String8& other)
+{
+ return removeAll(other.c_str());
+}
+
inline String8& String8::operator=(const String8& other)
{
setTo(other);
@@ -377,8 +337,18 @@
return mString;
}
+#ifdef HAS_STRING_VIEW
+inline String8::operator std::string_view() const
+{
+ return {mString, length()};
+}
+#endif
+
} // namespace android
// ---------------------------------------------------------------------------
+#undef HAS_STRING
+#undef HAS_STRING_VIEW
+
#endif // ANDROID_STRING8_H
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 47f77b1..d72ac66 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -11,9 +11,6 @@
"libsigchain.so",
// TODO(b/122876336): Remove libpac.so once it's migrated to Webview
"libpac.so",
- // TODO(b/184872979): Remove libbinder_rpc_unstable.so once stablized and
- // added to libbinder_ndk.
- "libbinder_rpc_unstable.so",
// TODO(b/120786417 or b/134659294): libicuuc.so
// and libicui18n.so are kept for app compat.
"libicui18n.so",
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index cacc47c..9be2ef6 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
libandroid.so
libaaudio.so
libamidi.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index 77f8bb8..0513968 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
libandroid.so
libandroidthings.so
libaaudio.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index ea1e234..5915dcb 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
libandroid.so
libaaudio.so
libamidi.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 487e5da..317f809 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -219,6 +219,26 @@
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
+ # Create blkio group and apply initial settings.
+ # This feature needs kernel to support it, and the
+ # device's init.rc must actually set the correct values.
+ mkdir /dev/blkio/background
+ chown system system /dev/blkio
+ chown system system /dev/blkio/background
+ chown system system /dev/blkio/tasks
+ chown system system /dev/blkio/background/tasks
+ chown system system /dev/blkio/cgroup.procs
+ chown system system /dev/blkio/background/cgroup.procs
+ chmod 0664 /dev/blkio/tasks
+ chmod 0664 /dev/blkio/background/tasks
+ chmod 0664 /dev/blkio/cgroup.procs
+ chmod 0664 /dev/blkio/background/cgroup.procs
+ write /dev/blkio/blkio.weight 1000
+ write /dev/blkio/background/blkio.weight 200
+ write /dev/blkio/background/blkio.bfq.weight 10
+ write /dev/blkio/blkio.group_idle 0
+ write /dev/blkio/background/blkio.group_idle 0
+
restorecon_recursive /mnt
mount configfs none /config nodev noexec nosuid
@@ -610,7 +630,6 @@
restorecon_recursive /metadata/apex
mkdir /metadata/staged-install 0770 root system
- mkdir /metadata/sepolicy 0700 root root
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@@ -1057,6 +1076,9 @@
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
+on boot && property:suspend.disable_sync_on_suspend=true
+ write /sys/power/sync_on_suspend 0
+
on boot
# basic network init
ifup lo
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 2f0ec8a..442bd15 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,7 +13,7 @@
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
- onrestart restart media.tuner
+ onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 74a64c8..3422121 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,7 +13,7 @@
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
- onrestart restart media.tuner
+ onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e2c77c3..f65bb20 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -111,8 +111,7 @@
break;
bits_size = res + 16;
bits = realloc(bits, bits_size * 2);
- if(bits == NULL)
- err(1, "failed to allocate buffer of size %d\n", (int)bits_size);
+ if (bits == NULL) err(1, "failed to allocate buffer of size %zd", bits_size);
}
res2 = 0;
switch(i) {
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 17d083c..f782d2a 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -19,6 +19,7 @@
#include <BufferAllocator/BufferAllocator.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
diff --git a/trusty/keymaster/TEST_MAPPING b/trusty/keymaster/TEST_MAPPING
index 0475e04..4f082d8 100644
--- a/trusty/keymaster/TEST_MAPPING
+++ b/trusty/keymaster/TEST_MAPPING
@@ -10,13 +10,15 @@
"name": "RkpdAppUnitTests"
},
{
- "name": "RkpdAppGoogleUnitTests"
+ "name": "RkpdAppGoogleUnitTests",
+ "keywords": ["internal"]
},
{
"name": "RkpdAppIntegrationTests"
},
{
- "name": "RkpdAppGoogleIntegrationTests"
+ "name": "RkpdAppGoogleIntegrationTests",
+ "keywords": ["internal"]
}
]
}
diff --git a/trusty/stats/test/README.md b/trusty/stats/test/README.md
index 45e6af8..175409e 100644
--- a/trusty/stats/test/README.md
+++ b/trusty/stats/test/README.md
@@ -1,8 +1,8 @@
# Development Notes
-* First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/master/tools/repo_pull/) from aosp.
+* First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/) from aosp.
-* Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/master/tools/repo_pull/#installation) steps if necessary.
+* Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/#installation) steps if necessary.
## Build
diff --git a/trusty/stats/test/stats_test.cpp b/trusty/stats/test/stats_test.cpp
index 1edddeb..1d6eb34 100644
--- a/trusty/stats/test/stats_test.cpp
+++ b/trusty/stats/test/stats_test.cpp
@@ -252,20 +252,20 @@
::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
::testing::Eq(TrustyAtoms::TrustyError),
::testing::Eq(TrustyAtoms::TrustyStorageError)));
- ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+ ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
switch (vendorAtom.atomId) {
case TrustyAtoms::TrustyAppCrashed:
++atomAppCrashedCnt;
- ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
- "5247d19b-cf09-4272-a450-3ef20dbefc14");
+ ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+ "5247d19b-cf09-4272-a450-3ef20dbefc14");
break;
case TrustyAtoms::TrustyStorageError:
++atomStorageErrorCnt;
ASSERT_EQ(vendorAtom.values[0].get<VendorAtomValue::intValue>(), 5);
- ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
- "5247d19b-cf09-4272-a450-3ef20dbefc14");
- ASSERT_STREQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
- "5247d19b-cf09-4272-a450-3ef20dbefc14");
+ ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
+ "5247d19b-cf09-4272-a450-3ef20dbefc14");
+ ASSERT_EQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
+ "5247d19b-cf09-4272-a450-3ef20dbefc14");
ASSERT_EQ(vendorAtom.values[3].get<VendorAtomValue::intValue>(), 1);
ASSERT_EQ(vendorAtom.values[4].get<VendorAtomValue::intValue>(), 3);
ASSERT_EQ(vendorAtom.values[5].get<VendorAtomValue::longValue>(),
@@ -330,13 +330,13 @@
::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
::testing::Eq(TrustyAtoms::TrustyError),
::testing::Eq(TrustyAtoms::TrustyStorageError)));
- ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+ ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
switch (vendorAtom.atomId) {
case TrustyAtoms::TrustyAppCrashed:
++atomAppCrashedCnt;
- ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
- kTrustyCrasherUuid);
+ ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+ kTrustyCrasherUuid);
atomCrashReasons.push_back(vendorAtom.values[1].get<VendorAtomValue::intValue>());
break;
case TrustyAtoms::TrustyStorageError:
@@ -344,7 +344,7 @@
break;
case TrustyAtoms::TrustyError:
++atomTrustyErrorCnt;
- ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
+ ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
break;
default:
FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
diff --git a/trusty/utils/acvp/trusty_modulewrapper.cpp b/trusty/utils/acvp/trusty_modulewrapper.cpp
index 70ffb52..85b7159 100644
--- a/trusty/utils/acvp/trusty_modulewrapper.cpp
+++ b/trusty/utils/acvp/trusty_modulewrapper.cpp
@@ -21,15 +21,16 @@
#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <errno.h>
+#include <iostream>
#include <log/log.h>
#include <modulewrapper.h>
#include <openssl/span.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
#include <trusty/tipc.h>
#include <unistd.h>
-#include <iostream>
#include "acvp_ipc.h"
@@ -208,6 +209,11 @@
return {};
}
+static bool EqString(bssl::Span<const uint8_t> cmd, const char *str) {
+ return cmd.size() == strlen(str) &&
+ memcmp(str, cmd.data(), cmd.size()) == 0;
+}
+
int main() {
for (;;) {
auto buffer = bssl::acvp::RequestBuffer::New();
@@ -217,17 +223,24 @@
return EXIT_FAILURE;
}
- ModuleWrapper wrapper;
- auto res = wrapper.SendMessage(args);
- if (!res.ok()) {
- std::cerr << res.error() << std::endl;
- return EXIT_FAILURE;
- }
+ if (EqString(args[0], "flush")) {
+ if (!bssl::acvp::FlushBuffer(STDOUT_FILENO)) {
+ ALOGE("Could not flush the buffer to stdout\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ ModuleWrapper wrapper;
+ auto res = wrapper.SendMessage(args);
+ if (!res.ok()) {
+ std::cerr << res.error() << std::endl;
+ return EXIT_FAILURE;
+ }
- res = wrapper.ForwardResponse();
- if (!res.ok()) {
- std::cerr << res.error() << std::endl;
- return EXIT_FAILURE;
+ res = wrapper.ForwardResponse();
+ if (!res.ok()) {
+ std::cerr << res.error() << std::endl;
+ return EXIT_FAILURE;
+ }
}
}
diff --git a/trusty/utils/coverage-controller/controller.cpp b/trusty/utils/coverage-controller/controller.cpp
index 0047046..381a452 100644
--- a/trusty/utils/coverage-controller/controller.cpp
+++ b/trusty/utils/coverage-controller/controller.cpp
@@ -54,7 +54,7 @@
if (complete_cnt != counters[index] && start_cnt == complete_cnt) {
WRITE_ONCE(control->cntrl_flags, FLAG_NONE);
std::string filename;
- filename = android::base::StringPrintf("/%s.%lu.profraw",
+ filename = android::base::StringPrintf("/%s.%" PRIu64 ".profraw",
uuid_list_[index].c_str(),
counters[index]);
filename.insert(0, output_dir);