blob: f7608dc5f0bf6348ca73654b798b006a81f99746 [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 <chrono>
#include <memory>
#include <string>
#include <vector>
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.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 android {
namespace fiemap {
class IImageManager;
} // namespace fiemap
namespace snapshot {
enum class UpdateState : unsigned int {
// No update or merge is in progress.
None,
// An update is applying; snapshots may already exist.
Initiated,
// An update is pending, but has not been successfully booted yet.
Unverified,
// The kernel is merging in the background.
Merging,
// Post-merge cleanup steps could not be completed due to a transient
// error, but the next reboot will finish any pending operations.
MergeNeedsReboot,
// Merging is complete, and needs to be acknowledged.
MergeCompleted,
// Merging failed due to an unrecoverable error.
MergeFailed
};
class SnapshotManager final {
public:
// Dependency injection for testing.
class IDeviceInfo {
public:
virtual ~IDeviceInfo() {}
virtual std::string GetGsidDir() const = 0;
virtual std::string GetMetadataDir() const = 0;
// Return true if the device is currently running off snapshot devices,
// indicating that we have booted after applying (but not merging) an
// OTA.
virtual bool IsRunningSnapshot() const = 0;
};
~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);
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
bool BeginUpdate();
// Cancel an update; any snapshots will be deleted. This will fail if the
// state != Initiated or None.
bool CancelUpdate();
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
bool InitiateMerge();
// Wait for the current merge to finish, then perform cleanup when it
// completes. It is necessary to call this after InitiateMerge(), or when
// a merge state is detected during boot.
//
// Note that after calling WaitForMerge(), 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.
UpdateState WaitForMerge();
// 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
UpdateState GetUpdateState(double* progress = nullptr);
private:
FRIEND_TEST(SnapshotTest, CreateSnapshot);
FRIEND_TEST(SnapshotTest, MapSnapshot);
FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, Merge);
FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
friend class SnapshotTest;
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 connects via binder.
bool EnsureImageManager();
// Helper function for tests.
IImageManager* image_manager() const { return images_.get(); }
// 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();
const std::string& path() const { return path_; }
int fd() const { return fd_; }
int lock_mode() const { return lock_mode_; }
private:
std::string path_;
android::base::unique_fd fd_;
int lock_mode_;
};
std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
bool Truncate(LockedFile* file);
// 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().
//
// |device_size| should be the size of the base_device that will be passed
// via MapDevice(). |snapshot_size| should be the number of bytes in the
// base device, starting from 0, that will be snapshotted. The cow_size
// should be the amount of space that will be allocated to store snapshot
// deltas.
//
// If |snapshot_size| < 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 and snapshot sizes
// must be a multiple of the sector size (512 bytes). |cow_size| will
// be rounded up to the nearest sector.
bool CreateSnapshot(LockedFile* lock, const std::string& name, uint64_t device_size,
uint64_t snapshot_size, uint64_t cow_size);
// 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::chrono::milliseconds& timeout_ms, std::string* dev_path);
// Remove the backing copy-on-write image 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 and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
// List the known snapshot names.
bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
// Interact with /metadata/ota/state.
std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
std::unique_ptr<LockedFile> LockShared();
std::unique_ptr<LockedFile> LockExclusive();
UpdateState ReadUpdateState(LockedFile* file);
bool WriteUpdateState(LockedFile* file, UpdateState state);
std::string GetStateFilePath() const;
// This state is persisted per-snapshot in /metadata/ota/snapshots/.
struct SnapshotStatus {
std::string state;
uint64_t device_size;
uint64_t snapshot_size;
// These are non-zero when merging.
uint64_t sectors_allocated = 0;
uint64_t metadata_sectors = 0;
};
// Helpers for merging.
bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
bool RewriteSnapshotDeviceTable(const std::string& dm_name);
bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
void AcknowledgeMergeSuccess(LockedFile* lock);
void AcknowledgeMergeFailure();
// 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(const std::string& name, const SnapshotStatus& status);
// Only the following UpdateStates are used here:
// UpdateState::Merging
// UpdateState::MergeCompleted
// UpdateState::MergeFailed
// UpdateState::MergeNeedsReboot
UpdateState CheckMergeState();
UpdateState CheckMergeState(LockedFile* lock);
UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
// Interact with status files under /metadata/ota/snapshots.
bool WriteSnapshotStatus(LockedFile* lock, const std::string& name,
const SnapshotStatus& status);
bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
std::string GetSnapshotStatusFilePath(const std::string& name);
// Return the name of the device holding the "snapshot" or "snapshot-merge"
// target. This may not be the final device presented via MapSnapshot(), if
// for example there is a linear segment.
std::string GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status);
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
};
} // namespace snapshot
} // namespace android
#ifdef DEFINED_FRIEND_TEST
#undef DEFINED_FRIEND_TEST
#undef FRIEND_TEST
#endif