blob: e7b0020ead86a84671ce9667e6cab7191bbad06f [file] [log] [blame]
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <unistd.h>
#include <chrono>
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <string_view>
#include <vector>
#include <android-base/unique_fd.h>
#include <android/snapshot/snapshot.pb.h>
#include <fs_mgr_dm_linear.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/cow_writer.h>
#include <libsnapshot/return.h>
#include <snapuserd/snapuserd_client.h>
#include <update_engine/update_metadata.pb.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
friend class test_set_name##_##individual_test##_Test
#define DEFINED_FRIEND_TEST
#endif
namespace aidl::android::hardware::boot {
enum class MergeStatus;
}
namespace android {
namespace fiemap {
class IImageManager;
} // namespace fiemap
namespace fs_mgr {
struct CreateLogicalPartitionParams;
class IPartitionOpener;
} // namespace fs_mgr
// Forward declare IBootControl types since we cannot include only the headers
// with Soong. Note: keep the enum width in sync.
namespace snapshot {
struct AutoDeleteCowImage;
struct AutoDeleteSnapshot;
struct AutoDeviceList;
struct PartitionCowCreator;
class ISnapshotMergeStats;
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";
bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,
chromeos_update_engine::InstallOperation* optimized);
enum class CreateResult : unsigned int {
ERROR,
CREATED,
NOT_CREATED,
};
class ISnapshotManager {
public:
// Dependency injection for testing.
class IDeviceInfo {
public:
using IImageManager = android::fiemap::IImageManager;
using MergeStatus = aidl::android::hardware::boot::MergeStatus;
virtual ~IDeviceInfo() {}
virtual std::string GetMetadataDir() const = 0;
virtual std::string GetSlotSuffix() const = 0;
virtual std::string GetOtherSlotSuffix() const = 0;
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; }
virtual bool IsFirstStageInit() const = 0;
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
// Helper method for implementing OpenImageManager.
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
};
virtual ~ISnapshotManager() = default;
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
virtual bool BeginUpdate() = 0;
// Cancel an update; any snapshots will be deleted. This is allowed if the
// state == Initiated, None, or Unverified (before rebooting to the new
// slot).
virtual bool CancelUpdate() = 0;
// Mark snapshot writes as having completed. After this, new snapshots cannot
// be created, and the device must either cancel the OTA (either before
// rebooting or after rolling back), or merge the OTA.
// Before calling this function, all snapshots must be mapped.
// If |wipe| is set to true, wipe is scheduled after reboot, and snapshots
// may need to be merged before wiping.
virtual bool FinishedSnapshotWrites(bool wipe) = 0;
// Set feature flags on an ISnapshotMergeStats object.
virtual void SetMergeStatsFeatures(ISnapshotMergeStats* stats) = 0;
// Update an ISnapshotMergeStats object with statistics about COW usage.
// This should be called before the merge begins as otherwise snapshots
// may be deleted.
virtual void UpdateCowStats(ISnapshotMergeStats* stats) = 0;
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
virtual bool InitiateMerge() = 0;
// Perform any necessary post-boot actions. This should be run soon after
// /data is mounted.
//
// If a merge is in progress, this function will block until the merge is
// completed.
// - Callback is called periodically during the merge. If callback()
// returns false during the merge, ProcessUpdateState() will pause
// and returns Merging.
// If a merge or update was cancelled, this will clean up any
// update artifacts and return.
//
// Note that after calling this, GetUpdateState() may still return that a
// merge is in progress:
// MergeFailed indicates that a fatal error occurred. WaitForMerge() may
// called any number of times again to attempt to make more progress, but
// we do not expect it to succeed if a catastrophic error occurred.
//
// MergeNeedsReboot indicates that the merge has completed, but cleanup
// failed. This can happen if for some reason resources were not closed
// properly. In this case another reboot is needed before we can take
// another OTA. However, WaitForMerge() can be called again without
// rebooting, to attempt to finish cleanup anyway.
//
// MergeCompleted indicates that the update has fully completed.
// GetUpdateState will return None, and a new update can begin.
//
// The optional callback allows the caller to periodically check the
// progress with GetUpdateState().
virtual UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
const std::function<bool()>& before_cancel = {}) = 0;
// If ProcessUpdateState() returned MergeFailed, this returns the appropriate
// code. Otherwise, MergeFailureCode::Ok is returned.
virtual MergeFailureCode ReadMergeFailureCode() = 0;
// If an update is in progress, return the source build fingerprint.
virtual std::string ReadSourceBuildFingerprint() = 0;
// Find the status of the current update, if any.
//
// |progress| depends on the returned status:
// Merging: Value in the range [0, 100]
// MergeCompleted: 100
// Other: 0
virtual UpdateState GetUpdateState(double* progress = nullptr) = 0;
// Returns true if compression is enabled for the current update. This always returns false if
// UpdateState is None, or no snapshots have been created.
virtual bool UpdateUsesCompression() = 0;
// Returns true if userspace snapshots is enabled for the current update.
virtual bool UpdateUsesUserSnapshots() = 0;
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
// "write-protected" and snapshotted.
virtual Return CreateUpdateSnapshots(
const chromeos_update_engine::DeltaArchiveManifest& manifest) = 0;
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
//
// |snapshot_path| must not be nullptr.
//
// This method will return false if ro.virtual_ab.compression.enabled is true.
virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) = 0;
// Create an ICowWriter to build a snapshot against a target partition. The partition name
// must be suffixed. If a source partition exists, it must be specified as well. The source
// partition will only be used if raw bytes are needed. The source partition should be an
// absolute path to the device, not a partition name.
virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
std::optional<uint64_t> label = {}) = 0;
// Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
// OpenSnapshotWriter. All outstanding open descriptors, writers, or
// readers must be deleted before this is called.
virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
// If this returns true, first-stage mount must call
// CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
virtual bool NeedSnapshotsInFirstStageMount() = 0;
// Perform first-stage mapping of snapshot targets. This replaces init's
// call to CreateLogicalPartitions when snapshots are present.
virtual bool CreateLogicalAndSnapshotPartitions(
const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;
// Map all snapshots. This is analogous to CreateLogicalAndSnapshotPartitions, except it maps
// the target slot rather than the current slot. It should only be used immediately after
// applying an update, before rebooting to the new slot.
virtual bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) = 0;
// Unmap all snapshots. This should be called to undo MapAllSnapshots().
virtual bool UnmapAllSnapshots() = 0;
// This method should be called preceding any wipe or flash of metadata or
// userdata. It is only valid in recovery or fastbootd, and it ensures that
// a merge has been completed.
//
// When userdata will be wiped or flashed, it is necessary to clean up any
// snapshot state. If a merge is in progress, the merge must be finished.
// If a snapshot is present but not yet merged, the slot must be marked as
// unbootable.
//
// Returns true on success (or nothing to do), false on failure. The
// optional callback fires periodically to query progress via GetUpdateState.
virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
// Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
// but does not expect a data wipe after.
virtual bool FinishMergeInRecovery() = 0;
// This method is only allowed in recovery and is used as a helper to
// initialize the snapshot devices as a requirement to mount a snapshotted
// /system in recovery.
// This function returns:
// - CreateResult::CREATED if snapshot devices were successfully created;
// - CreateResult::NOT_CREATED if it was not necessary to create snapshot
// devices;
// - CreateResult::ERROR if a fatal error occurred, mounting /system should
// be aborted.
// This function mounts /metadata when called, and unmounts /metadata upon
// return.
virtual CreateResult RecoveryCreateSnapshotDevices() = 0;
// Same as RecoveryCreateSnapshotDevices(), but does not auto mount/umount
// /metadata.
virtual CreateResult RecoveryCreateSnapshotDevices(
const std::unique_ptr<AutoDevice>& metadata_device) = 0;
// Dump debug information.
virtual bool Dump(std::ostream& os) = 0;
// Ensure metadata directory is mounted in recovery. When the returned
// AutoDevice is destroyed, the metadata directory is automatically
// unmounted.
// Return nullptr if any failure.
// In Android mode, Return an AutoDevice that does nothing
// In recovery, return an AutoDevice that does nothing if metadata entry
// is not found in fstab.
// Note: if this function is called the second time before the AutoDevice returned from the
// first call is destroyed, the device will be unmounted when any of these AutoDevices is
// destroyed. For example:
// auto a = mgr->EnsureMetadataMounted(); // mounts
// auto b = mgr->EnsureMetadataMounted(); // does nothing
// b.reset() // unmounts
// a.reset() // does nothing
virtual std::unique_ptr<AutoDevice> EnsureMetadataMounted() = 0;
// Return the associated ISnapshotMergeStats instance. Never null.
virtual ISnapshotMergeStats* GetSnapshotMergeStatsInstance() = 0;
};
class SnapshotManager final : public ISnapshotManager {
using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
using IPartitionOpener = android::fs_mgr::IPartitionOpener;
using LpMetadata = android::fs_mgr::LpMetadata;
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
using MergeStatus = aidl::android::hardware::boot::MergeStatus;
using FiemapStatus = android::fiemap::FiemapStatus;
friend class SnapshotMergeStats;
public:
~SnapshotManager();
// Return a new SnapshotManager instance, or null on error. The device
// pointer is owned for the lifetime of SnapshotManager. If null, a default
// instance will be created.
static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
// This is similar to New(), except designed specifically for first-stage
// init or recovery.
static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
// Helper function for first-stage init to check whether a SnapshotManager
// might be needed to perform first-stage mounts.
static bool IsSnapshotManagerNeeded();
// Helper function for second stage init to restorecon on the rollback indicator.
static std::string GetGlobalRollbackIndicatorPath();
// Populate |snapuserd_argv| with the necessary arguments to restart snapuserd
// after loading selinux policy.
bool PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv);
// Detach dm-user devices from the first stage snapuserd. Load
// new dm-user tables after loading selinux policy.
bool DetachFirstStageSnapuserdForSelinux();
// Perform the transition from the selinux stage of snapuserd into the
// second-stage of 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.
bool PerformSecondStageInitTransition();
// ISnapshotManager overrides.
bool BeginUpdate() override;
bool CancelUpdate() override;
bool FinishedSnapshotWrites(bool wipe) override;
void UpdateCowStats(ISnapshotMergeStats* stats) override;
MergeFailureCode ReadMergeFailureCode() override;
bool InitiateMerge() override;
UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
const std::function<bool()>& before_cancel = {}) override;
UpdateState GetUpdateState(double* progress = nullptr) override;
bool UpdateUsesCompression() override;
bool UpdateUsesUserSnapshots() override;
Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
std::unique_ptr<ICowWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
std::optional<uint64_t> label) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
bool CreateLogicalAndSnapshotPartitions(
const std::string& super_device,
const std::chrono::milliseconds& timeout_ms = {}) override;
bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
bool FinishMergeInRecovery() override;
CreateResult RecoveryCreateSnapshotDevices() override;
CreateResult RecoveryCreateSnapshotDevices(
const std::unique_ptr<AutoDevice>& metadata_device) override;
bool Dump(std::ostream& os) override;
std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
bool UnmapAllSnapshots() override;
std::string ReadSourceBuildFingerprint() override;
void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
// We can't use WaitForFile during first-stage init, because ueventd is not
// running and therefore will not automatically create symlinks. Instead,
// we let init provide us with the correct function to use to ensure
// uevents have been processed and symlink/mknod calls completed.
void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
uevent_regen_callback_ = callback;
}
// If true, compression is enabled for this update. This is used by
// first-stage to decide whether to launch snapuserd.
bool IsSnapuserdRequired();
// This is primarily used to device reboot. If OTA update is in progress,
// init will avoid killing processes
bool IsUserspaceSnapshotUpdateInProgress();
enum class SnapshotDriver {
DM_SNAPSHOT,
DM_USER,
};
// Add new public entries above this line.
// Helpers for failure injection.
using MergeConsistencyChecker =
std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>;
void set_merge_consistency_checker(MergeConsistencyChecker checker) {
merge_consistency_checker_ = checker;
}
MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; }
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);
FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
FRIEND_TEST(SnapshotTest, MapSnapshot);
FRIEND_TEST(SnapshotTest, Merge);
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);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
FRIEND_TEST(SnapshotUpdateTest, DataWipeWithStaleSnapshots);
FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
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;
using DmTargetSnapshot = android::dm::DmTargetSnapshot;
using IImageManager = android::fiemap::IImageManager;
using TargetInfo = android::dm::DeviceMapper::TargetInfo;
explicit SnapshotManager(IDeviceInfo* info);
// This is created lazily since it can connect via binder.
bool EnsureImageManager();
// Ensure we're connected to snapuserd.
bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
// Helpers for first-stage init.
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
// 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
// this. It also serves as a proof-of-lock for some functions.
class LockedFile final {
public:
LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode)
: path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {}
~LockedFile();
int lock_mode() const { return lock_mode_; }
private:
std::string path_;
android::base::unique_fd fd_;
int lock_mode_;
};
static std::unique_ptr<LockedFile> OpenFile(const std::string& file, int lock_flags);
SnapshotDriver GetSnapshotDriver(LockedFile* lock);
// Create a new snapshot record. This creates the backing COW store and
// persists information needed to map the device. The device can be mapped
// with MapSnapshot().
//
// |status|.device_size should be the size of the base_device that will be passed
// via MapDevice(). |status|.snapshot_size should be the number of bytes in the
// base device, starting from 0, that will be snapshotted. |status|.cow_file_size
// should be the amount of space that will be allocated to store snapshot
// deltas.
//
// If |status|.snapshot_size < |status|.device_size, then the device will always
// be mapped with two table entries: a dm-snapshot range covering
// snapshot_size, and a dm-linear range covering the remainder.
//
// All sizes are specified in bytes, and the device, snapshot, COW partition and COW file sizes
// must be a multiple of the sector size (512 bytes).
bool CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_creator, SnapshotStatus* status);
// |name| should be the base partition name (e.g. "system_a"). Create the
// backing COW image using the size previously passed to CreateSnapshot().
Return CreateCowImage(LockedFile* lock, const std::string& name);
// Map a snapshot device that was previously created with CreateSnapshot.
// If a merge was previously initiated, the device-mapper table will have a
// snapshot-merge target instead of a snapshot target. If the timeout
// parameter greater than zero, this function will wait the given amount
// of time for |dev_path| to become available, and fail otherwise. If
// timeout_ms is 0, then no wait will occur and |dev_path| may not yet
// exist on return.
bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,
const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
std::string* dev_path);
// Create a dm-user device for a given snapshot.
bool MapDmUserCow(LockedFile* lock, const std::string& name, const std::string& cow_file,
const std::string& base_device, const std::string& base_path_merge,
const std::chrono::milliseconds& timeout_ms, std::string* path);
// Map the source device used for dm-user.
bool MapSourceDevice(LockedFile* lock, const std::string& name,
const std::chrono::milliseconds& timeout_ms, std::string* path);
// Map a COW image that was previous created with CreateCowImage.
std::optional<std::string> MapCowImage(const std::string& name,
const std::chrono::milliseconds& timeout_ms);
// Remove the backing copy-on-write image and snapshot states for the named snapshot. The
// caller is responsible for ensuring that the snapshot is unmapped.
bool DeleteSnapshot(LockedFile* lock, const std::string& name);
// Unmap a snapshot device previously mapped with MapSnapshotDevice().
bool UnmapSnapshot(LockedFile* lock, const std::string& name);
// Unmap a COW image device previously mapped with MapCowImage().
bool UnmapCowImage(const std::string& name);
// Unmap a COW and remove it from a MetadataBuilder.
void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);
// Remove invalid snapshots if any
void RemoveInvalidSnapshots(LockedFile* lock);
// 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 = "");
// Check for a cancelled or rolled back merge, returning true if such a
// condition was detected and handled.
bool HandleCancelledUpdate(LockedFile* lock, const std::function<bool()>& before_cancel);
// Helper for HandleCancelledUpdate. Assumes booting from new slot.
bool AreAllSnapshotsCancelled(LockedFile* lock);
// Determine whether partition names in |snapshots| have been flashed and
// store result to |out|.
// Return true if values are successfully retrieved and false on error
// (e.g. super partition metadata cannot be read). When it returns true,
// |out| stores true for partitions that have been flashed and false for
// partitions that have not been flashed.
bool GetSnapshotFlashingStatus(LockedFile* lock, const std::vector<std::string>& snapshots,
std::map<std::string, bool>* out);
// Remove artifacts created by the update process, such as snapshots, and
// set the update state to None.
bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {});
// Interact with /metadata/ota.
std::unique_ptr<LockedFile> OpenLock(int lock_flags);
std::unique_ptr<LockedFile> LockShared();
std::unique_ptr<LockedFile> LockExclusive();
std::string GetLockPath() const;
// Interact with /metadata/ota/state.
UpdateState ReadUpdateState(LockedFile* file);
SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);
bool WriteUpdateState(LockedFile* file, UpdateState state,
MergeFailureCode failure_code = MergeFailureCode::Ok);
bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
std::string GetStateFilePath() const;
// Interact with /metadata/ota/merge_state.
// This file contains information related to the snapshot merge process.
std::string GetMergeStateFilePath() const;
// Helpers for merging.
MergeFailureCode MergeSecondPhaseSnapshots(LockedFile* lock);
MergeFailureCode SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
MergeFailureCode RewriteSnapshotDeviceTable(const std::string& dm_name);
bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
void AcknowledgeMergeSuccess(LockedFile* lock);
void AcknowledgeMergeFailure(MergeFailureCode failure_code);
MergePhase DecideMergePhase(const SnapshotStatus& status);
std::unique_ptr<LpMetadata> ReadCurrentMetadata();
enum class MetadataPartitionState {
// Partition does not exist.
None,
// Partition is flashed.
Flashed,
// Partition is created by OTA client.
Updated,
};
// Helper function to check the state of a partition as described in metadata.
MetadataPartitionState GetMetadataPartitionState(const LpMetadata& metadata,
const std::string& name);
// Note that these require the name of the device containing the snapshot,
// which may be the "inner" device. Use GetsnapshotDeviecName().
bool QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
DmTargetSnapshot::Status* status);
bool IsSnapshotDevice(const std::string& dm_name, TargetInfo* target = nullptr);
// Internal callback for when merging is complete.
bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
const SnapshotStatus& status);
bool CollapseSnapshotDevice(LockedFile* lock, const std::string& name,
const SnapshotStatus& status);
struct [[nodiscard]] MergeResult {
explicit MergeResult(UpdateState state,
MergeFailureCode failure_code = MergeFailureCode::Ok)
: state(state), failure_code(failure_code) {}
UpdateState state;
MergeFailureCode failure_code;
};
// Only the following UpdateStates are used here:
// UpdateState::Merging
// UpdateState::MergeCompleted
// UpdateState::MergeFailed
// UpdateState::MergeNeedsReboot
MergeResult CheckMergeState(const std::function<bool()>& before_cancel);
MergeResult CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
MergeResult CheckTargetMergeState(LockedFile* lock, const std::string& name,
const SnapshotUpdateStatus& update_status);
MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
const SnapshotStatus& update_status);
auto UpdateStateToStr(enum UpdateState state);
// Get status or table information about a device-mapper node with a single target.
enum class TableQuery {
Table,
Status,
};
bool GetSingleTarget(const std::string& dm_name, TableQuery query,
android::dm::DeviceMapper::TargetInfo* target);
// Interact with status files under /metadata/ota/snapshots.
bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
std::string GetSnapshotStatusFilePath(const std::string& name);
std::string GetSnapshotBootIndicatorPath();
std::string GetRollbackIndicatorPath();
std::string GetForwardMergeIndicatorPath();
std::string GetOldPartitionMetadataPath();
std::string GetBootSnapshotsWithoutSlotSwitchPath();
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
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).
Update,
// For mounting a full readable device.
Mount,
};
struct SnapshotPaths {
// Target/base device (eg system_b), always present.
std::string target_device;
// COW name (eg system_cow). Not present if no COW is needed.
std::string cow_device_name;
// dm-snapshot instance. Not present in Update mode for VABC.
std::string snapshot_device;
};
// Helpers for OpenSnapshotWriter.
std::unique_ptr<ICowWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
const SnapshotStatus& status,
const SnapshotPaths& paths,
std::optional<uint64_t> label);
// Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
SnapshotContext context, SnapshotPaths* paths);
// Map the COW devices, including the partition in super and the images.
// |params|:
// - |partition_name| should be the name of the top-level partition (e.g. system_b),
// not system_b-cow-img
// - |device_name| and |partition| is ignored
// - |timeout_ms| and the rest is respected
// Return the path in |cow_device_path| (e.g. /dev/block/dm-1) and major:minor in
// |cow_device_string|
bool MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,
const SnapshotStatus& snapshot_status, AutoDeviceList* created_devices,
std::string* cow_name);
// The reverse of MapCowDevices.
bool UnmapCowDevices(LockedFile* lock, const std::string& name);
// The reverse of MapPartitionWithSnapshot.
bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
// Unmap a dm-user device through snapuserd.
bool UnmapDmUserDevice(const std::string& dm_user_name);
// Unmap a dm-user device for user space snapshots
bool UnmapUserspaceSnapshotDevice(LockedFile* lock, const std::string& snapshot_name);
// If there isn't a previous update, return true. |needs_merge| is set to false.
// If there is a previous update but the device has not boot into it, tries to cancel the
// update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
// If there is a previous update and the device has boot into it, do nothing and return true.
// |needs_merge| is set to true.
bool TryCancelUpdate(bool* needs_merge);
// Helper for CreateUpdateSnapshots.
// Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
Return CreateUpdateSnapshotsInternal(
LockedFile* lock, const DeltaArchiveManifest& manifest,
PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,
std::map<std::string, SnapshotStatus>* all_snapshot_status);
// Initialize snapshots so that they can be mapped later.
// Map the COW partition and zero-initialize the header.
Return InitializeUpdateSnapshots(
LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
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 UnmapAllPartitionsInRecovery();
// Check no snapshot overflows. Note that this returns false negatives if the snapshot
// overflows, then is remapped and not written afterwards.
bool EnsureNoOverflowSnapshot(LockedFile* lock);
enum class Slot { Unknown, Source, Target };
friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);
Slot GetCurrentSlot();
// Return the suffix we expect snapshots to have.
std::string GetSnapshotSlotSuffix();
std::string ReadUpdateSourceSlotSuffix();
// Helper for RemoveAllSnapshots.
// Check whether |name| should be deleted as a snapshot name.
bool ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status, Slot current_slot,
const std::string& name);
// Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
// allow forward merge on FDR.
bool UpdateForwardMergeIndicator(bool wipe);
// Helper for HandleImminentDataWipe.
// Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
// if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
// necessary.
UpdateState ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
const std::function<bool()>& callback);
// Return device string of a mapped image, or if it is not available, the mapped image path.
bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
std::string* device_string_or_mapped_path);
// Same as above, but for paths only (no major:minor device strings).
bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
// Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
// This is needed for any code that uses device-mapper path in first-stage init. If
// |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
// returns true.
bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
enum class InitTransition { SELINUX_DETACH, SECOND_STAGE };
// 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.
//
// If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will
// be populated with a list of snapuserd arguments to pass to execve(). It
// is otherwise ignored.
bool PerformInitTransition(InitTransition transition,
std::vector<std::string>* snapuserd_argv = nullptr);
SnapuserdClient* snapuserd_client() const { return snapuserd_client_.get(); }
// Helper of UpdateUsesCompression
bool UpdateUsesCompression(LockedFile* lock);
// Locked and unlocked functions to test whether the current update uses
// userspace snapshots.
bool UpdateUsesUserSnapshots(LockedFile* lock);
// Check if io_uring API's need to be used
bool UpdateUsesIouring(LockedFile* lock);
// Wrapper around libdm, with diagnostics.
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
android::dm::IDeviceMapper& dm_;
std::unique_ptr<IDeviceInfo> device_;
std::string metadata_dir_;
std::unique_ptr<IImageManager> images_;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
std::function<bool(const std::string&)> uevent_regen_callback_;
std::unique_ptr<SnapuserdClient> snapuserd_client_;
std::unique_ptr<LpMetadata> old_partition_metadata_;
std::optional<bool> is_snapshot_userspace_;
MergeConsistencyChecker merge_consistency_checker_;
};
} // namespace snapshot
} // namespace android
#ifdef DEFINED_FRIEND_TEST
#undef DEFINED_FRIEND_TEST
#undef FRIEND_TEST
#endif