libsnapshot: Add EnsureMetadataMounted

In recovery, client is responsible for calling
EnsureMetadataMounted before doing (almost) all
operations on SnapshotManager, e.g.

- CancelUpdate() before sideloading
- BeginUpdate() on retrofit Virtual A/B before sideloading
- Finishing merge before flashing

Test: libsnapshot_test
Test: recovery sideload
Bug: 140749209
Change-Id: I1034a7fa74e31b6850896e61e86341239dbf2699
This commit is contained in:
Yifan Hong 2019-10-17 18:25:04 -07:00
parent 82f1cbea1e
commit d2a0247538
7 changed files with 188 additions and 13 deletions

View File

@ -0,0 +1,40 @@
// 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 <string>
#include <android-base/macros.h>
namespace android {
namespace snapshot {
// An abstract "device" that will be cleaned up (unmapped, unmounted, etc.) upon
// destruction.
struct AutoDevice {
virtual ~AutoDevice(){};
void Release();
protected:
AutoDevice(const std::string& name) : name_(name) {}
std::string name_;
private:
DISALLOW_COPY_AND_ASSIGN(AutoDevice);
AutoDevice(AutoDevice&& other) = delete;
};
} // namespace snapshot
} // namespace android

View File

@ -33,6 +33,8 @@
#include <liblp/liblp.h>
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
friend class test_set_name##_##individual_test##_Test
@ -120,6 +122,7 @@ class SnapshotManager final {
virtual const IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
virtual bool IsRecovery() const = 0;
};
~SnapshotManager();
@ -209,6 +212,22 @@ class SnapshotManager final {
// Dump debug information.
bool Dump(std::ostream& os);
// 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
std::unique_ptr<AutoDevice> EnsureMetadataMounted();
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);

View File

@ -77,6 +77,12 @@ using namespace std::string_literals;
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
#ifdef __ANDROID_RECOVERY__
constexpr bool kIsRecovery = true;
#else
constexpr bool kIsRecovery = false;
#endif
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
public:
std::string GetGsidDir() const override { return "ota"s; }
@ -89,6 +95,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
}
bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
bool SetBootControlMergeStatus(MergeStatus status) override;
bool IsRecovery() const override { return kIsRecovery; }
private:
android::fs_mgr::PartitionOpener opener_;
@ -2065,5 +2072,14 @@ bool SnapshotManager::Dump(std::ostream& os) {
return ok;
}
std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
if (!device_->IsRecovery()) {
// No need to mount anything in recovery.
LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
}
return AutoUnmountDevice::New(device_->GetMetadataDir());
}
} // namespace snapshot
} // namespace android

View File

@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
@ -47,7 +48,10 @@ using android::fiemap::IImageManager;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::DestroyLogicalPartition;
using android::fs_mgr::EnsurePathMounted;
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Extent;
using android::fs_mgr::Fstab;
using android::fs_mgr::GetPartitionGroupName;
using android::fs_mgr::GetPartitionName;
using android::fs_mgr::Interval;
@ -1056,6 +1060,64 @@ TEST_F(SnapshotUpdateTest, ReclaimCow) {
}
}
class MetadataMountedTest : public SnapshotUpdateTest {
public:
void SetUp() override {
metadata_dir_ = test_device->GetMetadataDir();
ASSERT_TRUE(ReadDefaultFstab(&fstab_));
}
void TearDown() override {
SetUp();
// Remount /metadata
test_device->set_recovery(false);
EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));
}
AssertionResult IsMetadataMounted() {
Fstab mounted_fstab;
if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
ADD_FAILURE() << "Failed to scan mounted volumes";
return AssertionFailure() << "Failed to scan mounted volumes";
}
auto entry = GetEntryForPath(&fstab_, metadata_dir_);
if (entry == nullptr) {
return AssertionFailure() << "No mount point found in fstab for path " << metadata_dir_;
}
auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);
if (mv == nullptr) {
return AssertionFailure() << metadata_dir_ << " is not mounted";
}
return AssertionSuccess() << metadata_dir_ << " is mounted";
}
std::string metadata_dir_;
Fstab fstab_;
};
TEST_F(MetadataMountedTest, Android) {
auto device = sm->EnsureMetadataMounted();
EXPECT_NE(nullptr, device);
device.reset();
EXPECT_TRUE(IsMetadataMounted());
EXPECT_TRUE(sm->CancelUpdate()) << "Metadata dir should never be unmounted in Android mode";
}
TEST_F(MetadataMountedTest, Recovery) {
test_device->set_recovery(true);
metadata_dir_ = test_device->GetMetadataDir();
EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));
EXPECT_FALSE(IsMetadataMounted());
auto device = sm->EnsureMetadataMounted();
EXPECT_NE(nullptr, device);
EXPECT_TRUE(IsMetadataMounted());
device.reset();
EXPECT_FALSE(IsMetadataMounted());
}
} // namespace snapshot
} // namespace android
@ -1097,6 +1159,7 @@ int main(int argc, char** argv) {
}
// Clean up previous run.
MetadataMountedTest().TearDown();
SnapshotUpdateTest().Cleanup();
SnapshotTest().Cleanup();

View File

@ -88,17 +88,20 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
return true;
}
bool IsOverlayfsSetup() const override { return false; }
bool IsRecovery() const override { return recovery_; }
void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
void set_fake_super(const std::string& path) {
opener_ = std::make_unique<TestPartitionOpener>(path);
}
void set_recovery(bool value) { recovery_ = value; }
MergeStatus merge_status() const { return merge_status_; }
private:
std::string slot_suffix_ = "_a";
std::unique_ptr<TestPartitionOpener> opener_;
MergeStatus merge_status_;
bool recovery_ = false;
};
class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {

View File

@ -17,9 +17,15 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
using android::fs_mgr::EnsurePathMounted;
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
using android::fs_mgr::GetEntryForPath;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
using android::fs_mgr::ReadDefaultFstab;
namespace android {
namespace snapshot {
@ -109,5 +115,31 @@ bool InitializeCow(const std::string& device) {
return true;
}
std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
LOG(ERROR) << "Cannot read default fstab";
return nullptr;
}
if (GetEntryForPath(&fstab, path) == nullptr) {
LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping";
return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice("", {}));
}
if (!EnsurePathMounted(&fstab, path)) {
LOG(ERROR) << "Cannot mount " << path;
return nullptr;
}
return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));
}
AutoUnmountDevice::~AutoUnmountDevice() {
if (name_.empty()) return;
if (!EnsurePathUnmounted(&fstab_, name_)) {
LOG(ERROR) << "Cannot unmount " << name_;
}
}
} // namespace snapshot
} // namespace android

View File

@ -18,31 +18,21 @@
#include <string>
#include <android-base/macros.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
#include <libsnapshot/snapshot.h>
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
namespace android {
namespace snapshot {
// Unit is sectors, this is a 4K chunk.
static constexpr uint32_t kSnapshotChunkSize = 8;
struct AutoDevice {
virtual ~AutoDevice(){};
void Release();
protected:
AutoDevice(const std::string& name) : name_(name) {}
std::string name_;
private:
DISALLOW_COPY_AND_ASSIGN(AutoDevice);
AutoDevice(AutoDevice&& other) = delete;
};
// A list of devices we created along the way.
// - Whenever a device is created that is subject to GC'ed at the end of
// this function, add it to this list.
@ -103,6 +93,18 @@ struct AutoDeleteSnapshot : AutoDevice {
SnapshotManager::LockedFile* lock_ = nullptr;
};
struct AutoUnmountDevice : AutoDevice {
// Empty object that does nothing.
AutoUnmountDevice() : AutoDevice("") {}
static std::unique_ptr<AutoUnmountDevice> New(const std::string& path);
~AutoUnmountDevice();
private:
AutoUnmountDevice(const std::string& path, android::fs_mgr::Fstab&& fstab)
: AutoDevice(path), fstab_(std::move(fstab)) {}
android::fs_mgr::Fstab fstab_;
};
// Return a list of partitions in |builder| with the name ending in |suffix|.
std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);