Merge "libsnapshot: Partially implement OpenSnapshotWriter." am: 6c1d885150 am: 54231f255e

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1455356

Change-Id: Id3a392d50e237bafac6f4c004630f12516371e58
This commit is contained in:
David Anderson 2020-10-16 22:42:16 +00:00 committed by Automerger Merge Worker
commit d20ffda080
16 changed files with 426 additions and 86 deletions

View file

@ -147,6 +147,7 @@ cc_binary {
static_libs: [ static_libs: [
"libgtest_prod", "libgtest_prod",
"libhealthhalutils", "libhealthhalutils",
"libsnapshot_cow",
"libsnapshot_nobinder", "libsnapshot_nobinder",
"update_metadata-protos", "update_metadata-protos",
], ],

View file

@ -77,6 +77,7 @@ filegroup {
"snapshot_stats.cpp", "snapshot_stats.cpp",
"snapshot_stub.cpp", "snapshot_stub.cpp",
"snapshot_metadata_updater.cpp", "snapshot_metadata_updater.cpp",
"snapshot_writer.cpp",
"partition_cow_creator.cpp", "partition_cow_creator.cpp",
"return.cpp", "return.cpp",
"utility.cpp", "utility.cpp",
@ -247,6 +248,7 @@ cc_defaults {
"libgmock", "libgmock",
"liblp", "liblp",
"libsnapshot", "libsnapshot",
"libsnapshot_cow",
"libsnapshot_test_helpers", "libsnapshot_test_helpers",
"libsparse", "libsparse",
], ],
@ -275,6 +277,7 @@ cc_binary {
static_libs: [ static_libs: [
"libfstab", "libfstab",
"libsnapshot", "libsnapshot",
"libsnapshot_cow",
"update_metadata-protos", "update_metadata-protos",
], ],
shared_libs: [ shared_libs: [
@ -338,6 +341,7 @@ cc_defaults {
"libgmock", // from libsnapshot_test_helpers "libgmock", // from libsnapshot_test_helpers
"liblog", "liblog",
"liblp", "liblp",
"libsnapshot_cow",
"libsnapshot_test_helpers", "libsnapshot_test_helpers",
"libprotobuf-mutator", "libprotobuf-mutator",
], ],

View file

@ -32,6 +32,9 @@ namespace snapshot {
static_assert(sizeof(off_t) == sizeof(uint64_t)); 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) { bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
if (!ValidateNewBlock(new_block)) { if (!ValidateNewBlock(new_block)) {
return false; return false;
@ -98,12 +101,12 @@ bool CowWriter::ParseOptions() {
return true; 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); 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; fd_ = fd;
if (!ParseOptions()) { if (!ParseOptions()) {

View file

@ -58,6 +58,9 @@ class ICowWriter {
// Return number of bytes the cow image occupies on disk. // Return number of bytes the cow image occupies on disk.
virtual uint64_t GetCowSize() = 0; virtual uint64_t GetCowSize() = 0;
// Returns true if AddCopy() operations are supported.
virtual bool SupportsCopyOperation() const { return true; }
const CowOptions& options() { return options_; } const CowOptions& options() { return options_; }
protected: protected:

View file

@ -38,9 +38,7 @@ class MockSnapshotManager : public ISnapshotManager {
(const android::fs_mgr::CreateLogicalPartitionParams& params, (const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path), std::string* snapshot_path),
(override)); (override));
MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter, MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
(const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenSnapshotReader,
(const android::fs_mgr::CreateLogicalPartitionParams& params), (override)); (const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override)); MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override)); MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));

View file

@ -35,8 +35,8 @@
#include <update_engine/update_metadata.pb.h> #include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h> #include <libsnapshot/auto_device.h>
#include <libsnapshot/cow_writer.h>
#include <libsnapshot/return.h> #include <libsnapshot/return.h>
#include <libsnapshot/snapshot_writer.h>
#ifndef FRIEND_TEST #ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \ #define FRIEND_TEST(test_set_name, individual_test) \
@ -44,10 +44,6 @@
#define DEFINED_FRIEND_TEST #define DEFINED_FRIEND_TEST
#endif #endif
namespace chromeos_update_engine {
class FileDescriptor;
} // namespace chromeos_update_engine
namespace android { namespace android {
namespace fiemap { namespace fiemap {
@ -110,8 +106,6 @@ class ISnapshotManager {
}; };
virtual ~ISnapshotManager() = default; virtual ~ISnapshotManager() = default;
using FileDescriptor = chromeos_update_engine::FileDescriptor;
// Begin an update. This must be called before creating any snapshots. It // Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None. // will fail if GetUpdateState() != None.
virtual bool BeginUpdate() = 0; virtual bool BeginUpdate() = 0;
@ -187,19 +181,14 @@ class ISnapshotManager {
virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params, virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) = 0; std::string* snapshot_path) = 0;
// Create an ICowWriter to build a snapshot against a target partition. The partition name must // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
// be suffixed. // must be suffixed.
virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter( virtual std::unique_ptr<ISnapshotWriter> 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(
const android::fs_mgr::CreateLogicalPartitionParams& params) = 0; const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
// Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot, // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
// OpenSnapshotWriter, or OpenSnapshotReader. All outstanding open descriptors, writers, // OpenSnapshotWriter. All outstanding open descriptors, writers, or
// or readers must be deleted before this is called. // readers must be deleted before this is called.
virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0; virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
// If this returns true, first-stage mount must call // If this returns true, first-stage mount must call
@ -310,9 +299,7 @@ class SnapshotManager final : public ISnapshotManager {
Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override; Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
std::string* snapshot_path) override; std::string* snapshot_path) override;
std::unique_ptr<ICowWriter> OpenSnapshotWriter( std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
std::unique_ptr<FileDescriptor> OpenSnapshotReader(
const android::fs_mgr::CreateLogicalPartitionParams& params) override; const android::fs_mgr::CreateLogicalPartitionParams& params) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override; bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override; bool NeedSnapshotsInFirstStageMount() override;
@ -532,9 +519,39 @@ class SnapshotManager final : public ISnapshotManager {
std::string GetSnapshotDeviceName(const std::string& snapshot_name, std::string GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status); 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. // Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params, 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. // Map the COW devices, including the partition in super and the images.
// |params|: // |params|:

View file

@ -36,9 +36,7 @@ class SnapshotManagerStub : public ISnapshotManager {
const chromeos_update_engine::DeltaArchiveManifest& manifest) override; const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params, bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) override; std::string* snapshot_path) override;
std::unique_ptr<ICowWriter> OpenSnapshotWriter( std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
std::unique_ptr<FileDescriptor> OpenSnapshotReader(
const android::fs_mgr::CreateLogicalPartitionParams& params) override; const android::fs_mgr::CreateLogicalPartitionParams& params) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override; bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override; bool NeedSnapshotsInFirstStageMount() override;

View 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

View file

@ -144,6 +144,7 @@ void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::stri
// Expect space of |path| is multiple of 4K. // Expect space of |path| is multiple of 4K.
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt, bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr); std::string* hash = nullptr);
bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
std::optional<std::string> GetHash(const std::string& path); std::optional<std::string> GetHash(const std::string& path);

View file

@ -15,6 +15,7 @@
#include <libsnapshot/snapshot.h> #include <libsnapshot/snapshot.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h>
#include <math.h> #include <math.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/types.h> #include <sys/types.h>
@ -1569,8 +1570,8 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(
.partition_opener = &opener, .partition_opener = &opener,
.timeout_ms = timeout_ms, .timeout_ms = timeout_ms,
}; };
std::string ignore_path; if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) { nullptr)) {
return false; return false;
} }
} }
@ -1598,11 +1599,10 @@ static std::chrono::milliseconds GetRemainingTime(
bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock, bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
CreateLogicalPartitionParams params, CreateLogicalPartitionParams params,
std::string* path) { SnapshotContext context, SnapshotPaths* paths) {
auto begin = std::chrono::steady_clock::now(); auto begin = std::chrono::steady_clock::now();
CHECK(lock); CHECK(lock);
path->clear();
if (params.GetPartitionName() != params.GetDeviceName()) { if (params.GetPartitionName() != params.GetDeviceName()) {
LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = " LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
@ -1683,8 +1683,11 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
} }
created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName()); created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
if (paths) {
paths->target_device = base_path;
}
if (!live_snapshot_status.has_value()) { if (!live_snapshot_status.has_value()) {
*path = base_path;
created_devices.Release(); created_devices.Release();
return true; return true;
} }
@ -1711,21 +1714,33 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
LOG(ERROR) << "Could not determine major/minor for: " << cow_name; LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
return false; return false;
} }
if (paths) {
paths->cow_device = cow_device;
}
remaining_time = GetRemainingTime(params.timeout_ms, begin); remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false; 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, if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
path)) { &path)) {
LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName(); LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
return false; return false;
} }
// No need to add params.GetPartitionName() to created_devices since it is immediately released. // No need to add params.GetPartitionName() to created_devices since it is immediately released.
if (paths) {
paths->snapshot_device = path;
}
created_devices.Release(); 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; return true;
} }
@ -2438,25 +2453,87 @@ bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& para
<< params.GetPartitionName(); << params.GetPartitionName();
return false; return false;
} }
return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
SnapshotPaths paths;
if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
return false;
} }
std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter( if (!paths.snapshot_device.empty()) {
const android::fs_mgr::CreateLogicalPartitionParams& params) { *snapshot_path = paths.snapshot_device;
(void)params; } else {
*snapshot_path = paths.target_device;
}
DCHECK(!snapshot_path->empty());
return true;
}
LOG(ERROR) << "OpenSnapshotWriter not yet implemented"; std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& 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; return nullptr;
} }
std::unique_ptr<FileDescriptor> SnapshotManager::OpenSnapshotReader( SnapshotPaths paths;
const android::fs_mgr::CreateLogicalPartitionParams& params) { if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
(void)params;
LOG(ERROR) << "OpenSnapshotReader not yet implemented";
return nullptr; 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);
}
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<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
LockedFile* lock, [[maybe_unused]] const std::string& partition_name,
const SnapshotStatus& status, const SnapshotPaths& paths) {
CHECK(lock);
CowOptions cow_options;
cow_options.max_blocks = {status.device_size() / cow_options.block_size};
auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
std::string path = paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
unique_fd fd(open(path.c_str(), 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) { bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
auto lock = LockShared(); auto lock = LockShared();
if (!lock) return false; if (!lock) return false;

View file

@ -130,13 +130,7 @@ ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
return &snapshot_merge_stats; return &snapshot_merge_stats;
} }
std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter( std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
const CreateLogicalPartitionParams&) {
LOG(ERROR) << __FUNCTION__ << " should never be called.";
return nullptr;
}
std::unique_ptr<FileDescriptor> SnapshotManagerStub::OpenSnapshotReader(
const CreateLogicalPartitionParams&) { const CreateLogicalPartitionParams&) {
LOG(ERROR) << __FUNCTION__ << " should never be called."; LOG(ERROR) << __FUNCTION__ << " should never be called.";
return nullptr; return nullptr;

View file

@ -80,6 +80,7 @@ TestDeviceInfo* test_device = nullptr;
std::string fake_super; std::string fake_super;
void MountMetadata(); void MountMetadata();
bool IsCompressionEnabled();
class SnapshotTest : public ::testing::Test { class SnapshotTest : public ::testing::Test {
public: public:
@ -892,42 +893,78 @@ class SnapshotUpdateTest : public SnapshotTest {
return AssertionSuccess(); return AssertionSuccess();
} }
AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) { AssertionResult MapUpdateSnapshot(const std::string& name,
std::string real_path; std::unique_ptr<ICowWriter>* writer) {
if (!sm->MapUpdateSnapshot( CreateLogicalPartitionParams params{
CreateLogicalPartitionParams{
.block_device = fake_super, .block_device = fake_super,
.metadata_slot = 1, .metadata_slot = 1,
.partition_name = name, .partition_name = name,
.timeout_ms = 10s, .timeout_ms = 10s,
.partition_opener = opener_.get(), .partition_opener = opener_.get(),
}, };
&real_path)) {
return AssertionFailure() << "Unable to map snapshot " << name; auto result = sm->OpenSnapshotWriter(params);
} if (!result) {
if (path) { return AssertionFailure() << "Cannot open snapshot for writing: " << name;
*path = real_path;
}
return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
} }
AssertionResult WriteSnapshotAndHash(const std::string& name, if (writer) {
std::optional<size_t> size = std::nullopt) { *writer = std::move(result);
}
return AssertionSuccess();
}
AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
CreateLogicalPartitionParams params{
.block_device = fake_super,
.metadata_slot = 1,
.partition_name = name,
.timeout_ms = 10s,
.partition_opener = opener_.get(),
};
auto result = sm->MapUpdateSnapshot(params, path);
if (!result) {
return AssertionFailure() << "Cannot open snapshot for writing: " << name;
}
return AssertionSuccess();
}
AssertionResult MapUpdateSnapshot(const std::string& name) {
if (IsCompressionEnabled()) {
std::unique_ptr<ICowWriter> writer;
return MapUpdateSnapshot(name, &writer);
} else {
std::string path;
return MapUpdateSnapshot(name, &path);
}
}
AssertionResult WriteSnapshotAndHash(const std::string& name) {
if (IsCompressionEnabled()) {
std::unique_ptr<ICowWriter> writer;
auto res = MapUpdateSnapshot(name, &writer);
if (!res) {
return res;
}
if (!WriteRandomData(writer.get(), &hashes_[name])) {
return AssertionFailure() << "Unable to write random data to snapshot " << name;
}
} else {
std::string path; std::string path;
auto res = MapUpdateSnapshot(name, &path); auto res = MapUpdateSnapshot(name, &path);
if (!res) { if (!res) {
return res; return res;
} }
if (!WriteRandomData(path, std::nullopt, &hashes_[name])) {
std::string size_string = size ? (std::to_string(*size) + " bytes") : ""; return AssertionFailure() << "Unable to write random data to snapshot " << name;
}
if (!WriteRandomData(path, size, &hashes_[name])) {
return AssertionFailure() << "Unable to write " << size_string << " to " << path
<< " for partition " << name;
} }
return AssertionSuccess() << "Written " << size_string << " to " << path // Make sure updates to one device are seen by all devices.
<< " for snapshot partition " << name sync();
return AssertionSuccess() << "Written random data to snapshot " << name
<< ", hash: " << hashes_[name]; << ", hash: " << hashes_[name];
} }
@ -1003,7 +1040,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
// Write some data to target partitions. // Write some data to target partitions.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { 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. // Assert that source partitions aren't affected.
@ -1406,6 +1443,10 @@ void MountMetadata() {
MetadataMountedTest().TearDown(); MetadataMountedTest().TearDown();
} }
bool IsCompressionEnabled() {
return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
}
TEST_F(MetadataMountedTest, Android) { TEST_F(MetadataMountedTest, Android) {
auto device = sm->EnsureMetadataMounted(); auto device = sm->EnsureMetadataMounted();
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
@ -1623,7 +1664,7 @@ TEST_F(SnapshotUpdateTest, Hashtree) {
// Map and write some data to target partition. // Map and write some data to target partition.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"})); ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size)); ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
// Finish update. // Finish update.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@ -1655,7 +1696,7 @@ TEST_F(SnapshotUpdateTest, Overflow) {
// Map and write some data to target partitions. // Map and write some data to target partitions.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"})); 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; std::vector<android::dm::DeviceMapper::TargetInfo> table;
ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table)); ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));

View 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

View file

@ -127,6 +127,48 @@ bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size,
return true; 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::optional<std::string> GetHash(const std::string& path) {
std::string content; std::string content;
if (!android::base::ReadFileToString(path, &content, true)) { if (!android::base::ReadFileToString(path, &content, true)) {

View file

@ -129,6 +129,7 @@ cc_defaults {
"libprotobuf-cpp-lite", "libprotobuf-cpp-lite",
"libpropertyinfoserializer", "libpropertyinfoserializer",
"libpropertyinfoparser", "libpropertyinfoparser",
"libsnapshot_cow",
"libsnapshot_init", "libsnapshot_init",
"libxml2", "libxml2",
"lib_apex_manifest_proto_lite", "lib_apex_manifest_proto_lite",

View file

@ -112,6 +112,7 @@ LOCAL_STATIC_LIBRARIES := \
libmodprobe \ libmodprobe \
libext2_uuid \ libext2_uuid \
libprotobuf-cpp-lite \ libprotobuf-cpp-lite \
libsnapshot_cow \
libsnapshot_init \ libsnapshot_init \
update_metadata-protos \ update_metadata-protos \