Merge "libsnapshot: Partially implement OpenSnapshotWriter." am: 15f97700c2
am: b7b2d1255d
am: 8b171bc688
am: c0d48d2041
Original change: https://android-review.googlesource.com/c/platform/system/core/+/1433573 Change-Id: I5d7babec6462f596a0570b28b3f5bcf9e5434ce1
This commit is contained in:
commit
ef11411af7
16 changed files with 385 additions and 82 deletions
|
@ -147,6 +147,7 @@ cc_binary {
|
|||
static_libs: [
|
||||
"libgtest_prod",
|
||||
"libhealthhalutils",
|
||||
"libsnapshot_cow",
|
||||
"libsnapshot_nobinder",
|
||||
"update_metadata-protos",
|
||||
],
|
||||
|
|
|
@ -77,6 +77,7 @@ filegroup {
|
|||
"snapshot_stats.cpp",
|
||||
"snapshot_stub.cpp",
|
||||
"snapshot_metadata_updater.cpp",
|
||||
"snapshot_writer.cpp",
|
||||
"partition_cow_creator.cpp",
|
||||
"return.cpp",
|
||||
"utility.cpp",
|
||||
|
@ -215,6 +216,7 @@ cc_defaults {
|
|||
"libgmock",
|
||||
"liblp",
|
||||
"libsnapshot",
|
||||
"libsnapshot_cow",
|
||||
"libsnapshot_test_helpers",
|
||||
"libsparse",
|
||||
],
|
||||
|
@ -249,6 +251,7 @@ cc_binary {
|
|||
static_libs: [
|
||||
"libfstab",
|
||||
"libsnapshot",
|
||||
"libsnapshot_cow",
|
||||
"update_metadata-protos",
|
||||
],
|
||||
shared_libs: [
|
||||
|
@ -312,6 +315,7 @@ cc_defaults {
|
|||
"libgmock", // from libsnapshot_test_helpers
|
||||
"liblog",
|
||||
"liblp",
|
||||
"libsnapshot_cow",
|
||||
"libsnapshot_test_helpers",
|
||||
"libprotobuf-mutator",
|
||||
],
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace snapshot {
|
|||
|
||||
static_assert(sizeof(off_t) == sizeof(uint64_t));
|
||||
|
||||
using android::base::borrowed_fd;
|
||||
using android::base::unique_fd;
|
||||
|
||||
bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
|
||||
if (!ValidateNewBlock(new_block)) {
|
||||
return false;
|
||||
|
@ -98,12 +101,12 @@ bool CowWriter::ParseOptions() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CowWriter::Initialize(android::base::unique_fd&& fd, OpenMode mode) {
|
||||
bool CowWriter::Initialize(unique_fd&& fd, OpenMode mode) {
|
||||
owned_fd_ = std::move(fd);
|
||||
return Initialize(android::base::borrowed_fd{owned_fd_}, mode);
|
||||
return Initialize(borrowed_fd{owned_fd_}, mode);
|
||||
}
|
||||
|
||||
bool CowWriter::Initialize(android::base::borrowed_fd fd, OpenMode mode) {
|
||||
bool CowWriter::Initialize(borrowed_fd fd, OpenMode mode) {
|
||||
fd_ = fd;
|
||||
|
||||
if (!ParseOptions()) {
|
||||
|
|
|
@ -58,6 +58,9 @@ class ICowWriter {
|
|||
// Return number of bytes the cow image occupies on disk.
|
||||
virtual uint64_t GetCowSize() = 0;
|
||||
|
||||
// Returns true if AddCopy() operations are supported.
|
||||
virtual bool SupportsCopyOperation() const { return true; }
|
||||
|
||||
const CowOptions& options() { return options_; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -38,9 +38,7 @@ class MockSnapshotManager : public ISnapshotManager {
|
|||
(const android::fs_mgr::CreateLogicalPartitionParams& params,
|
||||
std::string* snapshot_path),
|
||||
(override));
|
||||
MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
|
||||
(const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
|
||||
MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenSnapshotReader,
|
||||
MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
|
||||
(const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
|
||||
MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
|
||||
MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
#include <update_engine/update_metadata.pb.h>
|
||||
|
||||
#include <libsnapshot/auto_device.h>
|
||||
#include <libsnapshot/cow_writer.h>
|
||||
#include <libsnapshot/return.h>
|
||||
#include <libsnapshot/snapshot_writer.h>
|
||||
|
||||
#ifndef FRIEND_TEST
|
||||
#define FRIEND_TEST(test_set_name, individual_test) \
|
||||
|
@ -44,10 +44,6 @@
|
|||
#define DEFINED_FRIEND_TEST
|
||||
#endif
|
||||
|
||||
namespace chromeos_update_engine {
|
||||
class FileDescriptor;
|
||||
} // namespace chromeos_update_engine
|
||||
|
||||
namespace android {
|
||||
|
||||
namespace fiemap {
|
||||
|
@ -110,8 +106,6 @@ class ISnapshotManager {
|
|||
};
|
||||
virtual ~ISnapshotManager() = default;
|
||||
|
||||
using FileDescriptor = chromeos_update_engine::FileDescriptor;
|
||||
|
||||
// Begin an update. This must be called before creating any snapshots. It
|
||||
// will fail if GetUpdateState() != None.
|
||||
virtual bool BeginUpdate() = 0;
|
||||
|
@ -187,19 +181,14 @@ class ISnapshotManager {
|
|||
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.
|
||||
virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
|
||||
|
||||
// Open a snapshot for reading. A file-like interface is provided through the FileDescriptor.
|
||||
// In this mode, writes are not supported. The partition name must be suffixed.
|
||||
virtual std::unique_ptr<FileDescriptor> OpenSnapshotReader(
|
||||
// Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
|
||||
// must be suffixed.
|
||||
virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
|
||||
|
||||
// Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
|
||||
// OpenSnapshotWriter, or OpenSnapshotReader. All outstanding open descriptors, writers,
|
||||
// or readers must be deleted before this is called.
|
||||
// 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
|
||||
|
@ -310,9 +299,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
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) override;
|
||||
std::unique_ptr<FileDescriptor> OpenSnapshotReader(
|
||||
std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
|
||||
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
|
||||
bool NeedSnapshotsInFirstStageMount() override;
|
||||
|
@ -532,9 +519,39 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
std::string GetSnapshotDeviceName(const std::string& snapshot_name,
|
||||
const SnapshotStatus& status);
|
||||
|
||||
// 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 path (eg system_cow). Not present if no COW is needed.
|
||||
std::string cow_device;
|
||||
|
||||
// dm-snapshot instance. Not present in Update mode for VABC.
|
||||
std::string snapshot_device;
|
||||
};
|
||||
|
||||
// Helpers for OpenSnapshotWriter.
|
||||
std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
|
||||
const std::string& partition_name,
|
||||
const SnapshotStatus& status,
|
||||
const SnapshotPaths& paths);
|
||||
std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(LockedFile* lock,
|
||||
const std::string& partition_name,
|
||||
const SnapshotStatus& status,
|
||||
const SnapshotPaths& paths);
|
||||
|
||||
// Map the base device, COW devices, and snapshot device.
|
||||
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
|
||||
std::string* path);
|
||||
SnapshotContext context, SnapshotPaths* paths);
|
||||
|
||||
// Map the COW devices, including the partition in super and the images.
|
||||
// |params|:
|
||||
|
|
|
@ -36,9 +36,7 @@ class SnapshotManagerStub : public ISnapshotManager {
|
|||
const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
|
||||
bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
|
||||
std::string* snapshot_path) override;
|
||||
std::unique_ptr<ICowWriter> OpenSnapshotWriter(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
|
||||
std::unique_ptr<FileDescriptor> OpenSnapshotReader(
|
||||
std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
|
||||
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
|
||||
bool NeedSnapshotsInFirstStageMount() override;
|
||||
|
|
68
fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
Normal file
68
fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright (C) 2020 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 <android-base/unique_fd.h>
|
||||
|
||||
#include <libsnapshot/cow_writer.h>
|
||||
|
||||
namespace chromeos_update_engine {
|
||||
class FileDescriptor;
|
||||
} // namespace chromeos_update_engine
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
class ISnapshotWriter : public ICowWriter {
|
||||
public:
|
||||
using FileDescriptor = chromeos_update_engine::FileDescriptor;
|
||||
|
||||
explicit ISnapshotWriter(const CowOptions& options);
|
||||
|
||||
// Set the source device. This is used for AddCopy() operations, if the
|
||||
// underlying writer needs the original bytes (for example if backed by
|
||||
// dm-snapshot or if writing directly to an unsnapshotted region).
|
||||
void SetSourceDevice(android::base::unique_fd&& source_fd);
|
||||
|
||||
virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
|
||||
|
||||
protected:
|
||||
android::base::unique_fd source_fd_;
|
||||
};
|
||||
|
||||
// Write directly to a dm-snapshot device.
|
||||
class OnlineKernelSnapshotWriter : public ISnapshotWriter {
|
||||
public:
|
||||
OnlineKernelSnapshotWriter(const CowOptions& options);
|
||||
|
||||
// Set the device used for all writes.
|
||||
void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
|
||||
|
||||
bool Flush() override;
|
||||
uint64_t GetCowSize() override { return cow_size_; }
|
||||
virtual std::unique_ptr<FileDescriptor> OpenReader() override;
|
||||
|
||||
protected:
|
||||
bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
|
||||
bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
|
||||
bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
|
||||
|
||||
private:
|
||||
android::base::unique_fd snapshot_fd_;
|
||||
uint64_t cow_size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
|
@ -144,6 +144,7 @@ void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::stri
|
|||
// Expect space of |path| is multiple of 4K.
|
||||
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
|
||||
std::string* hash = nullptr);
|
||||
bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
|
||||
|
||||
std::optional<std::string> GetHash(const std::string& path);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <libsnapshot/snapshot.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -1570,7 +1571,8 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(
|
|||
.timeout_ms = timeout_ms,
|
||||
};
|
||||
std::string ignore_path;
|
||||
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
|
||||
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
|
||||
nullptr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1598,11 +1600,10 @@ static std::chrono::milliseconds GetRemainingTime(
|
|||
|
||||
bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
||||
CreateLogicalPartitionParams params,
|
||||
std::string* path) {
|
||||
SnapshotContext context, SnapshotPaths* paths) {
|
||||
auto begin = std::chrono::steady_clock::now();
|
||||
|
||||
CHECK(lock);
|
||||
path->clear();
|
||||
|
||||
if (params.GetPartitionName() != params.GetDeviceName()) {
|
||||
LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
|
||||
|
@ -1683,8 +1684,11 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
|||
}
|
||||
created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
|
||||
|
||||
if (paths) {
|
||||
paths->target_device = base_path;
|
||||
}
|
||||
|
||||
if (!live_snapshot_status.has_value()) {
|
||||
*path = base_path;
|
||||
created_devices.Release();
|
||||
return true;
|
||||
}
|
||||
|
@ -1711,21 +1715,33 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
|||
LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
|
||||
return false;
|
||||
}
|
||||
if (paths) {
|
||||
paths->cow_device = cow_device;
|
||||
}
|
||||
|
||||
remaining_time = GetRemainingTime(params.timeout_ms, begin);
|
||||
if (remaining_time.count() < 0) return false;
|
||||
|
||||
if (context == SnapshotContext::Update && IsCompressionEnabled()) {
|
||||
// Stop here, we can't run dm-user yet, the COW isn't built.
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
|
||||
path)) {
|
||||
&path)) {
|
||||
LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
|
||||
return false;
|
||||
}
|
||||
// No need to add params.GetPartitionName() to created_devices since it is immediately released.
|
||||
|
||||
if (paths) {
|
||||
paths->snapshot_device = path;
|
||||
}
|
||||
|
||||
created_devices.Release();
|
||||
|
||||
LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
|
||||
|
||||
LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << path;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2438,23 +2454,86 @@ bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& para
|
|||
<< params.GetPartitionName();
|
||||
return false;
|
||||
}
|
||||
return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
|
||||
|
||||
SnapshotPaths paths;
|
||||
if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (paths.snapshot_device.empty()) {
|
||||
*snapshot_path = paths.snapshot_device;
|
||||
} else {
|
||||
*snapshot_path = paths.target_device;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
|
||||
std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) {
|
||||
(void)params;
|
||||
// First unmap any existing mapping.
|
||||
auto lock = LockShared();
|
||||
if (!lock) return nullptr;
|
||||
if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
|
||||
LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
|
||||
<< params.GetPartitionName();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "OpenSnapshotWriter not yet implemented";
|
||||
SnapshotPaths paths;
|
||||
if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SnapshotStatus status;
|
||||
if (!paths.cow_device.empty()) {
|
||||
if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// Currently, partition_cow_creator always creates snapshots. The
|
||||
// reason is that if partition X shrinks while partition Y grows, we
|
||||
// cannot bindly write to the newly freed extents in X. This would
|
||||
// make the old slot unusable. So, the entire size of the target
|
||||
// partition is currently considered snapshottable.
|
||||
LOG(ERROR) << "No snapshot available for partition " << params.GetPartitionName();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsCompressionEnabled()) {
|
||||
return OpenCompressedSnapshotWriter(lock.get(), params.GetPartitionName(), status, paths);
|
||||
} else {
|
||||
return OpenKernelSnapshotWriter(lock.get(), params.GetPartitionName(), status, paths);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
|
||||
LockedFile*, const std::string&, const SnapshotStatus&, const SnapshotPaths&) {
|
||||
LOG(ERROR) << "OpenSnapshotWriter not yet implemented for compression";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<FileDescriptor> SnapshotManager::OpenSnapshotReader(
|
||||
const android::fs_mgr::CreateLogicalPartitionParams& params) {
|
||||
(void)params;
|
||||
std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
|
||||
LockedFile* lock, [[maybe_unused]] const std::string& partition_name,
|
||||
const SnapshotStatus& status, const SnapshotPaths& paths) {
|
||||
CHECK(lock);
|
||||
|
||||
LOG(ERROR) << "OpenSnapshotReader not yet implemented";
|
||||
return nullptr;
|
||||
CowOptions cow_options;
|
||||
cow_options.max_blocks = {status.device_size() / cow_options.block_size};
|
||||
|
||||
auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
|
||||
|
||||
std::string_view path =
|
||||
paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
|
||||
unique_fd fd(open(path.data(), O_RDWR | O_CLOEXEC));
|
||||
if (fd < 0) {
|
||||
PLOG(ERROR) << "open failed: " << path;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
|
||||
writer->SetSnapshotDevice(std::move(fd), cow_size);
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
|
||||
|
|
|
@ -130,13 +130,7 @@ ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
|
|||
return &snapshot_merge_stats;
|
||||
}
|
||||
|
||||
std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
|
||||
const CreateLogicalPartitionParams&) {
|
||||
LOG(ERROR) << __FUNCTION__ << " should never be called.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<FileDescriptor> SnapshotManagerStub::OpenSnapshotReader(
|
||||
std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
|
||||
const CreateLogicalPartitionParams&) {
|
||||
LOG(ERROR) << __FUNCTION__ << " should never be called.";
|
||||
return nullptr;
|
||||
|
|
|
@ -80,6 +80,7 @@ TestDeviceInfo* test_device = nullptr;
|
|||
std::string fake_super;
|
||||
|
||||
void MountMetadata();
|
||||
bool IsCompressionEnabled();
|
||||
|
||||
class SnapshotTest : public ::testing::Test {
|
||||
public:
|
||||
|
@ -892,42 +893,39 @@ class SnapshotUpdateTest : public SnapshotTest {
|
|||
return AssertionSuccess();
|
||||
}
|
||||
|
||||
AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) {
|
||||
std::string real_path;
|
||||
if (!sm->MapUpdateSnapshot(
|
||||
CreateLogicalPartitionParams{
|
||||
.block_device = fake_super,
|
||||
.metadata_slot = 1,
|
||||
.partition_name = name,
|
||||
.timeout_ms = 10s,
|
||||
.partition_opener = opener_.get(),
|
||||
},
|
||||
&real_path)) {
|
||||
return AssertionFailure() << "Unable to map snapshot " << name;
|
||||
AssertionResult MapUpdateSnapshot(const std::string& name,
|
||||
std::unique_ptr<ICowWriter>* writer = nullptr) {
|
||||
CreateLogicalPartitionParams params{
|
||||
.block_device = fake_super,
|
||||
.metadata_slot = 1,
|
||||
.partition_name = name,
|
||||
.timeout_ms = 10s,
|
||||
.partition_opener = opener_.get(),
|
||||
};
|
||||
|
||||
auto result = sm->OpenSnapshotWriter(params);
|
||||
if (!result) {
|
||||
return AssertionFailure() << "Cannot open snapshot for writing: " << name;
|
||||
}
|
||||
if (path) {
|
||||
*path = real_path;
|
||||
|
||||
if (writer) {
|
||||
*writer = std::move(result);
|
||||
}
|
||||
return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
|
||||
return AssertionSuccess();
|
||||
}
|
||||
|
||||
AssertionResult WriteSnapshotAndHash(const std::string& name,
|
||||
std::optional<size_t> size = std::nullopt) {
|
||||
std::string path;
|
||||
auto res = MapUpdateSnapshot(name, &path);
|
||||
AssertionResult WriteSnapshotAndHash(const std::string& name) {
|
||||
std::unique_ptr<ICowWriter> writer;
|
||||
auto res = MapUpdateSnapshot(name, &writer);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
|
||||
|
||||
if (!WriteRandomData(path, size, &hashes_[name])) {
|
||||
return AssertionFailure() << "Unable to write " << size_string << " to " << path
|
||||
<< " for partition " << name;
|
||||
if (!WriteRandomData(writer.get(), &hashes_[name])) {
|
||||
return AssertionFailure() << "Unable to write random data to snapshot " << name;
|
||||
}
|
||||
|
||||
return AssertionSuccess() << "Written " << size_string << " to " << path
|
||||
<< " for snapshot partition " << name
|
||||
return AssertionSuccess() << "Written random data to snapshot " << name
|
||||
<< ", hash: " << hashes_[name];
|
||||
}
|
||||
|
||||
|
@ -1003,7 +1001,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
|
|||
|
||||
// Write some data to target partitions.
|
||||
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
|
||||
ASSERT_TRUE(WriteSnapshotAndHash(name, partition_size));
|
||||
ASSERT_TRUE(WriteSnapshotAndHash(name));
|
||||
}
|
||||
|
||||
// Assert that source partitions aren't affected.
|
||||
|
@ -1406,6 +1404,10 @@ void MountMetadata() {
|
|||
MetadataMountedTest().TearDown();
|
||||
}
|
||||
|
||||
bool IsCompressionEnabled() {
|
||||
return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
|
||||
}
|
||||
|
||||
TEST_F(MetadataMountedTest, Android) {
|
||||
auto device = sm->EnsureMetadataMounted();
|
||||
EXPECT_NE(nullptr, device);
|
||||
|
@ -1623,7 +1625,7 @@ TEST_F(SnapshotUpdateTest, Hashtree) {
|
|||
|
||||
// Map and write some data to target partition.
|
||||
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
|
||||
ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
|
||||
ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
|
||||
|
||||
// Finish update.
|
||||
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
|
||||
|
@ -1655,7 +1657,7 @@ TEST_F(SnapshotUpdateTest, Overflow) {
|
|||
|
||||
// Map and write some data to target partitions.
|
||||
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
|
||||
ASSERT_TRUE(WriteSnapshotAndHash("sys_b", actual_write_size));
|
||||
ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
|
||||
|
||||
std::vector<android::dm::DeviceMapper::TargetInfo> table;
|
||||
ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
|
||||
|
|
91
fs_mgr/libsnapshot/snapshot_writer.cpp
Normal file
91
fs_mgr/libsnapshot/snapshot_writer.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// Copyright (C) 2020 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 <libsnapshot/snapshot_writer.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <payload_consumer/file_descriptor.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
using chromeos_update_engine::FileDescriptor;
|
||||
|
||||
ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
|
||||
|
||||
void ISnapshotWriter::SetSourceDevice(android::base::unique_fd&& source_fd) {
|
||||
source_fd_ = std::move(source_fd);
|
||||
}
|
||||
|
||||
OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
|
||||
: ISnapshotWriter(options) {}
|
||||
|
||||
void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
|
||||
uint64_t cow_size) {
|
||||
snapshot_fd_ = std::move(snapshot_fd);
|
||||
cow_size_ = cow_size;
|
||||
}
|
||||
|
||||
bool OnlineKernelSnapshotWriter::Flush() {
|
||||
if (fsync(snapshot_fd_.get()) < 0) {
|
||||
PLOG(ERROR) << "fsync";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
|
||||
size_t size) {
|
||||
uint64_t offset = new_block_start * options_.block_size;
|
||||
if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
|
||||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(snapshot_fd_, data, size)) {
|
||||
PLOG(ERROR) << "EmitRawBlocks write";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
|
||||
std::string zeroes(options_.block_size, 0);
|
||||
for (uint64_t i = 0; i < num_blocks; i++) {
|
||||
if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
|
||||
std::string buffer(options_.block_size, 0);
|
||||
uint64_t offset = old_block * options_.block_size;
|
||||
if (!android::base::ReadFullyAtOffset(source_fd_, buffer.data(), buffer.size(), offset)) {
|
||||
PLOG(ERROR) << "EmitCopy read";
|
||||
return false;
|
||||
}
|
||||
return EmitRawBlocks(new_block, buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
|
||||
LOG(ERROR) << "OnlineKernelSnapshotWriter::OpenReader not yet implemented";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
|
@ -127,6 +127,48 @@ bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WriteRandomData(ICowWriter* writer, std::string* hash) {
|
||||
unique_fd rand(open("/dev/urandom", O_RDONLY));
|
||||
if (rand < 0) {
|
||||
PLOG(ERROR) << "open /dev/urandom";
|
||||
return false;
|
||||
}
|
||||
|
||||
SHA256_CTX ctx;
|
||||
if (hash) {
|
||||
SHA256_Init(&ctx);
|
||||
}
|
||||
|
||||
if (!writer->options().max_blocks) {
|
||||
LOG(ERROR) << "CowWriter must specify maximum number of blocks";
|
||||
return false;
|
||||
}
|
||||
uint64_t num_blocks = writer->options().max_blocks.value();
|
||||
|
||||
size_t block_size = writer->options().block_size;
|
||||
std::string block(block_size, '\0');
|
||||
for (uint64_t i = 0; i < num_blocks; i++) {
|
||||
if (!ReadFully(rand, block.data(), block.size())) {
|
||||
PLOG(ERROR) << "read /dev/urandom";
|
||||
return false;
|
||||
}
|
||||
if (!writer->AddRawBlocks(i, block.data(), block.size())) {
|
||||
LOG(ERROR) << "Failed to add raw block " << i;
|
||||
return false;
|
||||
}
|
||||
if (hash) {
|
||||
SHA256_Update(&ctx, block.data(), block.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
uint8_t out[32];
|
||||
SHA256_Final(out, &ctx);
|
||||
*hash = ToHexString(out, sizeof(out));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetHash(const std::string& path) {
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(path, &content, true)) {
|
||||
|
|
|
@ -129,6 +129,7 @@ cc_defaults {
|
|||
"libprotobuf-cpp-lite",
|
||||
"libpropertyinfoserializer",
|
||||
"libpropertyinfoparser",
|
||||
"libsnapshot_cow",
|
||||
"libsnapshot_init",
|
||||
"libxml2",
|
||||
"lib_apex_manifest_proto_lite",
|
||||
|
|
|
@ -112,6 +112,7 @@ LOCAL_STATIC_LIBRARIES := \
|
|||
libmodprobe \
|
||||
libext2_uuid \
|
||||
libprotobuf-cpp-lite \
|
||||
libsnapshot_cow \
|
||||
libsnapshot_init \
|
||||
update_metadata-protos \
|
||||
|
||||
|
|
Loading…
Reference in a new issue