Merge changes from topic "init-snapuserd"

* changes:
  libsnapshot: Implement MapAllSnapshots and UnmapAllSnapshots.
  Start snapuserd if needed as soon as possible during second-stage init.
  libsnapshot: Add support for first-to-second-stage transitions of snapuserd.
This commit is contained in:
David Anderson 2020-11-03 18:24:49 +00:00 committed by Gerrit Code Review
commit dfaede06bc
11 changed files with 396 additions and 32 deletions

View file

@ -31,15 +31,17 @@ cc_defaults {
"libbrotli",
"libdm",
"libfstab",
"libsnapshot_cow",
"update_metadata-protos",
],
whole_static_libs: [
"libbrotli",
"libcutils",
"libext2_uuid",
"libext4_utils",
"libfstab",
"libsnapshot_cow",
"libsnapshot_snapuserd",
"libz",
],
header_libs: [
"libchrome",
@ -432,6 +434,7 @@ cc_binary {
init_rc: [
"snapuserd.rc",
],
static_executable: true,
}
cc_binary {

View file

@ -15,6 +15,7 @@
#pragma once
#include <stdint.h>
#include <unistd.h>
#include <chrono>
#include <map>
@ -77,6 +78,7 @@ class SnapshotMergeStats;
class SnapshotStatus;
static constexpr const std::string_view kCowGroupName = "cow";
static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,
chromeos_update_engine::InstallOperation* optimized);
@ -104,6 +106,7 @@ class ISnapshotManager {
android::hardware::boot::V1_1::MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; }
};
virtual ~ISnapshotManager() = default;
@ -303,6 +306,14 @@ class SnapshotManager final : public ISnapshotManager {
// Helper function for second stage init to restorecon on the rollback indicator.
static std::string GetGlobalRollbackIndicatorPath();
// Initiate the transition from first-stage to second-stage snapuserd. This
// process involves re-creating the dm-user table entries for each device,
// so that they connect to the new daemon. Once all new tables have been
// activated, we ask the first-stage daemon to cleanly exit.
//
// The caller must pass a function which starts snapuserd.
bool PerformSecondStageTransition();
// ISnapshotManager overrides.
bool BeginUpdate() override;
bool CancelUpdate() override;
@ -345,6 +356,7 @@ class SnapshotManager final : public ISnapshotManager {
FRIEND_TEST(SnapshotTest, Merge);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
@ -372,11 +384,13 @@ class SnapshotManager final : public ISnapshotManager {
// Ensure we're connected to snapuserd.
bool EnsureSnapuserdConnected();
// Helper for first-stage init.
// Helpers for first-stage init.
bool ForceLocalImageManager();
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
// Helper function for tests.
// Helper functions for tests.
IImageManager* image_manager() const { return images_.get(); }
void set_use_first_stage_snapuserd(bool value) { use_first_stage_snapuserd_ = value; }
// Since libsnapshot is included into multiple processes, we flock() our
// files for simple synchronization. LockedFile is a helper to assist with
@ -545,6 +559,9 @@ class SnapshotManager final : public ISnapshotManager {
std::string GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status);
bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
const std::chrono::milliseconds& timeout_ms);
// Reason for calling MapPartitionWithSnapshot.
enum class SnapshotContext {
// For writing or verification (during update_engine).
@ -618,9 +635,12 @@ class SnapshotManager final : public ISnapshotManager {
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
// Implementation of UnmapAllSnapshots(), with the lock provided.
bool UnmapAllSnapshots(LockedFile* lock);
// Unmap all partitions that were mapped by CreateLogicalAndSnapshotPartitions.
// This should only be called in recovery.
bool UnmapAllPartitions();
bool UnmapAllPartitionsInRecovery();
// Check no snapshot overflows. Note that this returns false negatives if the snapshot
// overflows, then is remapped and not written afterwards.
@ -660,6 +680,7 @@ class SnapshotManager final : public ISnapshotManager {
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
bool has_local_image_manager_ = false;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
std::unique_ptr<SnapuserdClient> snapuserd_client_;
};

View file

@ -14,6 +14,8 @@
#pragma once
#include <unistd.h>
#include <chrono>
#include <cstring>
#include <iostream>
@ -31,9 +33,15 @@ static constexpr uint32_t PACKET_SIZE = 512;
static constexpr char kSnapuserdSocketFirstStage[] = "snapuserd_first_stage";
static constexpr char kSnapuserdSocket[] = "snapuserd";
static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
// Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted();
// Start the first-stage version of snapuserd, returning its pid. This is used
// by first-stage init, as well as vts_libsnapshot_test. On failure, -1 is returned.
pid_t StartFirstStageSnapuserd();
class SnapuserdClient {
private:
android::base::unique_fd sockfd_;

View file

@ -95,6 +95,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
unbootable_slots_.insert(slot);
return true;
}
bool IsTestDevice() const override { return true; }
bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }

View file

@ -31,8 +31,10 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <fs_mgr/file_wait.h>
#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
@ -100,6 +102,12 @@ std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceI
if (!sm || !sm->ForceLocalImageManager()) {
return nullptr;
}
// The first-stage version of snapuserd is explicitly started by init. Do
// not attempt to using it during tests (which run in normal AOSP).
if (!sm->device()->IsTestDevice()) {
sm->use_first_stage_snapuserd_ = true;
}
return sm;
}
@ -400,8 +408,15 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
base_sectors = dev_size / kSectorSize;
}
// Use an extra decoration for first-stage init, so we can transition
// to a new table entry in second-stage.
std::string misc_name = name;
if (use_first_stage_snapuserd_) {
misc_name += "-init";
}
DmTable table;
table.Emplace<DmTargetUser>(0, base_sectors, name);
table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
@ -410,7 +425,7 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
return false;
}
auto control_device = "/dev/dm-user/" + name;
auto control_device = "/dev/dm-user/" + misc_name;
return snapuserd_client_->InitializeSnapuserd(cow_file, base_device, control_device);
}
@ -1284,6 +1299,107 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
return RemoveAllUpdateState(lock, before_cancel);
}
bool SnapshotManager::PerformSecondStageTransition() {
LOG(INFO) << "Performing second-stage transition for snapuserd.";
// Don't use EnsuerSnapuserdConnected() because this is called from init,
// and attempting to do so will deadlock.
if (!snapuserd_client_) {
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
}
}
auto& dm = DeviceMapper::Instance();
auto lock = LockExclusive();
if (!lock) return false;
std::vector<std::string> snapshots;
if (!ListSnapshots(lock.get(), &snapshots)) {
LOG(ERROR) << "Failed to list snapshots.";
return false;
}
size_t num_cows = 0;
size_t ok_cows = 0;
for (const auto& snapshot : snapshots) {
std::string cow_name = GetDmUserCowName(snapshot);
if (dm.GetState(cow_name) == DmDeviceState::INVALID) {
continue;
}
DeviceMapper::TargetInfo target;
if (!GetSingleTarget(cow_name, TableQuery::Table, &target)) {
continue;
}
auto target_type = DeviceMapper::GetTargetType(target.spec);
if (target_type != "user") {
LOG(ERROR) << "Unexpected target type for " << cow_name << ": " << target_type;
continue;
}
num_cows++;
DmTable table;
table.Emplace<DmTargetUser>(0, target.spec.length, cow_name);
if (!dm.LoadTableAndActivate(cow_name, table)) {
LOG(ERROR) << "Unable to swap tables for " << cow_name;
continue;
}
std::string backing_device;
if (!dm.GetDmDevicePathByName(GetBaseDeviceName(snapshot), &backing_device)) {
LOG(ERROR) << "Could not get device path for " << GetBaseDeviceName(snapshot);
continue;
}
std::string cow_device;
if (!dm.GetDmDevicePathByName(GetCowName(snapshot), &cow_device)) {
LOG(ERROR) << "Could not get device path for " << GetCowName(snapshot);
continue;
}
// Wait for ueventd to acknowledge and create the control device node.
std::string control_device = "/dev/dm-user/" + cow_name;
if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
LOG(ERROR) << "Could not find control device: " << control_device;
continue;
}
if (!snapuserd_client_->InitializeSnapuserd(cow_device, backing_device, control_device)) {
// This error is unrecoverable. We cannot proceed because reads to
// the underlying device will fail.
LOG(FATAL) << "Could not initialize snapuserd for " << cow_name;
return false;
}
ok_cows++;
}
if (ok_cows != num_cows) {
LOG(ERROR) << "Could not transition all snapuserd consumers.";
return false;
}
int pid;
const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
if (pid_str && android::base::ParseInt(pid_str, &pid)) {
if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
LOG(ERROR) << "kill snapuserd failed";
return false;
}
} else {
LOG(ERROR) << "Could not find or parse " << kSnapuserdFirstStagePidVar
<< " for snapuserd pid";
return false;
}
return true;
}
std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
const auto& opener = device_->GetPartitionOpener();
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
@ -1593,8 +1709,13 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(
auto lock = LockExclusive();
if (!lock) return false;
const auto& opener = device_->GetPartitionOpener();
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
return MapAllPartitions(lock.get(), super_device, slot, timeout_ms);
}
bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& super_device,
uint32_t slot, const std::chrono::milliseconds& timeout_ms) {
const auto& opener = device_->GetPartitionOpener();
auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
if (!metadata) {
LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
@ -1615,12 +1736,20 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(
.partition_opener = &opener,
.timeout_ms = timeout_ms,
};
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
nullptr)) {
if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) {
return false;
}
}
if (use_first_stage_snapuserd_) {
// Remove the first-stage socket as a precaution, there is no need to
// access the daemon anymore and we'll be killing it once second-stage
// is running.
auto socket = ANDROID_SOCKET_DIR + "/"s + kSnapuserdSocketFirstStage;
snapuserd_client_ = nullptr;
unlink(socket.c_str());
}
LOG(INFO) << "Created logical partitions with snapshot.";
return true;
}
@ -1925,10 +2054,18 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
LOG(ERROR) << "Cannot unmap " << dm_user_name;
return false;
}
if (!snapuserd_client_->WaitForDeviceDelete("/dev/dm-user/" + dm_user_name)) {
auto control_device = "/dev/dm-user/" + dm_user_name;
if (!snapuserd_client_->WaitForDeviceDelete(control_device)) {
LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
return false;
}
// Ensure the control device is gone so we don't run into ABA problems.
if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
return false;
}
}
auto cow_name = GetCowName(name);
@ -1945,14 +2082,49 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
return true;
}
bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds&) {
LOG(ERROR) << "Not yet implemented.";
return false;
bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) {
auto lock = LockExclusive();
if (!lock) return false;
auto state = ReadUpdateState(lock.get());
if (state == UpdateState::Unverified) {
if (GetCurrentSlot() == Slot::Target) {
LOG(ERROR) << "Cannot call MapAllSnapshots when booting from the target slot.";
return false;
}
} else if (state != UpdateState::Initiated) {
LOG(ERROR) << "Cannot call MapAllSnapshots from update state: " << state;
return false;
}
if (!UnmapAllSnapshots(lock.get())) {
return false;
}
uint32_t slot = SlotNumberForSlotSuffix(device_->GetOtherSlotSuffix());
return MapAllPartitions(lock.get(), device_->GetSuperDevice(slot), slot, timeout_ms);
}
bool SnapshotManager::UnmapAllSnapshots() {
LOG(ERROR) << "Not yet implemented.";
return false;
auto lock = LockExclusive();
if (!lock) return false;
return UnmapAllSnapshots(lock.get());
}
bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) {
return false;
}
for (const auto& snapshot : snapshots) {
if (!UnmapPartitionWithSnapshot(lock, snapshot)) {
LOG(ERROR) << "Failed to unmap snapshot: " << snapshot;
return false;
}
}
return true;
}
auto SnapshotManager::OpenFile(const std::string& file, int lock_flags)
@ -2212,15 +2384,35 @@ bool SnapshotManager::EnsureImageManager() {
}
bool SnapshotManager::EnsureSnapuserdConnected() {
if (!snapuserd_client_) {
if (snapuserd_client_) {
return true;
}
std::string socket;
if (use_first_stage_snapuserd_) {
auto pid = StartFirstStageSnapuserd();
if (pid < 0) {
LOG(ERROR) << "Failed to start snapuserd";
return false;
}
auto pid_str = std::to_string(static_cast<int>(pid));
if (setenv(kSnapuserdFirstStagePidVar, pid_str.c_str(), 1) < 0) {
PLOG(ERROR) << "setenv failed storing the snapuserd pid";
}
socket = kSnapuserdSocketFirstStage;
} else {
if (!EnsureSnapuserdStarted()) {
return false;
}
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
}
socket = kSnapuserdSocket;
}
snapuserd_client_ = SnapuserdClient::Connect(socket, 10s);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
}
return true;
}
@ -2538,11 +2730,26 @@ Return SnapshotManager::InitializeUpdateSnapshots(
return Return::Error();
}
auto ret = InitializeCow(cow_path);
if (!ret.is_ok()) {
LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
<< cow_path;
return AddRequiredSpace(ret, all_snapshot_status);
if (IsCompressionEnabled()) {
unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
<< cow_params.partition_name;
return Return::Error();
}
CowWriter writer(CowOptions{});
if (!writer.Initialize(fd) || !writer.Finalize()) {
LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
return Return::Error();
}
} else {
auto ret = InitializeKernelCow(cow_path);
if (!ret.is_ok()) {
LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
<< cow_path;
return AddRequiredSpace(ret, all_snapshot_status);
}
}
// Let destructor of created_devices_for_cow to unmap the COW devices.
};
@ -2700,7 +2907,7 @@ bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_na
return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
}
bool SnapshotManager::UnmapAllPartitions() {
bool SnapshotManager::UnmapAllPartitionsInRecovery() {
auto lock = LockExclusive();
if (!lock) return false;
@ -2844,7 +3051,7 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
}
// Nothing should be depending on partitions now, so unmap them all.
if (!UnmapAllPartitions()) {
if (!UnmapAllPartitionsInRecovery()) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
}
return true;
@ -2875,7 +3082,7 @@ bool SnapshotManager::FinishMergeInRecovery() {
}
// Nothing should be depending on partitions now, so unmap them all.
if (!UnmapAllPartitions()) {
if (!UnmapAllPartitionsInRecovery()) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
}
return true;

View file

@ -15,6 +15,7 @@
#include <libsnapshot/snapshot.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
@ -29,6 +30,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_mgr/file_wait.h>
#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
@ -118,6 +120,8 @@ class SnapshotTest : public ::testing::Test {
image_manager_ = sm->image_manager();
test_device->set_slot_suffix("_a");
sm->set_use_first_stage_snapuserd(false);
}
void CleanupTestArtifacts() {
@ -265,7 +269,7 @@ class SnapshotTest : public ::testing::Test {
if (!map_res) {
return map_res;
}
if (!InitializeCow(cow_device)) {
if (!InitializeKernelCow(cow_device)) {
return AssertionFailure() << "Cannot zero fill " << cow_device;
}
if (!sm->UnmapCowImage(name)) {
@ -1736,6 +1740,74 @@ TEST_F(SnapshotUpdateTest, LowSpace) {
ASSERT_LT(res.required_size(), 15_MiB);
}
class AutoKill final {
public:
explicit AutoKill(pid_t pid) : pid_(pid) {}
~AutoKill() {
if (pid_ > 0) kill(pid_, SIGKILL);
}
bool valid() const { return pid_ > 0; }
private:
pid_t pid_;
};
TEST_F(SnapshotUpdateTest, DaemonTransition) {
if (!IsCompressionEnabled()) {
GTEST_SKIP() << "Skipping Virtual A/B Compression test";
}
AutoKill auto_kill(StartFirstStageSnapuserd());
ASSERT_TRUE(auto_kill.valid());
// Ensure a connection to the second-stage daemon, but use the first-stage
// code paths thereafter.
ASSERT_TRUE(sm->EnsureSnapuserdConnected());
sm->set_use_first_stage_snapuserd(true);
AddOperationForPartitions();
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(UnmapAll());
auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->EnsureSnapuserdConnected());
init->set_use_first_stage_snapuserd(true);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
ASSERT_TRUE(init->PerformSecondStageTransition());
// The control device should have been renamed.
ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
}
TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
AddOperationForPartitions();
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
ASSERT_TRUE(WriteSnapshotAndHash(name));
}
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(sm->MapAllSnapshots(10s));
// Read bytes back and verify they match the cache.
ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
}
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:

View file

@ -51,6 +51,25 @@ bool EnsureSnapuserdStarted() {
return true;
}
pid_t StartFirstStageSnapuserd() {
pid_t pid = fork();
if (pid < 0) {
PLOG(ERROR) << "fork failed";
return pid;
}
if (pid != 0) {
return pid;
}
std::string arg0 = "/system/bin/snapuserd";
std::string arg1 = kSnapuserdSocketFirstStage;
char* const argv[] = {arg0.data(), arg1.data(), nullptr};
if (execv(arg0.c_str(), argv) < 0) {
PLOG(FATAL) << "execv failed";
}
return pid;
}
SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
static inline bool IsRetryErrno() {

View file

@ -191,6 +191,8 @@ bool SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::strin
}
void SnapuserdServer::RunThread(DmUserHandler* handler) {
LOG(INFO) << "Entering thread for handler: " << handler->GetControlDevice();
while (!StopRequested()) {
if (handler->snapuserd()->Run() < 0) {
LOG(INFO) << "Snapuserd: Thread terminating as control device is de-registered";
@ -198,6 +200,8 @@ void SnapuserdServer::RunThread(DmUserHandler* handler) {
}
}
LOG(INFO) << "Exiting thread for handler: " << handler->GetControlDevice();
if (auto client = RemoveHandler(handler->GetControlDevice())) {
// The main thread did not receive a WaitForDelete request for this
// control device. Since we transferred ownership within the lock,

View file

@ -91,7 +91,7 @@ AutoDeleteSnapshot::~AutoDeleteSnapshot() {
}
}
Return InitializeCow(const std::string& device) {
Return InitializeKernelCow(const std::string& device) {
// When the kernel creates a persistent dm-snapshot, it requires a CoW file
// to store the modifications. The kernel interface does not specify how
// the CoW is used, and there is no standard associated.

View file

@ -112,7 +112,7 @@ std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
// Initialize a device before using it as the COW device for a dm-snapshot device.
Return InitializeCow(const std::string& device);
Return InitializeKernelCow(const std::string& device);
// "Atomically" write string to file. This is done by a series of actions:
// 1. Write to path + ".tmp"

View file

@ -53,6 +53,7 @@
#include <keyutils.h>
#include <libavb/libavb.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
#include <selinux/android.h>
@ -94,6 +95,7 @@ using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
using android::fs_mgr::AvbHandle;
using android::snapshot::SnapshotManager;
namespace android {
namespace init {
@ -722,6 +724,32 @@ void SendLoadPersistentPropertiesMessage() {
}
}
static Result<void> TransitionSnapuserdAction(const BuiltinArguments&) {
if (!SnapshotManager::IsSnapshotManagerNeeded() ||
!android::base::GetBoolProperty(android::snapshot::kVirtualAbCompressionProp, false)) {
return {};
}
auto sm = SnapshotManager::New();
if (!sm) {
LOG(FATAL) << "Failed to create SnapshotManager, will not transition snapuserd";
return {};
}
ServiceList& service_list = ServiceList::GetInstance();
auto svc = service_list.FindService("snapuserd");
if (!svc) {
LOG(FATAL) << "Failed to find snapuserd service, aborting transition";
return {};
}
svc->Start();
if (!sm->PerformSecondStageTransition()) {
LOG(FATAL) << "Failed to transition snapuserd to second-stage";
}
return {};
}
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@ -847,6 +875,7 @@ int SecondStageMain(int argc, char** argv) {
SetProperty(gsi::kGsiInstalledProp, is_installed);
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(TransitionSnapuserdAction, "TransitionSnapuserd");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueEventTrigger("early-init");