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
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index a54db58..8df9c52 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -49,6 +49,7 @@
name: "libsnapshot_sources",
srcs: [
"snapshot.cpp",
+ "utility.cpp",
],
}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index d10410f..f00129a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -36,6 +36,8 @@
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
+#include "utility.h"
+
namespace android {
namespace snapshot {
@@ -1247,6 +1249,11 @@
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 @@
<< " 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 @@
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 @@
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;
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
new file mode 100644
index 0000000..164b472
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -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
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
new file mode 100644
index 0000000..cbab472
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.h
@@ -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