libsnapshot: MapPartitionWithSnapshot cleanup itself if failed

When we run MapPartitionWithSnapshot, intermediate devices aren't
cleaned up if the call fails. Hence, record these intermediate devices
we have created along the way using the new AutoDevices class. Upon
failure, the AutoDevices object will be destroyed, and all the
intermediate devices will be deleted from device mapper or image
manager. Upon success, AutoDevices::Release() makes sure the
intermediate devices aren't deleted.

Test: libsnapshot_test

Change-Id: Iff4c1297528288a27765c0224b67254b68c89776
This commit is contained in:
Yifan Hong 2019-08-27 18:37:41 -07:00
parent 5576f7cc13
commit f35687df6e
4 changed files with 158 additions and 0 deletions

View file

@ -49,6 +49,7 @@ filegroup {
name: "libsnapshot_sources",
srcs: [
"snapshot.cpp",
"utility.cpp",
],
}

View file

@ -36,6 +36,8 @@
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
#include "utility.h"
namespace android {
namespace snapshot {
@ -1247,6 +1249,11 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
params.device_name = GetBaseDeviceName(params.GetPartitionName());
}
AutoDeviceList created_devices;
// Create the base device for the snapshot, or if there is no snapshot, the
// device itself. This device consists of the real blocks in the super
// partition that this logical partition occupies.
auto& dm = DeviceMapper::Instance();
std::string ignore_path;
if (!CreateLogicalPartition(params, &ignore_path)) {
@ -1254,7 +1261,10 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
<< " as device " << params.GetDeviceName();
return false;
}
created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
if (!live_snapshot_status.has_value()) {
created_devices.Release();
return true;
}
@ -1277,6 +1287,9 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
LOG(ERROR) << "Could not map cow image for partition: " << params.GetPartitionName();
return false;
}
created_devices.EmplaceBack<AutoUnmapImage>(images_.get(),
GetCowImageDeviceName(params.partition_name));
// TODO: map cow linear device here
std::string cow_device = cow_image_device;
@ -1288,6 +1301,9 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
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.
created_devices.Release();
LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;

View file

@ -0,0 +1,56 @@
// 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.
#include "utility.h"
#include <android-base/logging.h>
#include <android-base/strings.h>
namespace android {
namespace snapshot {
void AutoDevice::Release() {
name_.clear();
}
AutoDeviceList::~AutoDeviceList() {
// Destroy devices in the reverse order because newer devices may have dependencies
// on older devices.
for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {
it->reset();
}
}
void AutoDeviceList::Release() {
for (auto&& p : devices_) {
p->Release();
}
}
AutoUnmapDevice::~AutoUnmapDevice() {
if (name_.empty()) return;
if (!dm_->DeleteDeviceIfExists(name_)) {
LOG(ERROR) << "Failed to auto unmap device " << name_;
}
}
AutoUnmapImage::~AutoUnmapImage() {
if (name_.empty()) return;
if (!images_->UnmapImageIfExists(name_)) {
LOG(ERROR) << "Failed to auto unmap cow image " << name_;
}
}
} // namespace snapshot
} // namespace android

View file

@ -0,0 +1,85 @@
// 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>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
namespace android {
namespace snapshot {
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.
// - If any error has occurred, the list is destroyed, and all these devices
// are cleaned up.
// - Upon success, Release() should be called so that the created devices
// are kept.
struct AutoDeviceList {
~AutoDeviceList();
template <typename T, typename... Args>
void EmplaceBack(Args&&... args) {
devices_.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
}
void Release();
private:
std::vector<std::unique_ptr<AutoDevice>> devices_;
};
// Automatically unmap a device upon deletion.
struct AutoUnmapDevice : AutoDevice {
// On destruct, delete |name| from device mapper.
AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
: AutoDevice(name), dm_(dm) {}
AutoUnmapDevice(AutoUnmapDevice&& other) = default;
~AutoUnmapDevice();
private:
DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
android::dm::DeviceMapper* dm_ = nullptr;
};
// Automatically unmap an image upon deletion.
struct AutoUnmapImage : AutoDevice {
// On destruct, delete |name| from image manager.
AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
: AutoDevice(name), images_(images) {}
AutoUnmapImage(AutoUnmapImage&& other) = default;
~AutoUnmapImage();
private:
DISALLOW_COPY_AND_ASSIGN(AutoUnmapImage);
android::fiemap::IImageManager* images_ = nullptr;
};
} // namespace snapshot
} // namespace android